@kelphect/sdk-nextjs
@kelphect/sdk-nextjs is the merged, server-only Next.js adapter for Lockwell's experimental LNW/1 binary data plane. It composes the shared @kelphect/sdk-native client; it does not duplicate the codec and it never falls back to HTTP, JSON, XML, or S3. Existing S3 applications should keep using @kelphect/sdk and migrate route-by-route.
Support boundary
| Deployment or surface | Status | Tested contract |
|---|---|---|
| Next.js 16.3.3 through 16.x | supported | App Router Server Components, Server Actions, and Node Route Handlers |
| Node.js 22+ | supported | next start, standalone/container output, and representative Node serverless warm processes |
| Bun 1.4+ | supported | Node-compatible Next runtime with the shared Bun socket condition |
| Client Components and browser bundles | refused | build-time server-only/browser condition denial; no credentials or native socket code |
| Edge Route Handlers and middleware | refused | socket-free edge-light export raises LOCKWELL_UNSUPPORTED_RUNTIME |
The adapter is for Node-compatible server execution. Add export const runtime = "nodejs" to routes that import it; Edge cannot open the authenticated raw TLS socket. No compatibility fallback is attempted. The package is a source and test contract in this repository; no package publication is implied.
Install and configure
npm install @kelphect/sdk-nextjs @kelphect/sdk-native server-onlyKeep all endpoint and credential values server-only. lockwellConfigFromEnv() rejects every NEXT_PUBLIC_LOCKWELL_* variable, and LockwellSecret redacts string and JSON conversion:
LOCKWELL_NATIVE_HOST=storage.internal
LOCKWELL_NATIVE_PORT=9444
LOCKWELL_ACCESS_KEY_ID=tenant-key
LOCKWELL_SECRET_KEY=replace-me
LOCKWELL_TLS_CA=/run/secrets/lockwell-ca.pem
# Optional mTLS: provide both values, never only one.
LOCKWELL_TLS_CERT=/run/secrets/client.crt
LOCKWELL_TLS_KEY=/run/secrets/client.key
LOCKWELL_TLS_SERVER_NAME=storage.internal
LOCKWELL_LIFECYCLE=auto
LOCKWELL_TIMEOUT_MS=30000The typed alternative is createNextLockwell(config) with NextLockwellCredentials, LockwellSecret, optional credential provider, TLS CA/server name, and an atomic mTLS certificate/key pair. getNextLockwell(config) owns a Fast Refresh-safe process accessor; disposeNextLockwell() drains that owned pool. Use lifecycle: "long-lived" for next start/containers or lifecycle: "serverless" for a small lazy warm pool. Neither lifecycle takes ownership of the application's SIGTERM/SIGINT handlers.
Streaming route handler
The adapter forwards a Web ReadableStream to LNW/1 without buffering the object in memory. A bounded upload requires a valid Content-Length header (or explicit contentLength), propagates the request abort signal, and accepts validated traceparent and Idempotency-Key context:
import "server-only";
import { lockwellErrorResponse } from "@kelphect/sdk-nextjs";
import { lockwell } from "@/lib/lockwell";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export async function PUT(request: Request, { params }: { params: Promise<{ key: string[] }> }) {
try {
const storage = await lockwell();
const result = await storage.putRequest(request, {
bucket: "app-private",
key: (await params).key.join("/"),
request: { signal: request.signal, timeoutMs: 120_000 },
cache: { mode: "revalidate", namespace: "tenant-from-auth" },
});
return Response.json({ etag: result.etag, versionId: result.versionId }, { status: 201 });
} catch (error) {
return lockwellErrorResponse(error);
}
}NextLockwellAdapter.getResponse() returns the native response stream, supports bounded single ranges, and emits private/no-store headers by default. headCached() caches metadata only under a stable tenant/principal namespace; object bytes are never put in Next's server cache.
Cache, lifecycle, and observability
Use expireLockwellObject() in a Server Action for immediate read-your-writes, or revalidateLockwellObject() in a Route Handler for stale-while-revalidate behavior. Tags hash the namespace, bucket, key, and optional version so a request-supplied bucket name cannot cross a tenant boundary.
The package exports lockwellRequestContext() for cancellation, traceparent, and idempotency propagation, lockwellDeploymentDiagnostics()/redactedConfig() for safe runtime diagnostics, and registerLockwellInstrumentation()/lockwellTraceSink() for optional metrics and OpenTelemetry-compatible spans. safeLockwellError() and lockwellErrorResponse() return bounded, no-store errors; unknown exceptions become a generic 500. Diagnostics and errors exclude credentials, tokens, certificates, payloads, and filesystem paths.
Data and failure semantics
The adapter exposes the shared native operations through adapter.client: bucket and object CRUD, ranges and streaming, copy, pagination, multipart resume/abort, versions/delete markers, tags, retention, legal hold, and signed capabilities. LNW capability negotiation, checksums, upload bounds, retryability, cancellation, and tenant authorization remain owned by the shared SDK/server. Application operations are never silently replayed; one-shot streaming bodies require a caller-owned recovery strategy. SSE-C is not an LNW capability: SSE-C-shaped headers are rejected before I/O, so use the documented S3 surface when customer-provided keys are required. Governance bypass and the Admin wire surface are absent.
Verification and example
The runnable examples/nextjs-native-wire fixture covers App Router actions, streaming PUT/GET/HEAD/DELETE, bounded ranges, multipart, checksums, versioning, retention/legal-hold denial, interrupted streams, cache invalidation, standalone output, serverless warm reuse, and Edge/Client Component denial. The package contract is documented in docs/nextjs-native-sdk.md and its tests under sdk/nextjs/test.
cd sdk/nextjs && npm ci && npm run check
cd ../../examples/nextjs-native-wire && npm ci && npm run build && npm run verify && npm run verify:serverlessThe merged adapter's Oracle ARM64 evidence covers Node and Bun live TLS workflows, standalone/container and representative serverless builds, packed consumers, browser/Edge denial scans, quality, supply-chain, and bounded concurrency. Its benchmark is a single-host measurement, not a general performance or hosted-provider claim.
Migration and rollback
Adopt one Server Component, Action, or Route Handler at a time while keeping the existing S3/HTTP-native path explicit. There is no automatic fallback. Rollback removes the adapter import, drains its owned pool with disposeNextLockwell(), and restores the prior route; object bytes, versions, retention, and legal holds require no migration.