Embedding package exports
Workspace registry liveness
@cotal-ai/workspace exports pruneStaleMeshes() for a liveness sweep. It keeps every
registration and returns a MeshSweep containing only offline: string[]; catalog entries
are not probed. The former always-empty pruned field is removed.
Call pruneMesh(space) only after a credentials, mode, or on-disk space mismatch. It removes
an up or pre-origin record and returns whether it was removed. Manual and catalog records
stay registered. The reason argument and PruneReason type are removed. Callers that passed
"gone" must report the target offline without calling pruneMesh; callers that passed
"mismatch" omit the argument.
Node connection signing
Node endpoints and standaloneConnectOpts sign NATS authentication challenges with the existing
native Ed25519 signer. Credentials are read on every connection attempt, including reconnects,
and the temporary signer is cleared after each callback. JWT, public-key and signature fields
use the NATS authenticator contract. Browser connections keep the NATS JavaScript implementation.
User credential authority
AuthProvider.userCredentials returns the bearer and sentinel credential. A provider may also
return authority, containing the owner and the current scope, allowSubscribe,
allowPublish and dmHistory grant that its issuer authorized for that mint. The stock provider
returns this from the exchange response on local and remote user-auth spaces. It does not decode
the bearer to reconstruct access.
The field is optional for provider compatibility. Its absence means the caller has no authoritative grant answer. A status surface must preserve unknown or unavailable instead of inferring broad access from a bearer, successful exchange or broker ping.
Login confirmation
AuthProvider.confirmLogin asks the space's IdP whether this machine's cached login still holds.
It mints nothing. It answers confirmed, signed-out when no login is cached, or rejected when
the IdP refused the cached one, and throws on any other failure. cotal manager start and
restart call it on a user-auth space before they save or launch anything, cotal up before it
starts the manager, and a team boot that resumes a stopped stack before any write. Each refuses
the manager UNSUPPORTED when the provider does not implement it.
Compiled service contracts
serviceContractTable(rows) returns a frozen contracts table. Each command's input/output pair
compiles on first access, is frozen, and is reused on later reads. Listing command names does not
compile validators. Callers cannot replace a command or swap its compiled input/output contracts.
artifactValues() still returns the schema roots and closure manifests used at registration.
Package entry points
Everything below is a real export of a published package, reachable from the package root (each
package publishes . via dist/index.{js,d.ts} and ships files: ["dist"]).
@cotal-ai/connector-claude-code also publishes ./mcp, which hosts managed Claude Code sessions
in one process (Connect Claude).
@cotal-ai/core also publishes ./browser-control and ./browser-validators-build
(Browser control). Type-only names are marked; import them with import type.
Endpoint presence metadata
CotalEndpoint accepts environment as an explicit presence label. Pass null to omit the label
without reading COTAL_ENVIRONMENT. Omitting the option preserves the process environment default.
Shared connector sessions report card.meta.host and card.meta.cwd from the connector process
when the session is constructed. They override agent-file values and are advisory display data.
The directory is a startup snapshot, not a model harness's later shell directory. Neither field
changes routing or access, and the connector reads no command-line arguments or environment dump
for these values. A connector whose process can host several sessions passes
{ withholdProcessCwd: true } as MeshAgent's third argument and reports each session's directory
with setCwd (CotalEndpoint.setCardCwd) once the harness supplies it; until then the field is
absent. The reported path is kept byte for byte, whitespace included; an empty string leaves it unchanged. Claude Code sessions do this from their SessionStart hook.
Daemon APIs
Daemon runners and lifecycle
| symbol | package | purpose |
|---|---|---|
runAuthService(args, store?, options?) | @cotal-ai/auth | boot the auth-service daemon; store injects the secret material, and options takes the daemon lifecycle hooks or interactiveManagerSelection. |
runDelivery(args, store?, onStarted?) | @cotal-ai/delivery | boot the delivery daemon; store injects the scoped delivery cred, and onStarted receives a status()/close() handle once it serves. |
startAuthService(inputs) | @cotal-ai/auth | start one account-scoped auth-service context and return an AuthServiceHandle with the loopback url, the per-start cap, readiness, drain, and idempotent close. close rejects when the context did not release its plane claim: the claim row was no longer its own, or the release write failed. The row then stays held, and the next start reclaims it through the liveness oracle. With the optional publicFace input it also serves the public exchange face and carries publicUrl. The optional calloutLimits input sets the auth callout's deadline, concurrency and slow-line limits (callout deadlines). Every full hosted context serves the capability-gated MANAGED_ACTIVATE_PATH below, including a separate process with no platformControl. With the optional platformControl input the handle also has platformControlAuthority, the in-process platform control door, platformControlReadiness, its read-only readiness read, observeManagerGate, the manager gate read a host composing the delegated user intent decisions passes them, and activateManagedLifecycle, the same native activation used by the loopback door. With platformControl.host it also has registerHostIncarnation, which registers the host process's own endpoint instance and returns the incarnation a delegated execution pins, observeHostGate, the read of that endpoint's issuance gate, and awaitHostFence, which resolves once a later registration or barrier fences an incarnation. runAuthService remains the CLI entry. |
MANAGED_ACTIVATE_PATH, ManagedLifecycleActivationRequest, ManagedLifecycleActivationResult | @cotal-ai/auth | POST /managed-lifecycle/activate on the full auth service's loopback face. A separately hosted account authority accepts only { accountPublicKey, owner, actor, lifecycleUid } with its per-start Bearer <cap>. The account key must equal that context's assigned data account. Success means the exact UID is active and returns the same coordinates plus activated: true. The public face and callout-only replicas do not serve it. |
PlatformControlAuthorityRequest, PlatformControlInnerRequest, PlatformControlAuthorityResult, PlatformControlAssignment (types) | @cotal-ai/core | the closed envelope, its inner request union, its result and the backend's assignment row for platformControlAuthority. platformControlOwner in @cotal-ai/auth derives the p_ owner the door issues under. |
startDeliveryService(inputs) | @cotal-ai/delivery | start one account-scoped delivery instance and return a HostedServiceHandle with readiness, drain, and idempotent close. close rejects when its lease release does not commit or cannot be confirmed, or when another holder's lease row is still there after it. The process runner remains the CLI entry. |
deliveryCredsKey(space, composition), membershipRwCredsKey(space, composition) | @cotal-ai/workspace | build the secret-store keys the delivery cred and the membership feed's rw cred are read/re-signed under. Keys are per-space: space.<hex>/<kind>. A hosted composition passes { injected: true }. |
retireManagerInstanceIdentity(root, space, expected) | @cotal-ai/workspace | remove a persisted manager identity only if its complete instance id and serve identity still match expected. Returns removed or absent; refuses malformed, nonregular, and changed records. absent is not proof of ownership or successful teardown. The caller must separately prove stop and retirement ownership before using it. |
DELIVERY_CREDS_KIND, MEMBERSHIP_RW_CREDS_KIND | @cotal-ai/workspace | the operator-facing KIND names (delivery.creds, membership-rw.creds) those keys are built from, and what renewal results report. A kind is not a key: putting a cred under the bare kind writes the pre-0.4 flat location, which nothing reads. |
Manager APIs
| symbol | package | purpose |
|---|---|---|
Manager, ManagerOptions (type) | @cotal-ai/manager | construct and run a supervisor in-process; ManagerOptions.secretStore injects the one store it reads/writes every secret through. ManagerOptions.remoteAuthority is the hosted manager-service authority bundle, including host-owned release, retained-validation, goal-index, and serve-time admin-authorization callbacks. |
ManagerOptions.pooled | @cotal-ai/manager | require signerless remote authority and either an explicit non-custodial runtime or execution: "none". A pooled composition must supply the assigned account key and all-duty renewal callback; the CLI's default remains unchanged. A signed-in human's manager gets that material from managerServiceAuthority. A platform-run control manager gets it from AuthServiceHandle.platformControlAuthority with the shipped remoteManagerClient builders, as the platform control authority design describes. |
ManagerStatus (type) | @cotal-ai/manager | verified status payload, including optional advisory host and version from the answering Manager. Older payloads may omit them. |
ManagerOptions.execution | @cotal-ai/manager | omitted or "runtime" keeps runtime execution. Explicit "none" accepts a genuine hosting user-auth root, or pooled complete signerless authority, with no runtime option. It constructs no Runtime and performs no connector inspection, seat launch or workflow hosting. |
createRuntime, Runtime (type) | @cotal-ai/manager | resolve the spawn backend (pty built in). |
liveKvEntries(kv, options?) / liveKvEntries(kv, filter, options?), LiveKvEntriesOptions (type) | @cotal-ai/core | read live KV entries in one finite scan. Pass { signal } as the second argument or after a key filter to cancel. An interrupted scan throws IncompleteKvScan; cancellation throws the signal reason, including during an empty-bucket bind. The scan deletes only its owned consumers, including each one nats.js rebuilt from it, after the broker has answered every create those rebuilds sent. A not-found delete counts as gone, a refused one leaves the consumers to broker inactivity expiry (which also covers a crash), and any other delete failure is thrown, as is a not-found delete of a consumer whose create got no reply from the broker before a timeout or a closed connection. A cleanup failure is thrown only when the scan would otherwise return; the scan's own error, its cancellation reason and IncompleteKvScan take precedence. |
remoteManagerServePrincipal(owner, instanceId) from @cotal-ai/core derives the manager's
serve actor and serializes it through principalKey, validating both tokens. Use it when a
host derives the serve principal from an instance id. APIs that already receive a serve actor
serialize that supplied actor with principalKey(owner, serveActor).key.
Pooled renewal authority
The remote manager authority parser accepts renewStandingBundle and renewRunDriver only with
an assigned account nkey, the current manager process epoch, and a host-authenticated registration
proof. A run renewal also names its active holder, takeover, epoch, fencing token, and the two
existing nkeys. The host must fresh-check those coordinates against its registration gate and run
journal before issuing server-selected profiles. A host without that renewal authorization refuses
the request. Until the host issuer wires the operations and validates them on real connections,
the presence of these types is not an operational pooled renewal guarantee.
Execution-free control
An execution-free control Manager retains native registration, goal recovery, session-ledger,
renewal and maintenance duties. Its status reports execution: "none", runtime: "none",
custody: "none", no children or connectors, and false classSpawn, runHosting and
terminalSessions. Local NONE uses this existing status schema without a new wire field.
Execution is recorded per instance. An incompatible record, retained seat/turn inventory or an
older unclassified instance refuses boot before recovery. NONE refuses caller-driven writes,
including persona definition, purge, transcript transfer, preservation and resume, and resolves
no model adapter. It starts no transcript receiver or sweep. Autonomous registration, lease,
renewal, session-ledger and goal maintenance still write their native control state.
Select the genuine participant Manager with a host-issued, instance-pinned manager-caller
view for workflow and terminal operations. That Manager retains the children, workflow journal
and terminal sessions. The control instance neither adopts roster peers nor proxies user runs.
Missing participant selection or authority refuses. This capability adds no application routing.
Local control admission
For a genuine local control instance, pass execution: "none" without pooled, remoteAuthority
or runtime. Startup requires that space's local signing trust, provider pin and user-mode mesh
record. It keeps the native persisted local instance and serve identity; local-primary selection
uses the existing gate. An existing unclassified identity, including a preclaimed identity with no
execution record, is refused. A NONE record cannot restart as runtime. Retained seat or turn
inventory refuses before recovery. Do not remove identity or capability files to change modes.
The public cotal supervise --execution none --space <space> composition supports the same local
mode. It validates arguments before connector seeding, resolves no Runtime, and rejects startup
workload flags and participant registrations. Omitted execution keeps the ordinary PTY behavior.
This does not opt an auth service into local-primary, change scopes or broaden native ps.
Manager execution modes
Runtime remains the default, but local startup now claims its native identity and execution record during admission, before later authentication and registration checks. A failed start can leave those files classified as runtime. Malformed identity or capability files also refuse earlier. These lifecycle timing changes do not change the instance's identity format or authority.
With renewStandingBundle configured, the manager renews all five standing credentials together.
It checks every returned credential for the held nkey and assigned account, test-connects each one,
and adopts them only if the serve epoch has not moved. A refused or failed candidate leaves the
current credentials in place and records the refusal as cleanup debt until a later renewal succeeds.
On shutdown, after the standing context is drained, an expired maintenance executor is renewed
through the existing scoped host operation so deregistration can finish without restarting duties.
Issued-authority references, source coordinates and stores accept nonempty space tokens in
[A-Za-z0-9_-], including underscores. These boundaries reject input that would be rewritten by
the subject-token sanitizer. Endpoint names still use the separate DNS-label grammar.
Provisioning APIs
Provisioning and minting (all @cotal-ai/core)
| symbol | purpose |
|---|---|
createBrokerAuth(label) | mint BROKER trust: the operator and system account one nats-server trusts. One per broker, shared by every space on it. |
createSpaceAccountAuth(broker, space) | mint one space's own data account, signed by that broker's operator: the add-a-tenant primitive. |
createSpaceAuth(space) | the one-space convenience: broker trust + one account in a single composed bundle. |
setupSpaceStreams({ servers, space, creds }) | create the space's JetStream streams. |
ensureDefaultDeliveryClass({ servers, space, creds?, deliveryClass }) | write the space's default delivery class at creation so it is wire-discoverable (SPEC section 4). |
serverConfig(broker, spaces, { storeDir, transport, maxFileStore?, maxPayload?, authTimeout?, extraAccounts?, port?, host?, wsPort?, wsHost? }) | render the broker config: one operator, N space accounts. wsPort enables an optional WebSocket listener; wsHost defaults to loopback. storeDir is required, maxFileStore caps JetStream file storage in bytes (omitted, nats-server's dynamic default applies), maxPayload sets the broker's max_payload in bytes (omitted, nats-server's 1 MiB default), authTimeout renders authorization { timeout } in whole seconds for the auth callout (omitted, no block and nats-server's default), and extraAccounts preloads the auth-callout account. |
mintCreds(auth, identity, profile, opts?) | mint a scoped cred for any Profile. |
mintMembershipObserverCreds, mintConnectionEvictorCreds | mint the membership/eviction scoped creds. |
provisionAgent, provisionAgentDurables | create a principal's bind-only durables. |
newIdentity, stripSpaceAuth | a fresh nkey identity; a stripped signer bundle (data signing seed only). |
Profile, CredentialKind, MintOpts, SpaceAuth (types), CREDENTIAL_LIFETIMES | the profile matrix and cred lifetime policy. |
Authentication APIs
Auth building blocks (all @cotal-ai/auth)
| symbol | purpose |
|---|---|
createCalloutAuth, startAuthCallout | the NATS auth-callout responder. It requires an awaited recordIssuance, which must durably record each success's broker user key before the response; the authority plane's recordIssuance is the stock one. |
createUserTokenIssuer, pinnedJwksResolver | mint and verify the Cotal user bearer. |
createIdpBridge | exchange a verified IdP JWT for a Cotal bearer (see the callout contract). |
deriveOwnerToken, validateUserToken | owner derivation; strict bearer validation. |
cotalAuthProvider | the self-registering auth-provider extension. |
ensureCalloutAuth/loadCalloutAuth, ensureIssuer/loadIssuer, ensureOwnerSecret/loadOwnerSecret | read/write the auth secret kinds through a SecretStore. Each ensure* writes its first value with create, so concurrent first calls on one space return the one value the store holds. |
PLANE_CLAIM_REFUSED, planeClaimRefusal, PlaneClaimRefusal (type) | every plane-claim refusal carries a PLANE_CLAIM_REFUSED detail, and planeClaimRefusal(err) reads its reason: corrupt, live-peer, unknown, concurrent, fenced, released or lost. Only unknown, an inconclusive liveness observation, is coded unavailable. A host can retry contention and stop on a corrupt row without matching message text. |
startAuthCallout processes up to 32 connections concurrently. Its hooks must support overlapping
calls for different connections. A slow authority check or recorder occupies one slot; other
connections can still authenticate. Each response waits for that connection's authority checks,
durable issuance and final expiry check. Work beyond the bound waits and can still reach the
broker's authorization deadline. The returned done waits for admitted hooks when the subscription
ends and rejects an unexpected worker failure after those hooks settle.
Composition APIs
Seams and the wire (all @cotal-ai/core unless noted)
| symbol | purpose |
|---|---|
SecretStore (type) | the durable hosted-secret seam (get/put/create/delete). create writes only while the key is absent and reports whether it did, so it must be one atomic step in the backend. get() returns raw seeds/keys into process memory, so it is a blob seam, not HSM/KMS signing. |
FsSecretStore, workspaceSecretStore(root) | the filesystem default. These live in @cotal-ai/workspace, not core. |
AuthProvider (type), Connector (type), Runtime (type), Command (type) | the extension contracts; implementations self-register on import. |
registry | the shared registry a composition root pulls surfaces into. |
CotalEndpoint, subjects, message types | the wire client and shapes. |
ParsedArgs (type) | the shape the daemon runners take (see below). |
For a Linux Unix-socket adapter, peerCredentials(socket) from @cotal-ai/seat returns
kernel-observed peer pid, uid and gid. Compare these against the host's authorization
policy; request-supplied identity and process liveness do not replace that policy or a
lifecycle fence. The helper starts no custodian and refuses unsupported platforms or a
missing native helper.
The runners take a CLI-shaped ParsedArgs, not a typed options object, so a host fabricates one:
const args: ParsedArgs = { values: { space, server, port: "0" }, positionals: [], raw: [] };Long-lived endpoint credentials
EndpointOptions.bearer accepts either a string or a function, and the difference is not stylistic.
A string is minted once, so when it expires (which it will: callout bearers live minutes) the
endpoint has nothing to renew with. It will not present the dead token to the broker, since that is
a guaranteed denial that still costs a full auth-callout round trip. It refuses to reconnect, emits
warning saying which case it is in, and retries on a widening backoff until the process
re-authenticates and rebuilds it. Retry notices use warning rather than error because Node
rethrows an unhandled error event and would kill a host the endpoint is still trying to recover.
When the broker refuses a bearer at connect, the warning says the broker refused this endpoint's
bearer and that the auth service logs why, because nats-server passes the client no reason. The
endpoint then retries on its capped backoff. Each wait is a random point in the upper half of its
step, so endpoints refused together, after a broker restart or by an auth limit, do not retry
together. A bearer source that throws an error with a numeric retryAfterSeconds is called again
when those seconds have passed, not before, and a reconnect inside that wait dials on the bearer it
holds. The auth provider's rate-limited exchange throws that way, and agentBearerCommand prints
retry-after <seconds> for it.
Pass a function for anything that outlives one bearer. That is a renewal source: it is called
ahead of each expiry and again whenever a reconnect finds the cached bearer dead, and it requires
explicit card.owner and card.actor. The first-party surfaces already do this
(UserViewAuth.source, the connector's agentBearerCommand). A string bearer is for a short
one-shot connection.
A MeshAgent a host runs in its own process takes the same kind of source. Pass
userAuth.bearer, a function returning a fresh bearer, instead of userAuth.bearerCmd, the argv a
launched seat execs. Such an agent has no manager-call bearer, so its spawn, despawn, persona and
run calls return a refusal without reaching a manager.
Endpoint event handling
Long-lived hosts must also subscribe to the endpoint's warning event. It carries conditions the
endpoint is surviving, including failed credential renewal, reconnect retries, its own DM inbox
missing from the broker while the connection is up, a durable leave
it keeps retrying after the broker refuses a durable channel's live subscription, and a channel
registry entry that does not decode to a valid config, such as a raw put of an unknown delivery
class or of null on the defaults key. The endpoint drops that entry from its cache and applies the
default policy until a valid entry is written. The DM inbox warning comes once, after the broker
has reported the inbox missing twice. The endpoint does not recreate the inbox. It keeps checking
and receives again if its provisioner creates the inbox again. A host may choose
to ignore warnings for a one-shot endpoint whose awaited operation owns the verdict, but that choice
should be explicit. An unhandled warning is nonfatal and silent.
The error event carries a fault the endpoint cannot return from a call, such as a refused
subscription or a refused publish that no request was waiting on. Attach a listener before start(),
since Node throws on an unhandled error. A denial the broker returns to a request, such as an
observer's read of the DM stream, reaches only that call, which decides what it means, and is not
emitted again as an error.
Native transport liveness
CotalEndpoint accepts transportPingIntervalMs and transportMaxPingOut together
as positive safe integers. Omission uses a 10-second interval and two outstanding pings. Connector seats and CLI endpoints use these defaults, including loopback proxy connections. A silent link normally disconnects within about 30 seconds, subject to scheduling. The delivery daemon retains its 2.5-second override. Native PING/PONG uses the
resident socket without another authenticated probe connection. A stale connection
emits a transport disconnect before reconnect attempts. The
delivery daemon's connection handling explains how
that event affects the service. The option validation and NATS mapping are in
endpoint.ts.
transportConnectTimeoutMs is a positive integer bounding the first connection attempt. Omission keeps the transport's 20-second default. Transport liveness does not prove endpoint readiness, presence health, or a send outcome.
Finite KV scans
liveKvEntries(kv, options?) and liveKvEntries(kv, filter, options?) read one finite scan of live entries.
A { signal } argument can appear second or after a key filter. The filter may be a string, an array
of strings, or undefined. liveKvValues(kv, decode, options?) and
liveKvValues(kv, decode, filter, options?) provide the same forms with decoded results.
Passing options in both slots is a TypeScript error and rejects with TypeError at runtime before
scanning. Combine the signal and callbacks into one options object.
An interrupted scan throws IncompleteKvScan.
Cancellation throws the signal's reason, including during a bind to an empty bucket.
The ordered consumer's pending count distinguishes an empty completed scan from an interrupted read.
Completion requires a zero pending count at bind or a delivered message reporting zero pending.
Successful empty and nonempty results await cleanup before returning.
Cancellation still wins while cleanup is pending, before a result returns.
Cleanup deletes only the scan's owned consumers, including consumers nats.js rebuilt from them. It waits for broker replies to every create sent by those rebuilds. A confirmed not-found delete counts as gone. A refused delete leaves the consumer to broker inactivity expiry, which also covers a crash. Other delete failures are thrown. A not-found delete also fails when the broker never answered that consumer's create before a timeout or closed connection.
A cleanup error replaces only an otherwise successful result.
The scan's own error, cancellation reason, or IncompleteKvScan takes precedence.
Related contracts
Runtime execution
Manager execution mode determines whether a host can launch agents and workflows. See the detailed contract.