Skip to main content
Version: 0.1.0-alpha.5 (alpha)

Bounded Node.js client

@latent/sdk/node implements the shared client profile on Node.js 24.19.x. The package root remains transport-neutral. This is a client for the shipped private node transport, not remote node discovery, public RPC, a browser management API, cluster routing or provider access from the host application.

Authority and channel lifetime​

Construct RpcClient with an exact http://127.x.x.x:PORT or http://[::1]:PORT, an explicit tenant and a 32–256-byte ASCII client bearer credential. Noncanonical addresses, DNS names, non-loopback IPs, URL userinfo, paths, query strings, fragments and proxy configuration are unsupported. The actual connected numeric peer and port must match. There is no hidden environment, cookie, browser-storage or provider-secret lookup.

The client copies the caller's credential into its own buffer, excludes it from ordinary inspection and marks the Authorization header as HPACK-sensitive. Shutdown zeros that owned buffer. JavaScript strings and HTTP/2 implementation copies cannot be reliably zeroized; this is not a process-memory secrecy claim. Keep client/server errors and payloads out of logs unless explicitly redacted.

One lazy HTTP/2 session owns at most one TCP socket. There is one initial connection attempt, no transparent reconnect, channel per call, automatic pagination or replay. A failed initialization or later connection failure requires an explicit new client. That decision does not authorize resending an uncertain invocation or mutation. The caller owns its Node event loop; the SDK does not create a thread or process per client, deployment or request.

Finite bounds​

LimitDefaultHard maximum
Concurrent admitted calls832
Encoded request256 KiB4 MiB
Encoded response256 KiB4 MiB
Aggregate message reservations16 MiB64 MiB
Connect ceiling3 seconds5 minutes
RPC ceiling10 seconds5 minutes

Admission is immediate: there is no SDK wait queue. Each call reserves 2 * (maximumRequestBytes + maximumResponseBytes) + 65536 bytes before encoding or I/O. Reservations cover bounded message staging, not exact JavaScript heap or process RSS. They remain owned until the physical stream closes, even after the caller's promise settles. Reply payloads copy only their exact bytes and do not retain the full reserved receive buffer.

HTTP/2 additionally has finite header, frame, stream and connection windows: 16 KiB/32-pair response headers, 4 KiB dynamic header table, 16 KiB frames, 32 KiB stream receive window and 128 KiB connection receive window. Push and reserved remote streams are disabled. The Node session's 4 MiB credit limit is an implementation accounting limit, not a hard process-memory sandbox.

Only one uncompressed unary gRPC message is accepted. Unsupported compression, duplicate header/status values, inconsistent lengths, extra frames, oversized messages and malformed Protobuf fail closed. Before allocating decoded objects, the codec enforces depth 12, 256 messages, 2048 fields, 128 entries per repeated field and 32 map entries with 128-byte string keys. String values are at most 1024 bytes; provider usage values retain exact unsigned 64-bit integers. Duplicate known singular/oneof fields, duplicate map keys and proto2 groups are rejected in this closed proto3 profile. Bounded unknown ordinary fields are skipped; unknown enum integers and opaque string values remain distinguishable. Input objects must be plain data without accessors, unknown fields or sparse lists.

These aggregate bounds can reject an otherwise schema-valid oversized object. They are supported-profile restrictions, not server authorization. Selected policy pages require an explicit size 1–32 and a cursor of at most 117 bytes. Capability pages preserve the authoritative absent/zero default of 128, permit sizes through 128 and bound cursors to 160 bytes. These are different rules, not a generic normalization. The client never follows a token automatically.

Deadlines, cancellation and physical shutdown​

Each call captures one monotonic deadline before encoding. It covers connect, HTTP/2 readiness, send, receive and decoding; a separate finite connect cap can only shorten it. CallOptions.timeoutMillis is a checked bigint of at most 300000. An optional invocation Unix-millisecond deadline further shortens the same budget; absent and present-zero remain different. Full-width wire deadlines are compared as bigint before converting a bounded local duration.

