Skip to content

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 ​

xml
<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:

properties
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:

MeterLabels / meaning
lockwell.native.wire.requestsresult=success, result=failure, or result=cancelled
lockwell.native.wire.request.durationrequest timer
lockwell.native.wire.connectionsaggregate active connections
lockwell.native.wire.bytesdirection=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 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:

java
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.

Source-available under PolyForm Noncommercial 1.0.0; commercial use requires a written grant. License