Skip to content

Native Wire (LNW/1)

Lockwell Native Wire (LNW/1) is the deterministic binary transport for the native data plane. It is experimental, opt-in, and disabled by default (defaultEnabled: false). The existing S3-compatible and HTTP-native JSON clients remain supported choices while an LNW deployment completes its own qualification. LNW is a transport, not a fourth authorization or storage surface: requests still enter the shared tenant, policy, quota, retention, object, metadata, encryption, audit, and storage pipeline.

The normative protocol and registry are maintained in docs/native-wire-v1.md, native-wire-v1.json, and the wire reference. The website pages are generated as pre-rendered HTML and raw Markdown (/guide/native-wire.md) so the contract can be fetched without JavaScript. Shared cross-language snippets and their security notes live in /sdk-public-api-examples-v1.json.

When to use it

Use LNW when a server-side application needs multiplexed, backpressured binary object I/O over a direct TLS connection and can operate a qualified Node, Bun, or JVM runtime. Use the S3 surface for existing S3 tools or genuine SSE-C. Use the HTTP-native JSON surface when a deployment has not enabled the binary listener. Use the Admin API for provisioning, quotas, and audit rather than an access-key data-plane connection.

LNW does not silently fall back to HTTP, JSON, XML, S3, or an object-store emulator. A refused capability, unsupported runtime, failed handshake, or transport error is returned to the caller with a stable error; choose another surface explicitly if that is the desired migration behavior.

Architecture and wire shape

The listener owns framing and connection state only. It dispatches operation codes to the existing native handlers; it never calls the storage backend directly. This keeps authorization and tenant isolation in one place:

Every frame has a 40-byte big-endian header (LKW1, version, type, flags, direction sequence, request/stream IDs, code, reserved bytes, metadata length, and payload length), followed by TLV metadata, payload, and a CRC32C. The CRC detects corruption and framing mistakes; TLS supplies confidentiality and channel integrity. Metadata TLVs are sorted, bounded, shortest-form UTF-8, NUL-free, and deterministic. Unknown optional fields are skipped; unknown critical fields, duplicate singular fields, non-canonical order, and invalid UTF-8 fail closed. Nested values use deterministic FieldDocument; JSON is never a wire fallback.

The default negotiated limits are 64 KiB metadata, 1 MiB payload per DATA frame, 1,114,156 bytes per frame, 128 streams per connection, 1,024 connections, a 4 MiB stream window, and a 16 MiB connection window. Clients must apply the negotiated minimum before allocating and must respect WINDOW_UPDATE credit. Only one reader and one writer goroutine own a connection; independent streams may execute concurrently.

TLS, mTLS, and authentication

Non-loopback LNW listeners require TLS 1.3. Hostname verification remains enabled. Configure a private CA on the client when the certificate is not publicly trusted. Listener client authentication is none, optional-mtls, or required-mtls; mTLS is a listener policy, not a capability negotiated in HELLO/WELCOME. The native clients require a certificate and key together when mTLS is used. Plaintext is reserved for an explicit loopback test configuration.

The connection state machine is:

  1. HELLO advertises the protocol range, capabilities, and receive limits.
  2. WELCOME selects exactly one version and the capability intersection. There is no silent downgrade.
  3. AUTH proves the access key with a timestamp, fresh 16-byte nonce, and HMAC-SHA-256 over the transcript.
  4. AUTH_OK returns the tenant, session expiry, negotiated capabilities, and effective limits; AUTH_ERROR is bounded.
  5. Odd client stream IDs carry one operation each (REQUEST, optional DATA, END); responses use RESPONSE, DATA, END, or a typed terminal ERROR.

Credential providers are evaluated for each new connection, so rotation and revocation do not require rebuilding a client. The server rechecks credential expiry, revocation, tenant-disabled state, and scope before every new stream. Authentication state is bounded per access-key principal: up to 256 unexpired nonces for each of 1,024 principals by default. A duplicate nonce is AUTH_REPLAY (0x0103), non-retryable and without a hint. Per-principal capacity is RATE_LIMITED (0x0300), retryable with an optional retryAfterMillis no greater than 600,000; the client discards the connection, resolves credentials again, and opens a fresh TLS connection with a fresh proof. A direct-peer accept limiter, when explicitly enabled, can close a socket before TLS and therefore emits no AUTH_ERROR.

Streaming, cancellation, and failure semantics

PUT_OBJECT and UPLOAD_PART stream bytes under both flow-control windows and the process-wide security.max_concurrent_uploads limit (default 128). security.upload_idle_timeout defaults to two minutes and security.upload_max_duration to 24 hours per stream. Only successful body reads reset the idle timer; unrelated frames, PINGs, or activity on another stream cannot extend it. Capacity errors are retryable. A deadline returns DEADLINE_EXCEEDED and removes uncommitted object or part state. A client CANCEL stops work and returns CANCELLED if no terminal frame has already been sent. A caller deadline may shorten, never extend, server limits.