AbortSignal aborts the local stream/wait. It does not send the application Cancel RPC, prove the node never admitted the guest, or prove guest cleanup. After dispatch, a timeout, disconnect or local abort remains uncertain. Retain the original activation ID and use a fresh live signal/deadline for explicit getActivation or cancel. No ID is invented when the caller omits one; a lost server-assigned response may therefore be unrecoverable by this client.

shutdown(timeoutMillis) closes admission, cancels transport work across all outstanding calls and waits for actual call/session/socket retirement. Concurrent shutdown callers share one pending promise. Success requires all physical counters to reach zero; a shutdown timeout is not clean completion. usage() exposes those finite counters. The Node event loop must remain alive to observe close events. Local client shutdown makes no guest-cleanup claim.

Typed results and recovery​

The generated wire descriptors live under src/node/protocol, separate from the generated cross-language DTO facade and the handwritten owner/convenience modules. The supported RPCs are Invoke, Cancel, GetActivation, GetPolicy, ListPolicies, ListCapabilities, ApplyPolicy and GetPolicyOperation. Control/management operations reuse the node's real authorization and protocol; there is no ad hoc JSON endpoint or authority derived from lineage claims.

Invocation success, declared application error and platform failure are distinct response alternatives. RpcError.failure separately describes local admission, cancellation, deadline, transport, gRPC or decode failure, with bounded recovery identity and explicit outcome knowledge. Canonical platform details must match the gRPC code. Standard string/inspection output does not print server-provided diagnostics, payloads or the credential. Failure facts remain available for explicit caller handling. Unknown cancellation classifications fail safely with their raw numeric value; they are not changed into a known disposition.

Optional IDs and zero values retain presence. Request buffers are snapshotted before dispatch; returned payload bytes belong to the caller. uint64 always uses bigint, including 18446744073709551615n. Audit absence, disabled status, durable acknowledgement and uncertainty stay distinct. A bounded unknown audit status remains opaque; it cannot be promoted to a known acknowledgement and does not erase an independently observed operation receipt.

ApplyPolicy requires an explicit operation ID and generation precondition (0n means create). Receipts must agree with the requested identity, tenant, record and observed policy. Save the exact original request before sending. On uncertain response, use the same operation ID with getPolicyOperation; an absent retained receipt is unknown, not proof of nonexecution. Explicit exact replay is a caller decision using the unchanged original precondition and document, never an automatic recovery action. Server retryable hints do not cause a retry. Explicit cancellation similarly preserves accepted, already-terminal and not-found as distinct outcomes.

Validation status​

The transport tests use a controlled, separate real TCP/HTTP2 peer with generated Protobuf messages. They cover full-width values, typed outcomes, caller ownership, aborted wait versus explicit cancellation/status, ambiguous mutations and exact manual replay, malformed/oversized replies, bounded pages, shared physical shutdown, failed-connection non-replacement and rejected local authority.

The separate native participant also passes all 18 checks against a real authenticated latentd with signed maintained HTTP/blob/callee guests. It retains nine admitted activation IDs, observes all four held upstream sockets physically close, and independently verifies clean node/provider reclamation. The shared qualification PR #366 retains the exact source and bounded receipts. Current policy RPCs emit no audit acknowledgement; the participant verifies absence rather than inventing durability. Provider examples select the node's 5,000-millisecond timeout profile rather than the longer general SDK default.

This native evidence does not qualify an installed bundle or browser. The separate public application browser test does: the maintained Angular fixture invokes an exact shared HTTP application route, not a management proxy, and verifies omitted credentials and forbidden RPC paths. It does not claim production Angular Wasm SSR or end-user authentication. The package README records commands and the Node-versus-browser boundary. The SDK does not duplicate an Angular renderer or install a generic privileged browser proxy.