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

| 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
[`java-spring-boot-starter/consumer-tests/maven`](https://github.com/RusticStack/lockwell-sdk-java/tree/main/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](/guide/native-wire), the [wire reference](/reference/native-wire), and the
[source README](https://github.com/RusticStack/lockwell-sdk-java/blob/main/java-spring-boot-starter/README.md) for the full
protocol, evidence, and compatibility ledger.
