@netscript/service
Service bootstrap builders, health probes, and Hono/oRPC runtime wiring for NetScript
applications. This page is written against the package's public surface reported by deno doc.
For the full index of packages and plugins return to the
reference overview.
The package has three layers. Layer 1 exposes small primitives for health, error, RPC,
OpenAPI, and Scalar docs handlers. Layer 2 exposes createService(), a fluent builder
that materializes a mountable ServiceApp or starts a listener. Layer 3 exposes
defineService(), the preset used by generated service entrypoints.
The service router is always an input to the builder. build() returns a non-listening
ServiceApp, which keeps the RFC 14 unified-platform seam open for callers that mount
service apps into another host. serve() starts a Deno listener and returns a
RunningService handle with stop() for tests, local development, and process supervisors.
Public types are package-owned structural mirrors: callers do not need to import Hono or oRPC
types to describe a service surface. Runtime interoperability still uses the real Hono app and
oRPC handlers internally. LoggerMiddlewareOptions is re-exported from the sibling
@netscript/logger/middleware package because it is a first-party
@netscript/* contract, not an upstream vendor surface.
Builder and presets
| Symbol | Signature | Description |
|---|---|---|
createService |
function createService<T extends ServiceRouter>(router: T, config: ServiceConfig): ServiceBuilder<T> |
Factory function to create a new service builder. |
defineService |
async function defineService<T extends ServiceRouter>(router: T, options: DefineServiceOptions): Promise<RunningService> |
One-liner preset for creating a fully-configured service. |
Health primitives
| Symbol | Signature | Description |
|---|---|---|
createHealthHandler |
function createHealthHandler(options?: HealthHandlerOptions): ServiceHandler |
Creates a comprehensive health check handler that runs all checks in parallel. |
createLivenessHandler |
function createLivenessHandler(): ServiceHandler |
Creates a simple liveness check handler. |
createReadinessHandler |
function createReadinessHandler(checks: Array<() => Promise<boolean>>): ServiceHandler |
Creates a readiness check handler that runs multiple async checks. |
healthChecks |
const healthChecks: { database; kv; service; custom } |
Pre-built health checks for common dependencies (database, kv, service, custom). |
HEALTH_STATUS |
const HEALTH_STATUS: { healthy; degraded; unhealthy } |
Health status values emitted by service health handlers. |
RPC, OpenAPI, and docs handlers
| Symbol | Signature | Description |
|---|---|---|
createRPCHandler |
function createRPCHandler<T extends ServiceRouter>(router: T, config?: RPCHandlerConfig): FetchHandler |
Creates an oRPC RPC handler for type-safe client communication. |
createRPCPlugins |
function createRPCPlugins(config: RPCHandlerConfig): ServiceHandlerPlugin[] |
Creates the standard set of oRPC plugins. |
createOpenAPIHandler |
function createOpenAPIHandler<T extends ServiceRouter>(router: T, config?: RPCHandlerConfig): FetchHandler |
Creates an oRPC OpenAPI handler for REST-style API access. |
createOpenAPISpec |
function createOpenAPISpec<T extends ServiceRouter>(router: T, config: OpenAPIConfig): ServiceHandler |
Creates an OpenAPI specification endpoint handler. |
createScalarDocs |
function createScalarDocs(options: ScalarDocsOptions): ServiceHandler |
Creates a Scalar API documentation UI handler. |
createScalarJs |
function createScalarJs(): ServiceHandler |
Creates a handler to serve the bundled Scalar JS file. |
When a procedure declares NetScriptProcedureMeta.access, createOpenAPISpec() preserves the
operation and projects access as follows:
| Contract declaration | OpenAPI operation |
|---|---|
authentication: 'none' |
security: [] |
authentication: 'required' |
security: [{ bearerAuth: scopes }] |
Required authorization.roles |
x-netscript-roles: roles |
authentication: 'optional' |
security: [{}, { bearerAuth: [] }] |
| No authentication declaration | No generated operation-level security field |
The generated bearerAuth component uses HTTP bearer authentication. Optional access remains
visible in the generated specification even though the current contract authorizer rejects it at
construction.
Request-body limit
| Symbol | Signature | Description |
|---|---|---|
createBodyLimitMiddleware |
function createBodyLimitMiddleware(options: ServiceBodyLimitOptions): ServiceMiddleware |
Creates middleware that rejects request bodies larger than maxBytes with a typed JSON 413 before any handler parses them. Throws RangeError unless maxBytes is a positive safe integer. |
PAYLOAD_TOO_LARGE_ERROR |
const PAYLOAD_TOO_LARGE_ERROR: 'PAYLOAD_TOO_LARGE' |
Error code carried by the JSON body of a 413 body-limit rejection. |
The builder form is createService(...).withBodyLimit({ maxBytes }) and the preset form is
defineService(router, { bodyLimit: { maxBytes } }). A request that declares Content-Length is
rejected from the header. A chunked request is counted as it streams and rejected once it passes
the limit, so at most maxBytes plus one chunk is buffered. The rejection body is a
PayloadTooLargeResponse: { error: 'PAYLOAD_TOO_LARGE', message, maxBytes }. There is no read
timeout.
Pipeline order
defineService and a builder chain installed in the same order produce this request pipeline:
| Order | Stage | Installed by |
|---|---|---|
| 1 | Tracing | always, at construction |
| 2 | CORS | withCors() |
| 3 | Request logging | withLogger() |
| 4 | Caller middleware, in order | use() / DefineServiceOptions.middleware |
| 5 | Authentication, then authorization | withAuthn() / withAuthz(), installed by build() |
| 6 | Request-body limit | withBodyLimit() / DefineServiceOptions.bodyLimit, installed by build() |
| 7 | OpenAPI spec, docs, RPC and OpenAPI projections, custom routes | build() |
use() registers middleware immediately, so builder middleware runs in call order relative to
withCors() and withLogger(). It always runs before the stages that build() installs. A
rejection returned at stage 4 or 6 keeps the CORS headers and is logged.
Rate limits and client addresses
createService(...).withRateLimit(options) adds middleware through the existing use() seam. It
runs in call order, before deferred routes and both RPC/REST projections. Install it before
immediate health/info routes if those routes should be limited. Repeated calls add stages.
The root also exports ServiceRateLimitOptions, ServiceProxyTrust, RateLimitStore,
RateLimitRequest, and RateLimitDecision for builder/port contracts.
The @netscript/service/rate-limit subpath exposes:
| Symbol | Kind | Description |
|---|---|---|
createRateLimitMiddleware |
function | Creates route-scoped quota middleware returning JSON 429 and whole-second Retry-After. |
createKvRateLimitStore |
function | Uses KV atomic compare-and-set plus TTL in a dedicated prefix, with a bounded retry budget. |
createMemoryRateLimitStore |
function | Creates a synchronous, capped development store with bounded TTL eviction. |
resolveServiceClientAddress |
function | Resolves the socket peer or explicitly trusted XFF hops, walking right to left. |
ServiceRateLimitOptions |
interface | routes, limit, windowMs, store, and optional key, trustProxy, ipv6Prefix, now. |
ServiceProxyTrust |
type | false or an address predicate identifying trusted proxy hops; default off. |
RateLimitStore |
interface | Port reserving one slot atomically with consume(request). |
RateLimitRequest |
interface | Key, quota, window, epoch milliseconds, and optional request cancellation signal. |
RateLimitDecision |
interface | Whether a slot was reserved and the epoch-millisecond window end. |
KvRateLimitStoreOptions |
interface | Dedicated prefix and optional maxAttempts (default 8, maximum 32). |
MemoryRateLimitStoreOptions |
interface | Optional retained-key cap maxKeys (default 10,000). |
The subpath also re-exports the first-party integration types ServiceContext,
ServiceEnvironment, ServiceMiddleware, KvStore, KvKey, KvEntry, KvListOptions,
KvSetOptions, AtomicCheck, AtomicMutation, and AtomicResult used by these signatures.
The six ancillary KV types are intentional: the accepted KvStore port references them through
its public methods. Re-exporting them preserves the upstream port and makes Deno's documentation
lint resolve its transitive signature types, without introducing a duplicate KV interface.
Selected routes share one quota per key and epoch-aligned window. Paths are exact or end in /* for
a subtree (including its root). Rejections return { error: 'RATE_LIMITED' } with 429 and
Retry-After rounded up to the remaining whole seconds. Unmatched paths perform no store IO.
Separate policies use separate memory stores or KV prefixes unless sharing a quota is intended. CAS
retry exhaustion or memory capacity pressure rejects conservatively; store failures throw.
ServiceEnvironment is exported from the root entrypoint. ServiceApp.fetch(request, env) and
ServiceApp.request(input, init, env) accept it. The listener supplies remoteAddr on plain and
TLS requests so Hono's Deno getConnInfo works. The default limiter key is the resolved client
address; IPv6 keys are canonicalized and grouped by /64, with ipv6Prefix (0–128) selecting
another prefix. IPv4-mapped IPv6 peers use their corresponding dotted IPv4 bucket before prefix
masking, so distinct IPv4 clients retain independent quotas on dual-stack listeners. Native IPv4
and custom keys stay unchanged. This grouping affects quota keys only, not
socket metadata or proxy trust. A mounted app without metadata uses a shared unknown bucket and
logs a warning once per stage through the request logger or service package logger.
KV counter TTL bounds each key's lifetime, not total live cardinality. Distinct attacker-chosen keys can allocate distinct counters until window expiry; use backend admission/capacity controls and stable custom keys. The memory store instead rejects new keys when its live-key cap is full.
Like auth's #2026 transport policy, forwarded headers are ignored by default. Explicit proxy trust
identifies permitted socket/hop addresses; malformed or excessive XFF chains fall back to the
socket. Auth's HTTPS helper is protocol-specific and has no reusable hop resolver, so this client
address resolver does not alter deriveHttps or trustProxyHeaders.
See Protect an anonymous service route for examples and storage/performance guarantees.
Error and routing handlers
| Symbol | Signature | Description |
|---|---|---|
createErrorHandler |
function createErrorHandler(serviceName: string): ServiceErrorHandler |
Creates a global error handler for uncaught exceptions. |
createNotFoundHandler |
function createNotFoundHandler(serviceName: string): ServiceHandler |
Creates a 404 Not Found handler for unmatched routes. |
Configuration and option types
| Symbol | Kind | Description |
|---|---|---|
ServiceConfig |
interface | Service configuration options (input to createService). |
DefineServiceOptions |
interface | Options for the defineService preset, including cors (explicit origins or the NETSCRIPT_CORS_ORIGINS workspace allowlist), middleware (caller middleware after CORS and logging, before auth) and the opt-in bodyLimit. |
ServiceBodyLimitOptions |
interface | { maxBytes } request-body limit accepted by withBodyLimit() and DefineServiceOptions.bodyLimit. |
PayloadTooLargeResponse |
interface | JSON body of a 413 body-limit rejection: { error: 'PAYLOAD_TOO_LARGE', message, maxBytes }. |
ServeOptions |
interface | Options for starting a service listener. |
CorsOptions |
interface | CORS options supported by withCors(). Omitted origin reads comma-separated exact HTTP(S) origins from NETSCRIPT_CORS_ORIGINS; unset/blank denies cross-origin access. Explicit origins override the environment; wildcard with credentials fails build(). |
OpenAPIConfig |
interface | Configuration for OpenAPI spec generation. |
RPCHandlerConfig |
interface | Configuration options for RPC handlers. |
ScalarDocsOptions |
interface | Configuration for the Scalar docs UI. |
HealthHandlerOptions |
interface | Options for createHealthHandler. |
LoggerMiddlewareOptions |
interface | Options for the logger middleware (re-exported from @netscript/logger/middleware). |
CORS origins
DefineServiceOptions.cors configures the same policy as withCors(). If origin is omitted,
the builder snapshots NETSCRIPT_CORS_ORIGINS: comma-separated exact HTTP(S) origins, without
paths or trailing slashes. Unset or blank means no cross-origin browser access. For example,
NETSCRIPT_CORS_ORIGINS='https://app.example,https://admin.example' permits those two origins.
An explicit cors: { origin: ['https://app.example'] } overrides the environment; origin: []
denies all cross-origin access. Invalid environment entries fail configuration. Wildcard origins
with credentials: true fail before the listener starts.
Generated CLI/Aspire helpers supply the enabled web apps' allocated HTTP endpoint origins to every
service and plugin service resource. This generated value overrides a declared resource environment
value; a workspace without enabled web apps supplies an empty allowlist. Independent services must
configure their launch environment themselves. Origins outside the allowlist receive no
Access-Control-Allow-Origin header. CORS controls browser response access; it does not authenticate
callers. The chosen browser authentication topology
uses a BFF; scaffolding that topology is separate follow-up scope.
Listener bind address
ServeOptions.hostname and DefineServiceOptions.hostname choose the interface the listener binds.
The value is forwarded unchanged to Deno.serve on both the plain and the TLS listener, and
RunningService.addr.hostname reports the address that was bound. Omitting it keeps Deno's
default, so the listener binds every IPv4 interface (0.0.0.0). Pass '127.0.0.1' to keep an
endpoint, such as an unauthenticated local control surface, reachable only from the same machine.
import { createService } from '@netscript/service';
const running = await createService({}, { name: 'control' })
.withHealth()
.serve({ hostname: '127.0.0.1', port: 0 }); // port 0: the OS picks a free port
console.log(running.addr.hostname); // '127.0.0.1'
await running.stop();
Generated service scaffolds leave hostname unset. Inside a container, a loopback-only bind makes
the service unreachable through its published ports, so narrow the bind per service, where you know
who must reach it.
Service surface types
| Symbol | Kind | Description |
|---|---|---|
ServiceBuilder |
interface | Fluent builder for configuring and materializing a NetScript service. |
ServiceApp |
interface | Minimal mountable service application returned by build(). |
RunningService |
interface | Running service handle returned by serve() and defineService(). |
RunningServiceAddress |
interface | Network address assigned to a running service listener. |
ServiceContext |
interface | Minimal context shape exposed to service middleware and handlers. |
ServiceRequest |
interface | Minimal request shape exposed to service middleware and handlers. |
ServiceMiddleware |
interface | Middleware function accepted by the service builder. |
ServiceHandler |
interface | Service route handler accepted by the builder route API. |
ServiceHandlerContext<TCustom> |
type alias | Readonly custom context plus optional framework-owned db, traceHeaders, and principal fields. |
Principal |
interface | Authenticated identity with subject, readonly scopes/roles, scheme, and verified claims. |
ServiceHandlerPlugin |
interface | Structural oRPC plugin accepted by service handler factories. |
ServiceErrorHandler |
interface | Error handler used by service applications. |
FetchHandler |
interface | Structural fetch handler used by RPC and OpenAPI service adapters. |
FetchHandlerResult |
interface | Result returned by oRPC-compatible fetch handlers. |
Health types
| Symbol | Kind | Description |
|---|---|---|
HealthCheck |
interface | A single health check definition. |
HealthResponse |
interface | Response format for the health endpoint. |
Database |
interface | Database client capable of a health-check query. |
HealthStatus |
type alias | Health status emitted by the service health endpoint (typeof HEALTH_STATUS[keyof typeof HEALTH_STATUS]). |
Router and context type aliases
| Symbol | Signature | Description |
|---|---|---|
ServiceRouter |
type ServiceRouter = Record<string, unknown> |
Router definition accepted by the service builder and handler factories. |
ContextFactory<TCustom> |
type ContextFactory<TCustom extends object = Record<never, never>> = (context: ServiceContext) => TCustom |
Creates the custom part of each service handler context. |
ServiceHandlerContext<TCustom> |
type ServiceHandlerContext<TCustom extends object = Record<never, never>> = Readonly<TCustom> & { db?; traceHeaders?; principal?; } |
Context visible after framework fields are composed. principal is optional and must be narrowed by handlers that require identity. |
DbContext |
type DbContext = Record<string, unknown> |
Database context injected into service handler context. |
Contract-declared authentication and authorization
@netscript/service/auth is provider-agnostic. .withAuthn() turns a request into the
service-owned Principal; .withAuthz() decides whether that principal may invoke the matched
procedure. The primary policy source is the procedure's own
.meta({ access: { authentication, authorization? } }) declaration:
import { createService } from '@netscript/service';
import {
createContractAuthorizer,
createStaticCredentialAuthenticator,
} from '@netscript/service/auth';
import { OrdersContractV1 } from '@example/contracts';
import { router } from './router.ts';
const authenticator = createStaticCredentialAuthenticator({
credentials: {
local: {
subject: 'service:orders',
scopes: ['orders:read'],
roles: ['service'],
},
},
});
const app = createService(router, { name: 'orders' })
.withRPC()
.withAuthn({ authenticator })
.withAuthz({ authorizer: createContractAuthorizer(OrdersContractV1) })
.build();
Contract enforcement is opt-in: existing unguarded services, scaffolds, and standalone
createScopeAuthorizer() consumers are unchanged. It activates only when an application passes a
createContractAuthorizer(contract, { fallback?, rawRoutes? }) result to .withAuthz().
Contract metadata wins on disagreement. A match-aware fallback, including
createScopeAuthorizer(), is consulted only when a matched procedure has no access metadata. No
metadata and no matching fallback rule denies, regardless of the fallback's standalone
denyByDefault option. The builder binds one resolver to the actual REST and RPC paths and their
aliases and shares it between authn and authz, so a declared public procedure is not rejected by an
earlier authentication stage.
createScopeAuthorizer() remains a supported standalone legacy path-prefix authorizer and a
match-aware migration fallback; it is not deprecated.
authentication: 'optional' is declared for future support, currently rejected.
createContractAuthorizer() throws
[netscript.service.contract-policy] optional authentication is unsupported: <procedure> during
construction, before any request.
Raw routes beside a contract router
A request under the guarded prefix that matches no contract procedure is denied with
authz.no-contract-procedure, including raw routes added with .route(method, path, handler).
Declare each raw route that should be served through the rawRoutes option:
const authorizer = createContractAuthorizer(OrdersContractV1, {
rawRoutes: [{ path: '/api/tools/mcp', authentication: 'required' }],
});
const app = createService(router, { name: 'orders' })
.withRPC()
.withAuthn({ authenticator })
.withAuthz({ authorizer })
.route('all', '/api/tools/mcp', handler)
.build();
A declared raw route requires a successfully authenticated principal. This applies even when its path
is outside protect or inside allowAnonymous, so a declaration never makes a path public. An
optional authorization: { scopes?, roles? } is enforced like a procedure's declared
authorization. Matching is exact and case-sensitive, and ignores only a trailing slash. Declaring
/api/tools/mcp covers neither /api/tools/mcp-admin nor /api/tools/mcp/nested, and every
undeclared sibling stays denied with authz.no-contract-procedure.
Declarations are validated before any request. Construction throws
[netscript.service.contract-policy] invalid raw route: <path> ... for a path that is not absolute,
a path containing *, :, {, }, ? or #, an authentication other than 'required', or a
path declared twice. Binding then throws
[netscript.service.contract-policy] raw route overlaps the contract projection: <path> when the
path falls under an RPC mount or alias or matches a REST procedure path. Binding happens in
.build().
Internal procedures and the internal service credential
.meta({ access: { audience: 'internal' } }) restricts a procedure to service-to-service callers
of the same installation: workers, sagas and triggers presenting the internal service credential.
Both contract authorizers enforce it on the RPC projection and on the OpenAPI projection, including
oRPC's default OpenAPI path (POST <apiPath>/<router>/<procedure>) when the contract declares no
route.path. Requests resolve to the procedure oRPC executes: the undecoded pathname is matched
with oRPC's own route patterns and rou3 precedence (static before parameter before wildcard),
not contract declaration order. A request using a method no procedure declares on an internal
procedure's OpenAPI path fails closed toward that procedure. A user session never satisfies the
audience, and the check uses the principal's identity, not its claims or roles. Construction
throws for an 'internal' audience combined with authentication: 'none', for any other audience
value, and for procedures with different access that share one OpenAPI route.
createContractOverlayAuthorizer(contract, { fallback?, isInternalCaller? }) governs only
procedures that declare meta.access. Every other request keeps the service's own policy: the
protect/allowAnonymous path guard, plus the optional fallback authorizer. Marking a few
internal procedures therefore never forces authentication onto public ones. To keep an otherwise
public service public, set allowAnonymous: ['/api', '/health']; access-marked procedures are
resolved before the path guard. createContractAuthorizer also enforces the audience, but it
still governs every procedure in the contract.
The credential is derived from one per-installation secret. Carriers deliver
NETSCRIPT_INSTALLATION_SECRET_FILE, a file reference, never the value. loadInstallationSecret()
reads it once at startup. The file holds a textual secret, such as base64 or hex, with surrounding
whitespace trimmed (4 KiB maximum, 32 bytes minimum). In-memory Uint8Array material passed to
createInstallationSecret() is used verbatim. Each service accepts only the bearer
derived for its own name (HKDF-SHA-256), so a credential cannot be replayed across services. A
credential expires by rotation: once the secret changes, every credential derived from the old one
is rejected. createCompositeAuthenticator([...]) lets one guarded service accept the internal
credential alongside user sessions over the single AuthenticatorPort. Callers send the
credential with the SDK's createInternalCredentialSdkClientContribution({ service }).
import { createService } from '@netscript/service';
import {
createCompositeAuthenticator,
createContractOverlayAuthorizer,
createInternalCredentialAuthenticator,
loadInstallationSecret,
} from '@netscript/service/auth';
import { OrdersContractV1 } from '@example/contracts';
import { router } from './router.ts';
import { sessionAuthenticator } from './session.ts';
const secret = await loadInstallationSecret();
const app = createService(router, { name: 'orders' })
.withRPC()
.withAuthn({
authenticator: createCompositeAuthenticator([
createInternalCredentialAuthenticator({ secret, service: 'orders' }),
sessionAuthenticator,
]),
})
.withAuthz({ authorizer: createContractOverlayAuthorizer(OrdersContractV1) })
.build();
Explicit service posture
ServiceAuthPolicy records either native guards ({ authn, authz? }) or a deliberate
public opt-out ({ public: true, reason }). ServiceGuardedAuthPolicy and
ServicePublicAuthPolicy are mutually exclusive; a public reason must be nonblank.
assertServiceAuthPolicy(value) rejects absent or ambiguous postures and malformed callable
ports with a redacted TypeError. It preserves the original options: a custom
allowAnonymous list replaces the native default; the assertion does not add /health.
It validates configuration only and does not authenticate requests or install middleware.
DefineServiceOptions.auth requires this policy. defineService() runs the shared assertion
before builder configuration, database startup, or listener creation, so JavaScript callers receive
an actionable TypeError as well as TypeScript callers receiving a missing-field error.
Guarded policies install the existing native stages unchanged; public policies deliberately skip
them. /api is guarded and /health stays anonymous with native defaults.
In 0.0.8, search existing entrypoints for defineService( and choose native guards or explicitly
record a public service's reason. The exact public migration is:
-await defineService(router, { name: 'status' });
+await defineService(router, {
+ name: 'status',
+ auth: { public: true, reason: 'Public status service with no protected operations' },
+});
netscript service add uses the same BFF composition as auth installation: browserAuthenticator
plus createContractAuthorizer(router). /health, /api/openapi.json, /api/docs and
contract-declared public demo procedures remain anonymous. A contract-protected REST or RPC
procedure requires a bearer session (401 when absent) and a successful contract authorization
decision (403 when denied). Authorized calls succeed. Without enabled auth, the generated entrypoint
records a greppable public opt-out with a reason explaining how to protect it. Lifecycle commands
reconcile scaffold-owned opt-outs; authored policies remain authoritative, and generate aspire
leaves authored inputs unchanged.
Generated contracts include a session identity probe with authentication: 'required'.
That metadata describes the guarded posture and remains visible in OpenAPI/discovery even when
auth is not installed. An explicit service-wide public opt-out skips authentication and
authorization, including this contract requirement: anonymous session calls then return
200 with { authenticated: false }. The metadata alone does not install guards. Install auth
or configure a guarded service policy to enforce it; the probe reports whether the runtime
provided a principal.
This policy requirement applies to the preset; lower-level createService() callers still compose
their middleware explicitly.
import { assertServiceAuthPolicy, type ServiceAuthPolicy } from '@netscript/service/auth';
const policy: ServiceAuthPolicy = {
public: true,
reason: 'Public status service with no protected operations',
};
assertServiceAuthPolicy(policy);
@netscript/service/auth surface
| Symbol | Description |
|---|---|
ServiceAuthPolicy |
Native guarded posture or an explicit public opt-out. |
ServiceGuardedAuthPolicy |
Native authentication with optional authorization; excludes public fields. |
ServicePublicAuthPolicy |
Literal public opt-out with a nonblank reason; excludes guard fields. |
assertServiceAuthPolicy |
Validates an explicit posture and required callable ports without changing options. |
createContractAuthorizer |
Traverses a metadata-bearing contract and returns an opt-in authorizer bound by the service builder. |
createContractOverlayAuthorizer |
Enforces only access-marked procedures and leaves every other request to the service's own policy. |
createInternalCredentialAuthenticator |
Accepts the internal bearer derived for this service and mints internal service principals. |
isInternalServicePrincipal |
Default InternalCallerPredicate; true only for principals minted by the internal-credential authenticator. |
createCompositeAuthenticator |
Tries authenticators in order over one AuthenticatorPort and returns the first success. |
loadInstallationSecret / createInstallationSecret |
Import the per-installation secret from its file reference or from memory. |
deriveInternalCredential |
Derives the per-service internal bearer from the installation secret. |
createScopeAuthorizer |
Ordered scope/role rules usable standalone or as a match-aware legacy fallback. |
createStaticCredentialAuthenticator |
Maps configured credentials to principals. |
createTrustedHeaderAuthenticator |
Maps trusted upstream identity headers to principals. |
Principal |
Service-owned identity contract. |
ContractPolicyAuthorizerPort |
Authorizer that binds to the builder's REST/RPC projection paths. |
ContractAuthorizerRawRoute |
Exact, authentication-required raw route declared through createContractAuthorizer(contract, { rawRoutes }). |
Exports
The following entrypoints are published alongside the root export:
| Export | Entrypoint | Purpose |
|---|---|---|
@netscript/service |
./mod.ts |
Full service surface (documented above). |
@netscript/service/commands/relay |
./commands-relay.ts |
Decoded bounded relay and checked sink lifecycle. |
@netscript/service/commands/testing |
./commands-testing.ts |
Atomic memory store and explicit test controls. |
@netscript/service/commands |
./commands.ts |
Opaque command definitions, once-only executor and codecs. |
@netscript/service/auth |
./src/auth/mod.ts |
Service authentication and authorization handlers. |
@netscript/service/rate-limit |
./src/rate-limit/mod.ts |
Route quotas, atomic stores, and trusted client-address resolution. |
@netscript/service/rpc-path |
./src/primitives/rpc-path.ts |
Type-safe RPC route mapping utilities. |
@netscript/service/internal-credential |
./src/auth/internal-credential/mod.ts |
Dependency-free installation secret loading and internal credential derivation. |
Command definitions and codecs
@netscript/service/commands defines immutable command policies and bounded canonical codecs
without executing handlers. Importing, defining and encoding require no permissions; execution
uses the explicitly supplied store/business operations and their permissions. JSON uses jcs-v1
with RFC 8785 ordering and numeric/string serialization. Default limits are depth 64, 10,000
aggregate values/keys and 1 MiB UTF-8 bytes; options may only tighten these bounds. Stored text must
match canonical serialization exactly. Synchronous schema validation must preserve canonical JSON
identity to keep replay stable.
| Symbol | Kind | Description |
|---|---|---|
defineCommand |
function | Validates durable identity and freezes an opaque definition. |
jsonCodec |
function | Synchronous Standard Schema validation with stable bounded JSON. |
canonicalCommandJson |
function | Produces bounded canonical JSON text. |
commandTraceContext |
function | Reuses strict W3C parent/state validation at producer boundaries. |
parseCanonicalCommandJson |
function | Accepts only bounded canonical stored text. |
CommandCodec |
type alias | Typed response/payload encoding and decoding boundary. |
CommandJsonLimits |
type alias | Tighten-only depth, item and byte safeguards. |
CommandJson |
type alias | Readonly recursive I-JSON values. |
CommandActor |
type alias | Principal or system identity, excluding roles and claims. |
CommandEnvelope |
type alias | Input, actor and transport fields with string version tokens. |
CommandTraceContext |
type alias | Validated W3C traceparent and optional tracestate. |
CommandAuditInput |
type alias | Redacted audit intent. |
CommandContext |
interface | Transaction handle and synchronous side-record operations. |
CommandDefinition |
interface | Opaque immutable identity, replay policy and record requirements. |
CommandDefinitionSpec |
type alias | Construction specification with privately bound handler. |
commandDefinitionBinding |
type-only symbol | Opaque marker; unavailable as a runtime export. |
commandExecutorCapability |
type-only symbol | Private binding capability; unavailable as a runtime export. |
CommandIdempotency |
type alias | Frozen semantic scope, fingerprint and replay codec. |
CommandIdempotencyMode |
type alias | Required or optional key policy. |
CommandIdempotencySpec |
type alias | Construction policy defaulting to required keys. |
CommandOutboxInput |
type alias | Delivery intent with typed codec. |
CommandRecordRequirement |
type alias | Required, optional or forbidden side-record policy. |
CommandError |
class | Frozen redacted failure with a trusted nonserialized cause. |
CommandFailure |
type alias | Bounded discriminated failure and retry vocabulary. |
IsolationLevel |
type alias | Database-owned transaction isolation vocabulary, re-exported as a type. |
Back to the reference overview.
Command testing
@netscript/service/commands/testing exports createMemoryCommandStore, MemoryCommandStore,
MemoryCommandBusiness, MemoryCommandSnapshot, MemoryCommandStoreOptions and
CommandStoreBarrier. The store atomically commits business, receipt, audit and outbox drafts and
exposes frozen detached snapshots. A one-use before-commit barrier controls concurrency. Explicit
receipt seeding and outside-transaction business writes support corruption and rollback negative
controls. These helpers require no permissions and certify no real provider. The fake supports
Serializable isolation, zero claim wait, a bounded cooperative timeout, terminal busy and one
callback attempt; it may reject disjoint concurrent drafts because it uses a global state revision.
Command executor
| Symbol | Kind | Description |
|---|---|---|
createCommandExecutor |
function | Compose once-only execution over a bound same-commit store. |
CommandExecutor |
interface | Execute a genuine definition and await the store boundary. |
CommandExecution |
type alias | Decoded value, applied/replayed outcome, idempotency state and original correlation. |
CommandExecutorOptions |
type alias | Store, optional clock/IDs/telemetry, claim wait and tighten-only record limits. |
CommandRecordLimits |
type alias | 64 audit/64 outbox defaults; aggregate canonical side-row byte default 64 KiB. |
CommandClock |
interface | Injected valid Date source, detached by the executor. |
CommandIdSource |
interface | Fresh bounded identifiers. |
CommandTelemetryPort |
interface | Once-only tracing extension preserving values and errors. |
CommandTelemetrySpan |
interface | Finish with finite outcome/count vocabulary after the boundary. |
CommandTelemetryStart |
type alias | Name/version, requested or default isolation, provider and keyed state. |
CommandTelemetryResult |
type alias | Finite outcome/idempotency/counts and optional bounded failure kind. |
The executor freezes detached bounded I-JSON input before the two identity callbacks, each called once. Required keys are 16–256 UTF-8 bytes; scope and other identity/header strings are bounded to 1–256 bytes. Identity uses exact canonical semantic material and a separate key digest, excluding scheme, correlation and W3C transport fields. Buffers include full side-row metadata in their shared byte budget; limits only tighten published defaults. Transactions receive a five-second timeout.
Replay validates receipt material without handler/side writes. Busy and cancellation surface only
after rollback; arbitrary business errors retain identity. Provider failures use the database-owned
CommandStoreError contract. Audit, outbox and receipt completion share the winning receipt ID and
flush in that order. Optional unkeyed attempts skip receipts. The telemetry port prepares the later
adapter; no transport, hidden retry or production fault option is provided.
Command conformance fixtures
The focused @netscript/service/commands/testing subpath provides an instance-bound
CommandFaultController, createTestingCommandExecutor() and a generic
CommandConformanceFixture<TTx>. Production executor options contain no fault controls. The testing
factory uses the production algorithm and the exact seven command boundaries, including a
postcommit response-loss boundary whose same-key retry must replay the original receipt.
runCommandConformance(createFixture) checks atomic rollback and recovery, ordered side writes,
replay/mismatch and namespace/version changes, corrupt replay, CAS, cancellation, busy, callback
counts and retry refusal. createMemoryCommandConformanceFixture() supplies a simulated fixture.
An outside-transaction business write is an explicit negative control: the shared suite must detect
its surviving effect after rollback. Database-owned store/row types and the generic business handle
allow provider adapters to supply their own inspection and fixture operations. This finite suite
certifies no real driver, locking strategy or pooled session settings.
assertCommandDeterminism() samples actual scope/fingerprint identity logic over equivalent frozen
inputs and actor material, without a transaction or handler. Its bounded 2–32 samples (default four)
can detect changing closure state in those invocations; sampled_equivalence is no universal purity
proof. Supplied provider operations need their own permissions; imports and simulated fixtures do
not.
Future traceparent fields remain opaque after known W3C prefix validation, with a bounded HTTP field-value guard rejecting CR, LF, NUL and other ASCII control bytes except HTAB. SP, HTAB and obs-text remain accepted in the opaque suffix; empty tracestate positives are preserved. This follows RFC9110 field values and W3C traceparent versioning.
Command outbox relay
createCommandOutboxRelay starts no resource: existing scheduling calls drainOnce and shutdown
awaits stop. It decodes canonical bounded payload and W3C fields, snapshots sink registration,
publishes before settlement and preserves stable identities across retries/crashes. All drains
share one bounded concurrency ceiling; stopping prevents claims and awaits every active/queued
drain. Invalid classifier/backoff cannot write arbitrary failure text or lose leases. Worker
acceptance is checked and normalized before the database writes it together with publication.
Uncertain settlement errors surface without a speculative release. Optional C4-compatible tracing
selects finite command attributes and preserves deferred/producer propagation without raw identity
attributes. The database owns leases; no second relay, queue or runtime DDL is introduced.
| Symbol | Kind | Signature | Description |
|---|---|---|---|
createCommandOutboxRelay |
function | function createCommandOutboxRelay(options): RunningCommandOutboxRelay |
Compose the one bounded decoded relay. |
CommandOutboxDelivery |
type alias | type CommandOutboxDelivery |
Frozen decoded delivery and transport context. |
CommandOutboxRelayOptions |
type alias | type CommandOutboxRelayOptions |
Explicit finite lifecycle/retry policy and supplied ports. |
CommandOutboxSink |
interface | interface CommandOutboxSink |
Documented acceptance boundary. |
CommandRelayTelemetryPort |
interface | interface CommandRelayTelemetryPort |
Privacy-safe structural C4 observer extension. |
RunningCommandOutboxRelay |
interface | interface RunningCommandOutboxRelay |
Drain/stop lifecycle. |
CommandRelayError |
class | class CommandRelayError |
Closed sink/decode diagnostic. |
COMMAND_RELAY_FAILURE_CLASSES |
const | readonly tuple |
Closed persisted relay failure vocabulary. |
CommandRelayFailureClass |
type alias | type CommandRelayFailureClass |
One of six finite failures. |
ClaimedCommandOutboxRow |
type alias | type ClaimedCommandOutboxRow |
Database-owned raw leased row. |
CommandOutboxAcceptance |
type alias | type CommandOutboxAcceptance |
Checked normalized receipt identity/time. |
CommandOutboxClaim |
type alias | type CommandOutboxClaim |
Bounded clock/generation request. |
CommandOutboxPublication |
type alias | type CommandOutboxPublication |
One publication/acceptance settlement. |
CommandOutboxRelayStore |
interface | interface CommandOutboxRelayStore |
Raw claim/mark/release port. |
CommandOutboxRelease |
type alias | type CommandOutboxRelease |
Retried or retained terminal row. |