Application retries are bounded and operation-aware. Reads and idempotent deletes may be retried. Buffered writes need an idempotency key and a replayable body; one-shot streaming bodies are not replayed implicitly. A fresh connection is required after authentication-capacity retry. There is no transparent byte-offset PUT/GET resume; after a conclusive failure, callers use the committed multipart parts or an explicit idempotent operation.

Errors contain only a stable code, retryable bit, optional bounded retry hint, safe message, and request/audit correlation. They never contain credentials, tokens, tenant secrets, payloads, filesystem paths, stacks, or peer certificates. See errors and retries for the application-facing policy.

Capabilities and data behavior

The effective Lockwell server mask advertises buckets, objects, pagination, multipart, versioning, Object Lock, tags, signed capabilities, trace context, CORS, and webhook notifications. SSE_C is intentionally not advertised because the native domain path has no server-enforced SSE-C operation. Use S3 for customer-provided AES-256 keys. The reserved Admin capability bit and 0x10000x10ff operation range are unimplemented and require a distinct authenticated contract.

The shipping LNW clients preserve ordered duplicate user metadata as a user-owned namespace; typed internal SSE-C and Object Lock state is separate and cannot be synthesized by metadata names. Object Lock retention and legal hold are authorized and committed atomically with a new version. Governance bypass is not supported. Checksums, ranges, tags, version/delete-marker listing, multipart completion/abort/resume discovery, batch delete, CORS, webhook configuration, and signed GET/PUT capabilities follow the operation registry and negotiated capability bits. Webhook configuration returns a signing secret once; use a credential-free HTTPS target and HMAC headers, never a token in the URL query.

Enable a listener

Start with a loopback certificate or a private CA in a non-production environment. The server-side configuration is under [native_wire] in examples/lockwell.toml:

toml
[native_wire]
enabled = false                 # opt in explicitly
listen_addr = "127.0.0.1:9444"
tls_cert_file = "./certs/lockwell.crt"
tls_key_file = "./certs/lockwell.key"
tls_ca_file = "./certs/lockwell-ca.pem" # required for mTLS
client_auth = "none"            # none | optional-mtls | required-mtls
max_connections = 1024
max_streams_per_connection = 128
handshake_timeout = "10s"
idle_timeout = "2m"

For a public bind, use a certificate whose name matches the endpoint and set the client CA policy deliberately. Keep enabled = false while rolling back: drain the listener, wait for admitted streams to finish, and remove the native-wire client selection. S3 and HTTP-native JSON remain unchanged and no metadata migration is performed.

Qualified clients and framework boundaries

The repository currently contains these implementation-backed LNW consumers:

ConsumerTested runtime floorEntry pointBoundary
@kelphect/sdk-native 0.1.0Node 22+, Bun 1.4+/node, /bun, or root conditionServer-only raw TLS; browser/default imports throw
@kelphect/sdk-solidstart 0.1.0built server Node 22+, Bun 1.4+/server, /node, /bunSolidStart v2; Nitro node_server, node_cluster, bun; edge/static refused
@kelphect/sdk-nextjs 0.1.0Next.js 16.3.3–16.x; Node 22+, Bun 1.4+package root (react-server/node)App Router server-only; Client/Edge/middleware refused
com.lockwell:lockwell-spring-boot-starter 0.2.2JDK 25, Spring Boot 4.1.1com.lockwell.sdk.springwire.*Opt-in starter; separate --release 25 artifact

These are source-shipped and test-qualified contracts, not a promise that a package has been published to your registry. The existing Go, Node, and Java core SDK pages document S3 and HTTP-native JSON. Go's standalone LNW client, Node's primary-LNW transport, Java shared-core LNW client, and Nuxt remain pending their open source-owner PRs; see the capability index for the exact boundary. Do not infer support for .NET, Rust, PHP, or Ruby from this page until a reviewed, merged, tested source contract exists.

Rollout checklist

Before enabling LNW for a tenant, reproduce the byte fixtures and malformed-frame tests, exercise auth/replay/downgrade/ scope/Object Lock denials, verify stream cancellation and upload bounds, run the language consumer tests, and record the exact server/client commit and TLS policy. Keep a tested S3 or HTTP-native mode as an explicitly selected rollback option, never as an exception handler that silently changes protocol. The wire reference, Bun/Node guide, SolidStart guide, Next.js guide, and Spring guide link directly to runnable examples and source tests.

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