Spring Boot Native Wire starter
com.lockwell:lockwell-spring-boot-starter 0.2.2 is the opt-in, JDK 25-first client for LNW/1. It is a separate artifact compiled with --release 25; the existing com.lockwell:lockwell-sdk remains Java 21 bytecode and continues to provide the S3, HTTP-native JSON, Admin, and kit clients. The starter sends deterministic binary frames only—never HTTP, JSON, or XML—and never falls back to another surface.
Install
<dependency>
<groupId>com.lockwell</groupId>
<artifactId>lockwell-spring-boot-starter</artifactId>
<version>0.2.2</version>
</dependency>This coordinate is source-shipped and consumer-tested in the repository; use your approved internal or GitHub Packages mirror and pin the artifact/checksums. The starter is tested with Spring Boot 4.1.1 and an external compatibility consumer under Spring Boot 4.0.7.
Configuration
Enable the starter explicitly and keep secrets in environment-backed configuration:
lockwell.native-wire.enabled=true
lockwell.native-wire.host=lockwell.internal.example
lockwell.native-wire.port=9444
lockwell.native-wire.access-key-id=${LOCKWELL_ACCESS_KEY_ID}
lockwell.native-wire.secret-key=${LOCKWELL_SECRET_KEY}
lockwell.native-wire.tls.ca-certificate=/run/secrets/lockwell-ca.pem
# Base64(SHA-256(SubjectPublicKeyInfo)); supplements PKIX + hostname verification.
lockwell.native-wire.tls.spki-sha256-pins[0]=${LOCKWELL_SPKI_PIN}
# Required together for an optional/required-mTLS listener.
lockwell.native-wire.tls.client-key-store=/run/secrets/lockwell-client.p12
lockwell.native-wire.tls.client-key-store-password=${LOCKWELL_CLIENT_KEYSTORE_PASSWORD}Public properties include host, port, accessKeyId, secretKey, poolSize (1–128, default 4), connectTimeout (10 seconds), readTimeout (2 minutes), acquireTimeout (10 seconds), and nested TLS CA, SPKI pin, PKCS#12 identity, and explicit loopback-plaintext test settings. TLS 1.3 and hostname verification are enforced for non-loopback use. toString() and diagnostics redact secret and keystore password material.
Spring auto-configuration creates lifecycle-managed LockwellNativeWireClient, a virtual-thread LockwellNativeWireAsyncClient, and LockwellNativeWireHealthIndicator when lockwell.native-wire.enabled=true. The health check performs authenticated readiness, including the server metadata-authority/quorum guard. Disable the property to roll back without changing stored objects.
Java API and streaming
The client exposes capabilities/readiness, bucket CRUD/versioning, object PUT/GET/HEAD/DELETE, ranges, ordered duplicate user metadata, checksums, conditions, list/pagination/copy, versions/delete markers, multipart create/upload/list/ complete/abort, tags, retention, legal hold, batch delete, bucket CORS, webhook notifications, and signed GET/PUT capabilities. ObjectLockWrite carries optional retention and legal-hold state on the initial PUT; the server rechecks Object Lock scopes and commits the state atomically with the new version. Governance bypass is not supported.
Use getObject(request, OutputStream) for direct streaming or withObject(request, handler) for a bounded 64 KiB backpressured pipe. The callback must consume the stream before returning; closing or abandoning it cancels and invalidates the underlying connection. TransferProgressListener reports bytes. RequestOptions carries a deadline and validated traceparent.
For serializable application work, the virtual-thread async client returns CompletableFuture values. The connection pool remains the concurrency bound; an application-supplied executor remains application-owned and is not closed by the client.
Auth, retry, and telemetry
The client performs HELLO/WELCOME and a fresh access-key timestamp/nonce proof for each connection. Only an AUTH_ERROR with exact RATE_LIMITED, retryable=true, and a bounded hint may reopen a fresh socket and regenerate credentials/proof before the caller deadline. AUTH_REPLAY, scope/tenant denials, malformed hints, and application operations are never retried implicitly. A streaming write needs a new body, idempotency key, and body-binding checksum for an explicit caller retry.
Micrometer meters use bounded labels only:
| Meter | Labels / meaning |
|---|---|
lockwell.native.wire.requests | result=success, result=failure, or result=cancelled |
lockwell.native.wire.request.duration | request timer |
lockwell.native.wire.connections | aggregate active connections |
lockwell.native.wire.bytes | direction=inbound or direction=outbound; header + metadata + payload + CRC after a successful write/validated read |
Tracing accepts a TraceparentProvider; diagnostics and errors are redacted. Webhook signing secrets are returned once; configure a credential-free HTTPS URL and HMAC headers rather than putting a token in a query string.
Capability boundary
SSE-C is deliberately not advertised by this starter: critical SSE-C fields are rejected and same-named user metadata is still user-owned, so metadata cannot manufacture internal encryption state. Use the S3 client for genuine SSE-C. The Admin capability is a separate unimplemented authentication surface. Browser, edge, and static runtimes are not starter targets.
TangibleShift-shaped example
The public consumer fixture under sdk/java-spring-boot-starter/consumer-tests/maven uses company-scoped keys and exercises immutable conditional writes, ranges, copy/list, multipart resume/abort, versions, batch delete, CORS, webhook configuration, retention/legal hold, and signed reads. A service can keep the same shape without copying private application code:
import com.lockwell.sdk.springwire.LockwellNativeWireClient;
import com.lockwell.sdk.springwire.NativeWireTypes;
import java.io.InputStream;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
@Service
final class ArtifactStore {
private final LockwellNativeWireClient lockwell;
ArtifactStore(LockwellNativeWireClient lockwell) {
this.lockwell = lockwell;
}
void write(String company, InputStream body, long length) {
var request = new NativeWireTypes.PutObjectRequest(
company + "-artifacts", "immutable/report.pdf", length,
"application/pdf", List.of(), Optional.empty(), Optional.empty(), false,
Optional.empty(), Optional.empty(),
Optional.of(NativeWireTypes.ObjectLockWrite.retention(
NativeWireTypes.RetentionMode.COMPLIANCE, Instant.now().plus(30, ChronoUnit.DAYS))),
NativeWireTypes.RequestOptions.defaults());
lockwell.putObject(request, body);
}
}Adapt the bucket naming, authorization, and retention policy to your tenant model; do not put access-key secrets in the service source. The exact record constructors may evolve with the starter contract, so compile against the pinned artifact and consult NativeWireTypes in source.
Verification and rollback
Run the starter's Maven tests and external Maven/Gradle consumers with JDK 25 before changing a claim. The cross-language fixture proves byte compatibility with the Go server, comma/equals/reserved-name metadata round trips, and that native wire metadata cannot trigger SSE-C. Disable lockwell.native-wire.enabled, drain clients, and restore the existing explicit S3 or HTTP-native JSON selection to roll back; no object migration is performed.
See LNW architecture, the wire reference, and the source README for the full protocol, evidence, and compatibility ledger.