@cotal-ai/pi 0.95.2 → 0.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65196,10 +65196,10 @@ function date4(params) {
65196
65196
  config(en_default());
65197
65197
 
65198
65198
  // ../connector-core/dist/docs-bundle.generated.js
65199
- var DOCS_VERSION = "0.95.2";
65199
+ var DOCS_VERSION = "0.96.0";
65200
65200
  function loadDocsBundle() {
65201
65201
  return {
65202
- "version": "0.95.2",
65202
+ "version": "0.96.0",
65203
65203
  "generatedFrom": "docs/*.md + SPEC.md + spec/cotal-lang.md + spec/cotal.schema.json",
65204
65204
  "pages": [
65205
65205
  {
@@ -65361,7 +65361,7 @@ function loadDocsBundle() {
65361
65361
  "title": "Embedding Cotal",
65362
65362
  "kind": "Guide (informative)",
65363
65363
  "summary": "The cotal binary in this repo is one composition root: an operator CLI.",
65364
- "body": "# Embedding Cotal\n\n> **Guide** (informative) \xB7 **For:** implementers building a service on top of Cotal \xB7 **Prereqs:** [Architecture](architecture.md), [Identity and auth](identity-and-auth.md), [Delivery daemon](delivery-daemon.md)\n\nThe `cotal` binary in this repo is one composition root: an operator CLI. A separate service\n(for example a hosted, multi-tenant Cotal) does not fork this repo. It writes its **own**\ncomposition root that depends on the published `@cotal-ai/*` packages and imports the surfaces it\nwants. `bin/cotal.ts` uses the same composition pattern. This page is the contract for that: what is a real library\nexport you can build against, how to boot the server-side daemons from those exports, and where the\ncurrent export surface stops short of a fully hosted composition.\n\nThis is the \"guarded substrate\" boundary in practice. Nothing here reveals or assumes a specific\nhost; it documents the public seams any embedder composes.\n\n## What you embed\n\nThe supported reference shape here is **one broker operator serving one space** (one tenant: a\ndedicated data account, under an operator that also holds the system account and a quarantined\nauth-callout account) plus three standalone processes. The trust layer itself composes many spaces\nunder one broker operator today (`createBrokerAuth` + `createSpaceAccountAuth` + N-space\n`serverConfig`); what does not exist yet is the per-space **lifecycle** on a shared broker (see\n[Known gaps](#hosted-composition-gaps)). The three processes:\n\n| daemon | package | what it is |\n|---|---|---|\n| auth-service | `@cotal-ai/auth` | the NATS auth callout, the IdP token exchange, and JWKS. Plane 1 to Plane 2. |\n| delivery | `@cotal-ai/delivery` | the Plane-3 durable backstop: fan-out writer plus trusted reader, per space. |\n| supervise | `@cotal-ai/manager` | the per-machine agent lifecycle (spawn/despawn/attach), per space. |\n\n`mint`, `deliver`, and `auth-service` expose their behavior as direct library primitives, and the\nsupported one-space bootstrap below re-composes from exported low-level primitives. `supervise` and\nthe full `up` orchestration are **not** public runners: `up` also does broker bring-up, restore,\nprocess and registry management, and lifecycle work, and `supervise`'s orchestration is private (see\n[Supervisor signing authority](#supervisor-signing-authority)).\n\n## Interactive Manager primary\n\nA trusted composition can pass `{ interactiveManagerSelection: \"local-primary\" }` as the third\nargument of `runAuthService(args, store, options)`. `AuthServiceRuntimeOptions` is exported by\n`@cotal-ai/auth`. The third argument still accepts the native daemon lifecycle hooks. No CLI\nflag or exchange field sets the policy, and unsupported runtime values refuse before startup effects. `openAuthAuthorityPlane` accepts the same option beside its fresh\n`localManager` reader; `OpenAuthAuthorityPlaneOptions` is exported too.\n\nThe opt-in chooses the genuine local Manager up front for an unpinned human `manager-caller`\nexchange. It reads the persisted local identity and requires the existing open gate, serve\nprincipal, registration revision and status epoch checks. An absent, unreadable or noncandidate\nlocal Manager refuses without scanning or substituting remote Managers. Explicit selectors win,\nand managed-secret exchanges and launching or issued instance bindings keep their existing rules.\nThe option adds no scopes, registry reads or inventory rows.\n\nThis routes all unpinned interactive Manager control, not only `ps`: slot reads, stop, attach,\ninput, spawn, models, hosted run verbs and generic Manager describe/invoke share the view. It\nchooses local even when the person has a live remote Manager. A host must decide whether that\nrouting is appropriate; a local Manager without execution cannot forward a spawn or run to the\nperson's remote Manager. The person selects that Manager explicitly for those operations.\n\nAbsent the option, selection is unchanged. `startAuthService` still has no local Manager and\nkeeps its remote-only composition. Supplying this programmatic option does not enable it in stock\n`up`, restart or recovery paths; a host must compose those paths separately after adopting the\npublished API. `ps` still lists only the selected Manager's managed seats, not HTTP-hosted sessions.\n\n## Account connection census\n\n`mintAccountCensusCreds` creates a host-only SYS user with at most five minutes of authority.\nIt can request CONNZ for one DATA account and subscribe only to an account-qualified reply prefix.\nIt grants neither KICK nor account events. The host supplies a registered SYS signer; the helper\ndoes not find, persist or renew that signer, and the broker verifies its authority on connection.\n`observeAccountCensusWithCreds` requires this narrow scope and a pinned server key. It reads one\nbounded page. Malformed, incomplete, multiple-server or cluster-declared replies are unknown,\nwith no connection list. A complete result describes only that server's observed account page.\nThe host still needs independent topology and restore guarantees, deny-new, a fixed cutoff and\nlifecycle fencing before using an observation for retirement. These helpers do not grant service\nregistration or terminal retirement.\n\n## Service authority contracts\n\n`@cotal-ai/core` exports closed service-authority request, proof and result types and their\nruntime parsers. The parsers bind a result to the submitted request and registration witness.\n`AuthProvider.serviceEndpointAuthority` is an optional provider hook for this protocol. A provider\nwithout the hook must refuse service setup. The user-auth provider implements the HTTP client,\nincluding `serviceEndpointInfo` for the DATA-account binding. It uses the pinned exchange origin,\nrefuses redirects and does not retry an unknown mutation outcome. Server-side HTTP failures\nremain unconfirmed even when their bodies resemble a protocol denial. A registered snapshot must\nname the submitted closure set. Inline artifacts include each closure manifest as well as its\ndocuments, within the envelope byte limit; native per-closure walk limits still apply. The client does not transmit\nthe local host capability or read callout signing material. Local setup needs the client sentinel\nprojection written by `prepareServer`; a missing projection refuses rather than reading server secrets.\n\n`@cotal-ai/auth` exports host request and view types with `parseServiceHostRequest`. Parsing checks\nstructure only, not owner consent, current grants or admission state. The host handlers and complete native\nservice lifecycle are not implemented yet, so these interfaces do not enable service registration.\nThe native endpoint gate also carries service approval and activation fields. Registration and\nserving guards retain those fields and refuse expired or revoked approval; these guards are not\na replacement for the missing host lifecycle. Internal host gate transitions bind approval to a\ncurrent native root and make activation monotonic. Registration completion is stamped in the\nreopen CAS; a definite abort preserves the previous stamp. They do not provide consent, public handlers\nor terminal retirement. Pre-activation registration requires an empty issuance family, including\ndeleted records, and release rechecks fixed expiry after asynchronous work.\n\n`UserTokenIssuer.issueServiceRenewal` signs a separate registration-bound renewal proof.\n`validateServiceRenewalProof` checks its signature, fixed expiry and full witness binding.\nIt is not a connect bearer. These primitives do not authenticate a current approval or native root;\nthe host must do that before issuing or accepting authority. No client refresh loop is provided yet.\n\n`UserTokenIssuer.issueServiceAccess` signs a serve-only user bearer with the full registration\nwitness and a fixed deadline of at most 60 seconds. Validation requires empty scope and rejects\nordinary parent, manager, session and transfer claims. Ordinary exchange and permission construction\nrefuse this view before actor lookup. Callout\nrefuses it unless a sealed native service authority is configured. The private composition binds\nthe current assignment, native root, registered serving grant and receipt before an encrypted\nresponse. Normal authority-plane startup does not enable it yet; public registration, restore\nreadiness and coordinated root retirement remain required.\n\nInternal service-access reads retain native credential-root authorization and check the activated\nregistration, assignment, current name and fixed expiry. They do not create a serving grant or\nrelease a native JWT. The private callout composition performs native issuance and final\nauthorization when explicitly configured.\n\nService serving-issuance evidence binds the authenticated broker user key, server key and client id.\nThe native release fence rechecks that record and the gate after its CAS. Ordinary serving and\ncredential-root records cannot use this service binding. This is not proof of broker acceptance;\nproduction authority-plane wiring and terminal retirement remain unfinished.\n\n`mintServiceCalloutJwt` is a host-only mint seam. It uses the native serving grant, the actual\nprincipal name and tags, the authenticated broker user key and a separate client inbox nonce.\nExpiry stays bounded by the original admission and access deadline. It does not authenticate the\ncallout request, record natkey evidence or release an encrypted response; the host must do those.\n\nNative service receipts bind the same epcred connection and registration witness to the broker user\nkey. Recording rechecks the evidence, receipt, native authority and fixed expiry. Expired service\nreceipts are retained, not pruned on elapsed JWT time. Qualified retirement and production\nauthority-plane wiring are still required before enabling the service path for clients.\n\nInternal service-name bindings are create-only and return owner and revision from one native read.\nThe trusted host must authenticate the operator and verify the namespace claim before writing.\nNative auth, delivery and manager names keep their existing authority; they cannot be application\nservice bindings. No transfer, release or public name-binding handler is implemented yet.\n\nInternal caller-policy storage uses the sealed native registry and binds each row to one account,\nspace and grantee UID. Grant ids are immutable; an append cannot replace or reactivate one.\nInitial writes require no prior native key history. A deletion arriving after an absence read\ncannot recreate a policy or a pending service gate.\nRevocation begins with a revision-pinned deny-new intent that unrelated appends preserve.\nThese are storage operations, not entitlement checks; the service endpoint host below is their only\nwriter. The effective resolver and native caller issuance are described under the service endpoint\nhost below. Receipts and terminal caller revocation remain unsupported.\n\nScoped Describe callbacks also receive an optional immutable local context with the actual subject,\ncomplete-envelope fingerprint, serving identity and receive-relative deadline information.\n`parseDescribeObservation` shares native syntax and full-JSON fingerprint checks with trusted observers.\nNeither the context nor a fingerprint proves broker receipt to a remote authority. The remote\nobserver and policy projection are not implemented yet. The client contract admits an explicit\nread-only `describe-view` operation using only service-access proof. It checks witness, observation,\nprincipal-policy basis and remaining validity, requires no-store responses, and bounds preparation,\nHTTP streaming and validation by the same local deadline. Cancellation cannot restart that deadline.\nThe native handler and serving adapter are still required before this can authorize a remote view.\n\n`parseEndpointCapabilities` normalizes the native caller capability shape. `endpointCapabilitiesWithin`\nchecks route, instance, target and journal containment using the native grant builders. The host\nstill authenticates the granter and checks current grantee, parent and approval records.\nCross-principal relative-target comparisons refuse when containment is unproved.\n\n`authorizeServeGrant` accepts a read-only `ServiceSpecReader`. A trusted provider can supply\nverified registration and contract reads without constructing a writable KV adapter. The function\nstill derives the complete serving surface and creates the process-local grant.\n\n## Owner-bound platform supervisors\n\nA host can start one user's supervisor without retaining that user's login. Pass\n`platformSupervisor: { authorizePlatformAdmin, observeAssignment }` to `startAuthService`, then call\n`handle.platformSupervisorAuthority(owner)` inside the trusted authority process. The first callback\nmust authenticate the platform's administrative caller through the host's own authority. The second\nreads that owner's recorded `PlatformSupervisorAssignment` fresh on every call. An absent,\nended, expired, foreign-account or wrong-revision assignment refuses issuance.\n\nTo end a supervisor, record its assignment `ended` and call\n`handle.endPlatformSupervisorAssignment(owner)`. It rechecks platform-admin authority and refuses\nunless your record already reads `ended`. It retires the manager gate and the issued supervisor and\nexecutor generations. After that, registration, activation and renewal refuse material returned\nearlier, even before it expires, with `platform supervisor assignment for owner <owner> has ended`\nor the retired-gate refusal. Stop the worker as well: an open broker connection lives until its\ncredential's capped expiry.\n\nThe assignment has `kind: \"platform-supervisor\"`, `scope: [\"supervise\"]`, one derived owner,\none manager instance, one lifecycle UID, a revision and a finite `expiresAt` in Unix seconds. It is\nnot an interactive or managed-agent ledger row and grants no `admin`. The returned door is bound to\nthat owner and accepts `PlatformSupervisorAuthorityRequest` with the same owner, assignment revision\nand an existing `RemoteManagerAuthorityRequest`. It serves only `prepare`, `activate`, `renew` and\n`renewStandingBundle`. Unknown fields, generic profiles, mint, human exchange and another owner\nare refused before issuance. Do not give the worker the auth handle, loopback capability or signer.\nThe host carries only the bound door over its authenticated worker channel.\n\nUse `remoteManagerClient` and `registerRemoteManagerAuthority` from `@cotal-ai/manager` for the\nexisting registration ceremony. The result keeps the stock manager material shape and five nkeys.\nAll returned credentials expire no later than the assignment. The supervisor and executor ceilings\nare recorded in the native issued store. The recorded permission set must be byte-identical to the\nauthority plane's signed native permission set at issuance and every renewal, and must never widen.\nThese JWTs are signed by the authority plane, not returned by the user-token callout. The door\nadds no human bearer, callout view, owner ledger grant or enrollment authority. Run admission,\nhost-backed enrollment and shutdown maintenance need their own host composition.\n\nSee [the issuance design](design/user-auth-run-start.md#platform-supervisor-registration).\n\n## The export surface\n\nEverything below is a real export of a published package, reachable from the package root (each\npackage publishes `.` via `dist/index.{js,d.ts}` and ships `files: [\"dist\"]`).\n`@cotal-ai/connector-claude-code` also publishes `./mcp`, which hosts managed Claude Code sessions\nin one process ([Connect Claude](connect-claude.md#host-sessions-in-one-process)).\n`@cotal-ai/core` also publishes `./browser-control` and `./browser-validators-build`\n([Browser control](#browser-control)). Type-only names are marked; import them with `import type`.\n\n`CotalEndpoint` accepts `environment` as an explicit presence label. Pass `null` to omit the label\nwithout reading `COTAL_ENVIRONMENT`. Omitting the option preserves the process environment default.\n\nShared connector sessions report `card.meta.host` and `card.meta.cwd` from the connector process\nwhen the session is constructed. They override agent-file values and are advisory display data.\nThe directory is a startup snapshot, not a model harness's later shell directory. Neither field\nchanges routing or access, and the connector reads no command-line arguments or environment dump\nfor these values. A connector whose process can host several sessions passes\n`{ withholdProcessCwd: true }` as `MeshAgent`'s third argument and reports each session's directory\nwith `setCwd` (`CotalEndpoint.setCardCwd`) once the harness supplies it; until then the field is\nabsent. 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.\n\n**Daemon runners and lifecycle**\n\n| symbol | package | purpose |\n|---|---|---|\n| `runAuthService(args, store?)` | `@cotal-ai/auth` | boot the auth-service daemon; `store` injects the secret material. |\n| `runDelivery(args, store?)` | `@cotal-ai/delivery` | boot the delivery daemon; `store` injects the scoped `delivery` cred. |\n| `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`. 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 activation that host runs at a delegated launch's pinned lifecycle UID. 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. |\n| `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. |\n| `startDeliveryService(inputs)` | `@cotal-ai/delivery` | start one account-scoped delivery instance and return a `HostedServiceHandle` with `readiness`, `drain`, and idempotent `close`. `close` rejects when the lease row is still held after it, by another holder or because the release did not commit, or when the release cannot be confirmed. The process runner remains the CLI entry. |\n| `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 }`. |\n| `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. |\n| `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. |\n| `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. |\n| `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/platform-pooled-control-authority.md) design describes. |\n| `ManagerStatus` *(type)* | `@cotal-ai/manager` | verified status payload, including optional advisory `host` and `version` from the answering Manager. Older payloads may omit them. |\n| `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. |\n| `createRuntime`, `Runtime` *(type)* | `@cotal-ai/manager` | resolve the spawn backend (pty built in). |\n| `liveKvEntries(kv, filterOrOptions?, 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. |\n\nThe remote manager authority parser accepts `renewStandingBundle` and `renewRunDriver` only with\nan assigned account nkey, the current manager process epoch, and a host-authenticated registration\nproof. A run renewal also names its active holder, takeover, epoch, fencing token, and the two\nexisting nkeys. The host must fresh-check those coordinates against its registration gate and run\njournal before issuing server-selected profiles. A host without that renewal authorization refuses\nthe request. Until the host issuer wires the operations and validates them on real connections,\nthe presence of these types is not an operational pooled renewal guarantee.\n\nAn execution-free control Manager retains native registration, goal recovery, session-ledger,\nrenewal and maintenance duties. Its status reports `execution: \"none\"`, `runtime: \"none\"`,\n`custody: \"none\"`, no children or connectors, and false `classSpawn`, `runHosting` and\n`terminalSessions`. Local NONE uses this existing status schema without a new wire field.\nExecution is recorded per instance. An incompatible record, retained seat/turn inventory or an\nolder unclassified instance refuses boot before recovery. NONE refuses caller-driven writes,\nincluding persona definition, purge, transcript transfer, preservation and resume, and resolves\nno model adapter. It starts no transcript receiver or sweep. Autonomous registration, lease,\nrenewal, session-ledger and goal maintenance still write their native control state.\n\nSelect the genuine participant Manager with a host-issued, instance-pinned `manager-caller`\nview for workflow and terminal operations. That Manager retains the children, workflow journal\nand terminal sessions. The control instance neither adopts roster peers nor proxies user runs.\nMissing participant selection or authority refuses. This capability adds no application routing.\n\nFor a genuine local control instance, pass `execution: \"none\"` without `pooled`, `remoteAuthority`\nor `runtime`. Startup requires that space's local signing trust, provider pin and user-mode mesh\nrecord. It keeps the native persisted local instance and serve identity; local-primary selection\nuses the existing gate. An existing unclassified identity, including a preclaimed identity with no\nexecution record, is refused. A NONE record cannot restart as runtime. Retained seat or turn\ninventory refuses before recovery. Do not remove identity or capability files to change modes.\n\nThe public `cotal supervise --execution none --space <space>` composition supports the same local\nmode. It validates arguments before connector seeding, resolves no Runtime, and rejects startup\nworkload flags and participant registrations. Omitted execution keeps the ordinary PTY behavior.\nThis does not opt an auth service into local-primary, change scopes or broaden native ps.\n\nRuntime remains the default, but local startup now claims its native identity and execution\nrecord during admission, before later authentication and registration checks. A failed start\ncan leave those files classified as runtime. Malformed identity or capability files also refuse\nearlier. These lifecycle timing changes do not change the instance's identity format or authority.\n\nWith `renewStandingBundle` configured, the manager renews all five standing credentials together.\nIt checks every returned credential for the held nkey and assigned account, test-connects each one,\nand adopts them only if the serve epoch has not moved. A refused or failed candidate leaves the\ncurrent credentials in place and records the refusal as cleanup debt until a later renewal succeeds.\nOn shutdown, after the standing context is drained, an expired maintenance executor is renewed\nthrough the existing scoped host operation so deregistration can finish without restarting duties.\n\nIssued-authority references, source coordinates and stores accept nonempty space tokens in\n`[A-Za-z0-9_-]`, including underscores. These boundaries reject input that would be rewritten by\nthe subject-token sanitizer. Endpoint names still use the separate DNS-label grammar.\n\n**Provisioning and minting** (all `@cotal-ai/core`)\n\n| symbol | purpose |\n|---|---|\n| `createBrokerAuth(label)` | mint BROKER trust: the operator and system account one nats-server trusts. One per broker, shared by every space on it. |\n| `createSpaceAccountAuth(broker, space)` | mint one space's own data account, signed by that broker's operator: the add-a-tenant primitive. |\n| `createSpaceAuth(space)` | the one-space convenience: broker trust + one account in a single composed bundle. |\n| `setupSpaceStreams({ servers, space, creds })` | create the space's JetStream streams. |\n| `ensureDefaultDeliveryClass({ servers, space, creds?, deliveryClass })` | write the space's default delivery class at creation so it is wire-discoverable (SPEC section 4). |\n| `serverConfig(broker, spaces, { storeDir, maxFileStore?, extraAccounts?, port?, host? })` | render the broker config: one operator, N space accounts. `storeDir` is required, `maxFileStore` caps JetStream file storage in bytes (omitted, nats-server's dynamic default applies), and `extraAccounts` preloads the auth-callout account. |\n| `mintCreds(auth, identity, profile, opts?)` | mint a scoped cred for any `Profile`. |\n| `mintMembershipObserverCreds`, `mintConnectionEvictorCreds` | mint the membership/eviction scoped creds. |\n| `provisionAgent`, `provisionAgentDurables` | create a principal's bind-only durables. |\n| `newIdentity`, `stripSpaceAuth` | a fresh nkey identity; a stripped signer bundle (data signing seed only). |\n| `Profile`, `CredentialKind`, `MintOpts`, `SpaceAuth` *(types)*, `CREDENTIAL_LIFETIMES` | the profile matrix and cred lifetime policy. |\n\n**Auth building blocks** (all `@cotal-ai/auth`)\n\n| symbol | purpose |\n|---|---|\n| `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. |\n| `createUserTokenIssuer`, `pinnedJwksResolver` | mint and verify the Cotal user bearer. |\n| `createIdpBridge` | exchange a verified IdP JWT for a Cotal bearer (see [the callout contract](identity-and-auth.md#the-idp-callout-contract)). |\n| `deriveOwnerToken`, `validateUserToken` | owner derivation; strict bearer validation. |\n| `cotalAuthProvider` | the self-registering `auth-provider` extension. |\n| `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. |\n| `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. |\n\n**Seams and the wire** (all `@cotal-ai/core` unless noted)\n\n| symbol | purpose |\n|---|---|\n| `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. |\n| `FsSecretStore`, `workspaceSecretStore(root)` | the filesystem default. **These live in `@cotal-ai/workspace`, not core.** |\n| `AuthProvider` *(type)*, `Connector` *(type)*, `Runtime` *(type)*, `Command` *(type)* | the extension contracts; implementations self-register on import. |\n| `registry` | the shared registry a composition root pulls surfaces into. |\n| `CotalEndpoint`, subjects, message types | the wire client and shapes. |\n| `ParsedArgs` *(type)* | the shape the daemon runners take (see below). |\n\nFor a Linux Unix-socket adapter, `peerCredentials(socket)` from `@cotal-ai/seat` returns\nkernel-observed peer `pid`, `uid` and `gid`. Compare these against the host's authorization\npolicy; request-supplied identity and process liveness do not replace that policy or a\nlifecycle fence. The helper starts no custodian and refuses unsupported platforms or a\nmissing native helper.\n\nThe runners take a CLI-shaped `ParsedArgs`, not a typed options object, so a host fabricates one:\n\n```ts\nconst args: ParsedArgs = { values: { space, server, port: \"0\" }, positionals: [], raw: [] };\n```\n\nFor an embedded delivery instance, use `startDeliveryService` instead. Its `HostedContextInputs`\ninclude the account public key and lifecycle UID, space, broker URL, injected store, stable\n`storeIdentity`, and an explicit `stateDir`. The store must declare that same injected identity.\nThe initial delivery credential must belong to the assigned account. The function returns only\nafter the delivery responder is bound. `close()` withdraws serving and releases only the lease\nowned by that instance. It closes both membership connections even when a disconnected drain\nfails, so they cannot reconnect after closure. It rejects when the lease row is still there\nafterwards, held by another instance or left by a release that did not commit, or when the broker\ncannot confirm the release. If the delivery connection drops or is down while the release is in\nflight, `close()` waits up to the broker-gone window (`COTAL_DELIVERY_BROKER_GONE_MS`, 15 seconds\nby default) from the drop for it to reconnect, and repeats the release once on the reconnected\nconnection before it decides. `drain()` returns the same promise. A successor is refused while such\na row lives, and the bucket TTL expires a row the instance left. Credential-expiry health state clears after\nsuccessful broker-verified adoption through the existing `reloadCreds` rail. A failed start\nrefuses locally without exiting the host process or stopping another account's delivery service.\nIf a health fault occurs during an asynchronous store read, startup rejects when the read returns\nand closes any resources created by that late completion.\n\n`startAuthService` takes the same `HostedContextInputs`. The store must declare the assigned\ninjected identity, and its data account must be the assigned account. The IdP pin and ledger live\nunder the explicit `stateDir`. The auth plane's instance identity holds its private serve seed, so it\nlives in the store under `authInstanceKey(space)`. A record an earlier release kept under `stateDir`\nmoves into the store on the first start.\nThe context never resolves a workspace root from the working directory and has no local manager, so\nonly remote manager gates can be selected. It returns after\nthe authority plane, the callout subscription and the loopback listener are bound. A start that\nfails closes the connections it opened and releases its plane claim, so a retry on the same space\ncan claim it. A fenced plane or\na lost broker connection makes that context `unavailable` and closes it without exiting the process.\nThe host writes no discovery file for it. The handle carries what `auth-service.json` holds for a\nCLI start: the loopback `url`, `publicUrl` when a public face runs, and the per-start `cap`. The cap\nalone authorizes the loopback host actions, lifecycle retirement and managed-agent enrollment\nverification, so keep it in the authority process. `publicFace` takes the CLI's public face inputs\n(`port`, `url`, `trustedProxy`, `advertisedServer`, `agentProvisioningUrl`) under the same rules,\nand a face without a port refuses to start.\n\nTwo optional inputs serve a platform composition. `platformControl: { observeAssignment }` adds\n`platformControlAuthority` to the handle. It is a typed in-process method, served on no listener,\nthat issues the manager-service request family for the one control manager the backend assigned to\nthis account, under a derived `p_` owner. It reads the assignment fresh on every call and refuses\nan IdP token, another account, a stale revision, another instance or lifecycle, and an instance\nanother owner registered. It refuses `prepare` and `activate` while the assignment's named\npredecessor is still registered or frozen. The same input adds `platformControlReadiness(instanceId)`,\nthe route for a host that needs to know whether its assigned control manager is serving. It returns\nthat instance's attributed `status` reply and refuses any instance the current assignment does not\nname, or one whose gate another owner holds. It reads over the context's own connection, whose\ngrant is the assigned instance's `describe` and `status` and its own reply rail. That connection\nrenews in process like the context's other connections and never leaves it, so the host lends no\nhuman or operator credential to a worker, mints no control instrument per read, and does not read\nliveness off the manager process. Without the input both members are `undefined`.\n`standingRenewableTtlSeconds` is forwarded unchanged to the authority plane, which bounds it to 5\nto 86400 seconds. It is a trusted-host input for the renewal rehearsal, and no request or CLI flag\nsets it. SPEC \xA713.1 and \xA713.6 define the view.\n\nThe auth plane can renew a registered manager's\nfive standing credentials from the current service registration. Run-driver renewal still\nrefuses without an authoritative activated-run reader, so these handles do not yet make a\ncomplete pooled auth and delivery host. A fresh auth plane can initialize without a\ndelivery-admin responder. Reclaiming a held claim from a dead predecessor needs the delivery\ninstance first: its admin rail must complete the broker connection-liveness sweep before the\nauth plane takes the claim. An absent or inconclusive oracle refuses the reclaim.\n\n### Remote MCP gateway\n\n`@cotal-ai/connector-core` exports `registerCotalGatewayTools(server, host, source?, options?)` for\na service that serves Cotal to a hosted MCP client over HTTP. A hosted client can send many\nconversations through one bearer and one MCP session, so a request does not say which conversation\nsent it. Each conversation calls `cotal_connect` and passes the `connection_id` it returns on every\nother tool call. A missing, unknown or other-account handle returns `CONNECTION_REQUIRED` and runs\nnothing. No call falls back to another connection of the account.\n\nThe host implements `GatewayHost`:\n\n- `account(auth)` returns the account the request authenticated as, read from the SDK's `AuthInfo`.\n It throws when the request has none, and the tool then runs nothing.\n- `open(account, name, context?, selection?, approvalId?)` starts a connection only after the person\n approves its permissions. `selection` is a proposal. Throw `ApprovalRequired` with the request ID,\n approval page, expiry and proposal while they decide. The tool returns a structured pending result\n and a readable link, with no connection handle. `approvalId` resumes one unchanged request when\n `options.approvalResume` is enabled. It conveys no authority. Bind it to the authenticated account,\n space, name and request generation. An issued approval cannot grant a replacement connection.\n Throw `NameTaken` only for an authoritative name collision. A new request tries `name_2` and later\n variants up to `GATEWAY_NAME_ATTEMPTS`; a resumed approval never renames. The collision check and\n issue must be atomic. Other errors reach the caller without renaming and must not carry secrets.\n- `find(account, id)` and `close(account, id)` act only on a connection that account owns.\n\n`cotal_connect` refuses a name `assertValidName` rejects before it calls `open`. Its reply names\nthe channels the new connection reads, may join and may post to, from the configuration its grant\nproduced. `GATEWAY_TOOLS` names the mesh tools the gateway offers, `cotal_docs` included. A native\nembedder whose host issues each conversation its own grant may pass `options.tools` from\n`NATIVE_GATEWAY_TOOLS`, which adds spawn, despawn, persona, run and reconnect but not yield.\nRegistering a tool offers it to no connection by itself: a connection calls only the tools its\nhost lists in `GatewayConnection.tools`, default `GATEWAY_TOOLS`, and only those its credential's\ncapabilities allow. With `options.selection`, `cotal_connect` takes channels, a role and capabilities,\nbuilds a canonical proposal with core's `conversationSelection`, and hands it to `open`. Only the\nperson's decision in the authority's authenticated UI is consent. The approved selection may differ\nfrom the proposal and must stay within their current authority. The empty approved selection still\nincludes direct-message send and receive and own activity publication; disclose these before\napproval. A model flag, API bearer or ordinary tool approval cannot approve the request. Unsupported\napproval paths refuse before issuing a grant. One tool list serves every connection, so no\nadvertised text names one connection's channels. Each call runs against that connection's own\nconfiguration. Authentication, credential issuance and revocation and durable connection records\nstay with the host.\n\nA remote MCP client gives the server no way to start a turn, so the gateway adds `cotal_wait`. It\nreturns at once when a message that should wake the agent is buffered, and otherwise as soon as one\narrives, with the same reply `cotal_inbox` gives. Wake-worthy follows the agent's attention: direct\nmessages, role requests and @mentions always, other channel traffic only in `open`. With none, it\nreturns after `options.waitSeconds`, which defaults to `GATEWAY_WAIT_SECONDS` (50). Keep it under\nevery proxy and client timeout in front of the service. The reply is one JSON body, so nothing is\nsent until the wait ends: Claude Code gives a remote server 60 seconds to the first byte, Cloudflare\nends an origin response after about 100 seconds and claude.ai ends a tool call after 240. A wait\nwhose client closes the request stops listening, and a read whose client is gone by the time it\nwould clear messages clears nothing, so neither takes a message it cannot deliver. A wait ends\nwhen its connection closes, also while the broker is unreachable; it listens for `MeshAgent`'s\n`stopping` event. In `focus` an @mention is not buffered, so the gateway counts the ones\nsince the last read, and a wait that starts after one returns it at once. Every other routed tool\nexcept `cotal_inbox` adds a line to its result when wake-worthy messages are waiting.\n\n`handleGatewayRequest(host, { req, res, body?, auth?, source?, instructions?, approvalResume?, waitSeconds? })`\nserves one HTTP request to the endpoint with the package's own MCP server, so the embedding service\nnever imports an MCP SDK or a zod version of its own. The service authenticates the request, answers\n401 itself when that fails, and passes the result as `auth`. A framework that already parsed the JSON body passes it\nas `body`. The endpoint is stateless: each POST gets a fresh server and a JSON response, and every\nother method answers 405. A `cotal_wait` call holds its POST open for up to `waitSeconds`.\n`GATEWAY_INSTRUCTIONS` is the server instructions text sent when `instructions` is omitted.\n\n`serveGatewayStdio(host, { input?, output?, source?, instructions?, waitSeconds?, wake? })` serves the same\nsurface over stdio, for a local client such as Claude Desktop that launches one process for all of\nits conversations. A stdio request carries no authentication, so `host.account` receives `undefined`\nand names the local user. A client that cancels a call aborts its signal, so a cancelled wait stops\nlistening. The streams default to the process's own, and stdout then carries only MCP.\n\nAn embedder whose client can be woken passes `wake`, to `serveGatewayStdio` or in the options of\n`registerCotalGatewayTools`. The gateway calls `wake.signal(connection, preview)` when a connection\ngets something that would end a `cotal_wait` while no wait holds it. A true result means a reader\nheard the signal, not that it received complete message content. The preview never consumes mail.\n`cotal_connect` adds `wake.listening(connection)` to its reply. An embedder delivering full content\ncan return false from `wake.inboxNotice(connection)` to omit competing inbox-read advice from other\ntool results. Notification-only clients retain that advice.\n\n`wake.attach` receives a rearm callback. Call it without arguments when a replacement reader starts\nlistening. Call it with one connection after confirmed inline delivery to reoffer that connection's\nremaining work. Pass `false` as its second argument to clear the latch without immediately retrying\na refused handoff. Ordinary inbox reads and waits also clear the latch. An adapter offering complete\ninline messages can use `prepareInlineDelivery`: hold whole automatic messages, validate the final\ntransport size, recheck the batch before admission, then settle it with the native result. A failed\nor unknown admission releases the hold without committing. Never settle successfully on a preview,\nsocket write or queued native call. `GATEWAY_INSTRUCTIONS` combines `GATEWAY_CONNECTING` and\n`GATEWAY_LISTENING`, so an embedder can supply its own listening instructions.\n\n### Private tool results\n\n[SPEC \xA713.19](../SPEC.md#1319-private-tool-results) accepts a private, consent-bound read path for\nmanaged-agent tool results. It is not served or enabled. Core exports the closed\n`ToolResultConsent`, `ToolResultRevocation` and `ToolResultServingMaterial` contracts, their\nparsers, and the frozen `TOOL_RESULTS_CLUSTER` declaration. These parse structure and static\nbindings only. They do not verify a consent signature, establish human proof or read current\nrevocation and lifecycle state. The serving material's grant coordinates are not a branded\n`EpServeGrant` and confer no authority. `parseToolResultAnswer` also checks the UTF-8 byte count;\na schema's character limit alone is not the byte bound.\n\n`@cotal-ai/auth` exports `verifyToolResultHumanProof` for the consent prerequisite. It verifies\nan EdDSA IdP JWT against the existing issuer, audience and key pins, then requires integer-second\n`auth_time` no later than the decision and at most 300 seconds old, plus a signed `amr` array\ncontaining `pwd`. The host compares elapsed milliseconds without rounding its clock to seconds.\nJWT clock tolerance does not widen this window. Ordinary bearer exchange does\nnot require these claims or turn them into consent authority.\n\nThe loopback-only `POST /tool-result-human-proof/verify` takes only `{ idpToken, subject }` with\nthe host's current capability and no Origin header. The host supplies its authenticated session\nsubject. Success returns `{ authorized: true, sub, owner, authTime, exp }`; the owner derives from\nthe verified subject, which must match the session. The door verifies proof only. It does not\nsign consent, issue credentials, register a service or serve private content. A host must still\nrefuse platform keys and check current membership and lifecycle at consent admission.\n\nA host may opt into consent issuance with `startAuthService({ ..., toolResultConsents: { keyId,\nresolveAnchor } })`. Provision its Ed25519 nkey seed under `toolResultConsentKey(space)` in the\ninjected private store as `{ ver: 1, keyId, seed }`. The resolver reads that space's current\n`consents` anchor with scope `tool-results`. Missing, corrupt, revoked or mismatched signing\nmaterial refuses. Neither the private store nor the resolver belongs in a worker or client.\n\nThe loopback-only `POST /tool-result-consent/issue` requires the current host capability and no\nOrigin header. Its closed input is `{ id, idpToken, subject, accountPublicKey, holder, target,\nttlMs }`. The host resolves those coordinates from its current authenticated membership and\naccount assignment. `holder` carries `{ actor, lifecycleUid }`; `target` carries `{ owner,\nactor, lifecycleUid }`. Auth verifies the signed password proof and derives the owner itself,\nrequires a current same-owner interactive holder and managed target, and caps the window at\n24 hours. It returns only `{ consent }`, after a create-only `consent.<id>` auth-store write.\nDuplicate ids refuse. A failed or lost acknowledgement returns no consent and never retries.\nThe host must not label an unknown outcome completed or resubmit it automatically.\nOnly the auth mint writer holds consent-KV publish authority. The environment admission writer\nand remote manager issuer do not inherit that grant. Create-only enforcement remains in the recorder.\n\nThis door is absent from the public face. Without the opt-in signer it answers unavailable.\nIt installs no serving material, revocation store, caller view, capture or result reader.\n\nPublic tool arguments and results remain withheld. Auth and host issuance, Manager material,\nconnector capture and the browser caller must each be landed and qualified in the\n[design's owner order](design/private-tool-results.md#9-owner-order), then carried by a stock\nrelease before activation. Without a complete authoritative revocation snapshot and an established,\nenforced clock-skew bound, the serving commands must answer `unavailable`.\n\n### Browser control\n\n`@cotal-ai/core/browser-control` describes and invokes an endpoint from a strict-CSP page. The page\ncompiles no code: the host passes validators it generated at build time with\n`generateBrowserContracts` from `@cotal-ai/core/browser-validators-build` (Node only). The generator\ntakes each cluster document with its recorded digest and schema roots, recomputes every digest and\nrefuses a mismatch or a missing root before writing anything. It supports single-document closures\nonly, so a command whose closure spans several documents refuses. The generated module imports\nAjv's runtime helpers (`ajv/dist/runtime/*.js`), so the host's bundler must resolve `ajv` from\nwhere the host writes that module.\n\n`openBrowserManagerCaller` opens a Manager caller for ONE instance the person selected; there is no\nimplicit selection. It takes the broker's websocket URL, the space, the principal and lifecycle the\nbearer is bound to, the instance, and the Manager's contracts generated from\n`managerBrowserContractSource()` (`@cotal-ai/manager`). Authority comes from an `acquire` callback\nthat returns the person's own `manager-caller` bearer and the space's sentinel credential; an\nembedding's backend obtains the bearer from the stock exchange with the person's IdP proof. The\nbearer's space, principal, lifecycle and instance are checked before anything is dialled, and the\ndescribe is pinned to that instance. The view carries only the commands the person's ledger scope\ngrants. `spawn`, for example, carries `spawn`, `despawn`, `inspect`, the persona commands and\n`goal-result`, while operator reads such as `status` and `models` come only with `admin`; the broker\nrefuses a command the view does not carry. The connection never reconnects on its own; `reopen`\nacquires again and re-checks every pin.\n\n`followGoal` submits a goal command once under an envelope id the host persisted before calling\nit, and follows the goal to its terminal with the stock goal follow. Live progress settles the goal\nonly when it comes from the instance and epoch that accepted it; any other terminal settles nothing\nand prompts a read of the goal's own record. `reconcileGoal` reads an\naccepted goal back through the Manager's `goal-result` command, and `verifyGoalResultFact` checks\nthe returned fact, including its outcome digest, through Web Crypto. Nothing here retries. Three\noutcomes stay distinct: an error reply is a refusal; a goal accepted but not seen to finish answers\noutcome `unknown` and is read back, never submitted again; a request no instance answered throws\nthe stock unanswered error (`unansweredRequest`).\n\n`POST /manager-instances` on the auth service lists the signed-in person's own Manager instances. It\ntakes `{ idpToken }`, verifies it as the human exchange does and answers\n`{ v: 1, owner, complete: true, instances: [{ instanceId, placement, state }] }`. `placement` is\n`remote` or `local`; `state` is the verdict the `manager-caller` exchange selects on (`candidate`,\n`not open`, `not registered`, `not this owner's`, `unknown`). A `candidate` is registration\nevidence, not proof that the instance answers; a describe pinned to it is. The route refuses\nbrowser-origin requests like every exchange route, so a page reaches it through its own backend,\nand a failed gate read answers 503, never an empty list.\n\n### Remote manager client composition\n\n`@cotal-ai/manager` exports the `remoteManagerClient` namespace, containing the stock remote\nrequest builders and response validators, and `registerRemoteManagerAuthority` for registration\nwith a host-issued prepare credential. It returns the process epoch and registration revision its\nown registration committed. When a later start of the same instance registers before this start\nauthorizes its serve grant, this start is refused with `expired`.\n`RemoteManagerIdentityState` describes the five private\nmanager identities stored under an explicit account-local root. Use these public exports when\ncomposing `ManagerOptions.remoteAuthority`; do not copy CLI validators or import private modules.\n`managerClusterArtifacts()` returns the canonical document, manifest and their digests used by\nregistration. Pass its `[document, manifest]` pair as `contractArtifacts` to both\n`remoteManagerRegistrationProof(owner, state, contractArtifacts)` from `@cotal-ai/core` and\n`remoteManagerClient.remoteManagerAuthorityRequest(state, actor, \"activate\", { registrationProof, contractArtifacts })`.\nThe request builder takes each operation's coordinates as named fields of its last argument.\nThe host still validates the artifact closure and current registration before activation.\n`managerBrowserContractSource()` returns the same document with its closure digest and each\ndistinct schema root, the input `generateBrowserContracts` from\n`@cotal-ai/core/browser-validators-build` takes at a host's build. Its closure manifests are left\nout because they are not schemas; the generator recomputes every digest and refuses a mismatch.\nThe namespace includes closed standing/run renewal, admission, maintenance, enrollment,\nretirement and goal-index scan helpers. `publicIdentities(state)` returns the `identities` record every request\ncarries: the five public ids, in the order the host echoes them. `remoteRunHosting` builds the four `runHosting` callbacks from the registration\nand the transport you supply for the host's run admission, run attempt and authority requests, so\nyour manager sends the run requests the stock manager sends. The namespace provides no signer or\nnew grant. The host still owns authenticated issuance, current registration and activated-run\nobservations, and any guarded foreign-holder repair.\nAn embedding must preserve those checks and supply a supported runtime; the client exports alone\ndo not provide a pooled runtime, an authority service or a complete hosted context.\n\n### Long-lived endpoints take a bearer function\n\n`EndpointOptions.bearer` accepts either a string or a function, and the difference is not stylistic.\nA string is minted once, so when it expires (which it will: callout bearers live minutes) the\nendpoint has nothing to renew with. It will not present the dead token to the broker, since that is\na guaranteed denial that still costs a full auth-callout round trip. It refuses to reconnect, emits\n`warning` saying which case it is in, and retries on a widening backoff until the process\nre-authenticates and rebuilds it. Retry notices use `warning` rather than `error` because Node\nrethrows an unhandled `error` event and would kill a host the endpoint is still trying to recover.\n\nPass a **function** for anything that outlives one bearer. That is a renewal source: it is called\nahead of each expiry and again whenever a reconnect finds the cached bearer dead, and it requires\nexplicit `card.owner` and `card.actor`. The first-party surfaces already do this\n(`UserViewAuth.source`, the connector's `agentBearerCommand`). A string bearer is for a short\none-shot connection.\n\nA `MeshAgent` a host runs in its own process takes the same kind of source. Pass\n`userAuth.bearer`, a function returning a fresh bearer, instead of `userAuth.bearerCmd`, the argv a\nlaunched seat execs. Such an agent has no manager-call bearer, so its spawn, despawn, persona and\nrun calls return a refusal without reaching a manager.\n\nLong-lived hosts must also subscribe to the endpoint's `warning` event. It carries conditions the\nendpoint is surviving, including failed credential renewal, reconnect retries, a durable leave\nit keeps retrying after the broker refuses a durable channel's live subscription, and a channel\nregistry entry that does not decode to a valid config, such as a raw put of an unknown delivery\nclass or of `null` on the defaults key. The endpoint drops that entry from its cache and applies the\ndefault policy until a valid entry is written. A host may choose\nto ignore warnings for a one-shot endpoint whose awaited operation owns the verdict, but that choice\nshould be explicit. An unhandled warning is nonfatal and silent.\n\nThe `error` event carries a fault the endpoint cannot return from a call, such as a refused\nsubscription or a refused publish that no request was waiting on. Attach a listener before `start()`,\nsince Node throws on an unhandled `error`. A denial the broker returns to a request, such as an\nobserver's read of the DM stream, reaches only that call, which decides what it means, and is not\nemitted again as an `error`.\n\n## Booting the daemons\n\n### auth-service\n\n`runAuthService(args, store?)` reads its provisioned long-lived secret kinds (service keys, callout\naccount, issuer keys, owner secret) through the injected `SecretStore`; a host provisions those into\nthe store first. It is a **signer and identity authority**, not a scoped daemon: at runtime it holds\nthe data-account and callout-account signing seeds, the issuer's private JWKs, and the\nowner-derivation secret in process memory (`SecretStore.get` exports raw values). The IdP pin and the\nactor ledger are **not** store-injected: `runAuthService` resolves them under\n`userAuthStateDir(findCotalRoot(), space)`, a path relative to the process working directory, so a\nhost provisions those into that exact directory (neither `store` nor `COTAL_HOME` selects it). It\nalso writes an ephemeral `auth-service.json` discovery file there that carries the live exchange\ncapability. That file appears only after every plane is bound, so waiting on it is the readiness\nsignal: a host that also passes the daemon's pid to the provider's `ready()` gets a process-bound\nwait. The wait extends past the base timeout while that pid is alive, up to a fixed bound, and it\nends at once when the pid exits.\n\n```ts\nimport { runAuthService } from \"@cotal-ai/auth\";\n// store implements SecretStore over your secret backend; get() returns raw seeds into memory.\n// Provision the auth secret kinds into the store, AND the IdP pin + actor ledger under\n// userAuthStateDir(findCotalRoot(), space), before this call.\nawait runAuthService(\n { values: { space, server: brokerUrl, port: \"8081\" }, positionals: [], raw: [] },\n store,\n);\n```\n\n### delivery\n\n`runDelivery(args, store?)` runs from a **pre-minted scoped `delivery` cred** and never loads the\nsigner. Provide the cred either through the injected store (under\n`deliveryCredsKey(space, { injected: true })`) or with a\n`--creds` file; the two are mutually exclusive. The daemon re-fetches the cred from the store at 75%\nof its JWT lifetime and fails loud rather than riding to expiry, so **something must re-sign a fresh\ncred into that same store**. When that read finds the previous generation still there, the daemon\nreports the missed remint and retries in 60 seconds. The current cred stays live until its expiry,\nand the store is read once per retry rather than once per second.\n\n```ts\nimport { runDelivery } from \"@cotal-ai/delivery\";\nawait runDelivery({ values: { space, server: brokerUrl }, positionals: [], raw: [] }, store);\n```\n\nThat renewal is a **signer** operation, not the delivery daemon's:\n`remintDaemonCreds(root, space, store?, { preflight? })` (`@cotal-ai/workspace`) reads the `SpaceAuth`\nsigner **through the same resolved `store`** (`getSpaceAuth(store ?? workspaceSecretStore(root), space)`,\nkeys `auth/broker.json` + `auth/account.<key>.json`; the pre-split `auth/auth.json` monolith is\nmigration input and the container signer mount only) and re-signs the daemon creds (`delivery.creds` and the membership feed's\n`membership-rw.creds`) back into that store. The injected `store` is both the signer source and the\ncredential destination, never a split. `space` is **required** and validated against the store's signer, so a\nstore swapped to a different space cannot re-sign over the wrong broker's creds. `preflight` is a\ncaller-supplied proof that the broker accepts the credential. The reference `Manager` passes a\n`probeConnect` over its `servers`. It gates **every** candidate before overwriting the last-good,\nwhether the signer is a full bundle or a stripped projection: a bundle's JWT chain proves only that\nit is self-consistent and\nnamed the space, NOT that its account is the broker's *current* account for that space (two\n`createSpaceAuth(space)` calls yield same-named, different-account chains), so a same-label alternate\nsigner would otherwise mint a broker-dead cred and clobber the good one. The offline local repair (`doctor auth --fix`) has no preflight. It permits the overwrite only\nunder **authority continuity**: the candidate must be signed by the same account signing key (`iss`) as the current\n(already broker-accepted) cred. A same-label alternate account breaks continuity and is refused, full or\nstripped; a legitimate local re-sign is continuous and proceeds without a network. The reference\n`Manager` runs it on a schedule against its **own**\n`secretStore` (see below), so passing the manager and the delivery daemon the *same* store closes the\nrenewal loop end-to-end on an injected backend: the manager reads the signer from the store, re-signs\ninto it, and the daemon adopts each generation on a preflight-proven 75% timer. The stock\ncross-host composition cannot satisfy that by writing one filesystem and fingerprinting another:\n`Manager.start()` and every later remint challenge the daemon's `reloadStoreIdentity` and a\ndivergent pair is refused naming both stores. The identity is the store the daemon actually\nreloads: an injected coordinate, the workstation root only when `--creds` is\n`<root>/.cotal/<spaceSegment(space)>/delivery.creds` (matching the canonical arm), the\nfile's own directory for any other `--creds` path, or the workstation root. A filesystem\nstore is also named by a random id it records in `store.id` inside its own directory, so two\nhosts that use the same root path are two stores. No key and no `--creds` file may be that\nfile under any name, and a `store.id` that is a symbolic link or holds anything but a lowercase UUID is refused. Uninjected\n`--creds` that names one real workstation while process cwd resolves another is refused\nat start, naming both, because membership-rw still uses `findCotalRoot`. A `--creds`\npath that is not under any `.cotal` tree is not that case and is not refused here. It never\nwalks ancestors with `findCotalRoot`. No bound daemon is not a named\nstore, so start proceeds; a later daemon on a foreign store is refused on the next remint.\nThe first-party filesystem adapter declares its workspace-root identity on the store itself. Other\ninjected adapters declare their stable coordinate on `SecretStore.identity`, or name it in\n`COTAL_SECRET_STORE` on both processes. It never throws: it\nreturns per-file results (`skipped: \"no-auth\"` when the store holds no signer records),\nso the caller must check them or the cred still rides to expiry. A composition whose signer lives in\nKMS/Vault simply injects that store; no bespoke renewal is needed. A `--creds` file path must be\nreplaced atomically before the 75% read. The signer can now be injected behind the store seam, which\nresolves custody. The remaining hosted gap is signer **isolation**. The seed is still decrypted\nin-process at the manager's uid, so it needs an OS sandbox or remote signer.\n\n### Supervisor signing authority\n\n`@cotal-ai/manager` exports the `Manager` class; there is **no** `runSupervise(opts)` runner. The\nprivate CLI `runManager` also does broker-reachability checks, space/default resolution,\nroster/launch parsing and materialization, installed-extension resolution, signal handling, staged\npre-spawn, and the forever wait. A host composes that lifecycle itself around `Manager`:\n\n```ts\nimport { Manager } from \"@cotal-ai/manager\";\nconst mgr = new Manager({ space, servers: brokerUrl, workspaceRoot });\nawait mgr.start(); // then wire your own SIGINT/SIGTERM -> mgr.stop()\n```\n\n`stop()` runs once. A later call joins the stop in progress and settles with it, and a call that\nasks for a different `withAgents` than the running stop is refused.\n\nUnlike delivery, the manager is **not** a pre-minted-scoped-cred daemon (auth-service is also a\nsigner: it holds fewer artifacts than the full trust bundle, but its data-account signing seed still\ngrants complete data-account mint authority on compromise, so this is not least-privilege). On `start()`\nthe manager reads its space's full trust chain **through its `secretStore`** (`getSpaceAuth(this.secrets,\nthis.space)`, composed from `auth/broker.json` + `auth/account.<key>.json`; a container may instead\nmount a stripped signer bundle at the legacy `auth/auth.json` key) and **self-mints** its supervisor cred and renewals from the\ndata-account signing seed. In static mode it also mints every per-agent cred from that seed; in user\nmode agents instead receive callout-minted bearers, but the manager still holds the signing seed for\nits own creds and renewal. So a hosted supervisor is a **trusted per-tenant account-signer process**,\nnot a least-privilege connect client. It additionally requires a `~/.cotal/meshes/space.<key>.json`\nregistry record and the workspace user-auth marker to start in user mode. `ManagerOptions.secretStore`\ninjects the one `SecretStore` the manager uses for **the signer itself (the split trust\nrecords)**, daemon-credential renewal (`remintDaemonCreds`), and per-agent secrets,\ndefaulting to the workspace filesystem store; pass the delivery daemon the *same* store for end-to-end\nhosted renewal. The store declares the same identity on both processes, or both set\n`COTAL_SECRET_STORE` to the same coordinate. The manager\nremints no daemon credential when the daemon names a different store, including a daemon that binds\nafter start; it keeps running and serving its own agents, so one space can carry a manager on more\nthan one workspace root. That manager also stays off the space's renewal lease, so a manager or a\n`cotal doctor auth --fix` on the daemon's store can still take it. Pointing several managers at one coordinate is safe: the store identity\nalone cannot pick an owner (it carries no holder and no tiebreak, so every manager sharing the store\nmatches), so the manager that also holds the space's renewal lease is the one that remints and the\nrest skip it. Without that lease two owners would remint on independent timers with no ordering\nbetween them, and one write would land between the other's re-sign and its fingerprint-only\n`reloadCreds`. `cotal doctor auth --fix` takes the same lease before it re-signs, so a live manager\nand a local repair never race each other either. The signer IS now injectable: a hosted composition injects a KMS/Vault store and no\nsigning seed lands on the hosted disk. What remains is signer **isolation**. The seed is decrypted\nin-process at the manager's uid. That issue needs an OS sandbox or remote signer; it is no longer a\ncustody problem. The other knobs are `workspaceRoot` and the process-global `COTAL_HOME`.\n\nA store, runtime or extension the manager calls may reject with any value, including `null`. The\nmanager logs or refuses with that value's `message`, or with the value itself as text when it has\nnone, and keeps serving. A value it cannot read reports as `an unreadable rejection`.\n\n> Scope note: the **static-auth** operator paths (`cotal spawn`/`join`/`status`/`web`, via\n> `mesh-target` \u2192 `connect`/`preflight`) still read the signer from the local split records (sync\n> `loadSpaceAuth`). That is the single-machine composition, where the signer is on local disk by the\n> static-auth model; multi-tenant hosting runs **user mode**, which never mints from on-disk trust. The\n> store-injectable signer path is the hosted-server set: the manager, `remintDaemonCreds`, and delivery.\n\nThe typed remote-manager authority contract includes a one-shot terminal phase. A host implements\n`remoteAuthority.prepareAgentRetirement` to revoke the managed grant and finish its resumable\nrelease while preserving the UID, then `remoteAuthority.mintRetirementRequester` returns the\nhost-signed JWT for a fresh participant-owned nkey. The credential is pinned to the authenticated\nowner, server-derived manager serve principal, current instance epoch, and exact target lifecycle.\nThe manager then uses the existing auth `retireLifecycle` rail with the operation id derived by\n`managedRetirementOpId(target.lifecycleUid)`. This derivation is the reference remote-Manager\ncomposition's closed contract, not a rule for every retirement entry point; interactive retirement\nkeeps its existing operation identity and remains compatible. The `retireLifecycle` rail independently\nrecomputes the managed id from its broker-pinned target before any gate, head, intent, or barrier\naccess, so mint-time validation is not the terminal boundary. A failure keeps\nthe alias held. This does not expose the auth barrier or give the participant signer authority.\n\nIf the participant disappears after prepare, the host finishes the retirement itself on the auth\nservice's loopback face: `POST /managed-lifecycle/retire` (exported as `MANAGED_RETIRE_PATH` from\n`@cotal-ai/auth`) with the `Bearer <cap>` from `auth-service.json` and only\n`{ owner, actor, lifecycleUid }`. It has the interactive door's guards (POST only, no `Origin`, JSON,\ncapability, closed body) and is never served on the public face. The managed grant must already be\nrevoked at that uid, or it answers 409. It runs the same `managedRetirementOpId(uid)` operation as\nthe rail, and the rail and the door share one in-process flight, so a late participant request and\nthe host call converge on one barrier.\n\n| lifecycle head | answer |\n| --- | --- |\n| absent, or `retired` at another uid | `200 { retired: false, lifecycleUid, notStarted: true }` |\n| `active`/`retiring` at another uid | `409` |\n| `retired` at this uid | `200 { retired: true, lifecycleUid, alreadyRetired: true }` |\n| `active`/`retiring` at this uid | the barrier runs, then `200 { retired: true, lifecycleUid }` |\n\nDeprovisioning durables stays with `deprovisionAgent` and a `deprovisioner` credential. Pass `memberChannels` to both to also purge the retired lifecycle's durable membership rows on those concrete channels. The manager fills that list from the launch's concrete read channels and the delivery daemon's read-only `lifecycleMemberships` admin verb. When that verb cannot answer, rows on other channels stay retained, the teardown logs the inventory as incomplete, and retirement remains held pending retry rather than releasing the alias. Each teardown examines two exact consumers, one ACL key and the named member keys. KV deletion uses a native revision condition; a lost condition with a live replacement refuses rather than claiming absence. Consumer INFO checks before and after DELETE distinguish verified prior absence from disappearance. `acknowledged` counts native DELETE success replies. `disappeared` counts observed live-to-absent consumers and live KV rows whose conditional purge lost to a competing deletion. These KV rows were present at the first read, so they never count as prior `absent` or as this caller's `deleted`. The ACL and membership subtotals preserve that distinction. Neither establishes which concurrent caller uniquely removed a consumer, so `consumers.deleted` and the total `deleted` are `null` when a consumer disappears without a winner token. A repeat after verified absence reports zero, not an invented deletion. `refused` counts slots whose cleanup or state remains uncertain. A partial failure raises `DeprovisionError` carrying these bounded observations. The Manager's static sweep preserves unknown uniqueness rather than adding acknowledged requests as physical removals; its slot totals remain separate.\n\nA host that resumes retained managed actors also implements\n`remoteAuthority.validateRetainedAgent`. The participant sends back the actor token and sentinel it\nalready holds, plus the `nextRegistrationProof` returned by the activation response. That proof is\nhost-issued after registration and binds the manager owner, actor, lifecycle, identity nkeys, current\nregistration revision, and serving epoch. The host checks it against the current open manager gate,\nvalidates the retained secrets against its current managed row, and returns only the non-secret\nauthority shape. The manager binds every result coordinate and the returned authority back to its\ninventory before use. Do not copy the provider's `issuer.json` or `callout.json` into the participant\nstore. Both contain private signing or exchange authority.\n\nThe same composition supplies `remoteAuthority.agentBearerExchangeUrl`, the pinned public auth-service\nbase used by retained children. Remote adoption launches `agent-bearer --exchange-url <base>`; it must\nnot select the local `--dir` arm, which depends on a host-only auth-service process record.\n\nA host that lets a remote participant spawn FRESH managed agents implements\n`remoteAuthority.enrollManagedAgent`. The participant generates the standing actor token, writes it\nat mode 0600, and passes only its SHA-256 digest with the requested actor, label, role,\ncapabilities, and channel lists, so the plaintext secret never leaves the participant machine. There\nis deliberately no `lifecycleUid` input: the host selects the UID, because only the host sees the\nretirement tombstones that make a UID permanently unusable, and a participant-chosen UID could aim a\nfresh grant at a dead incarnation. The host authors the ledger grant, pre-creates the lifecycle-keyed\ndurables, clamps the requested lists to what the spawning owner already holds, and returns the owner,\nactor, chosen `lifecycleUid`, the space sentinel credentials, the effective lists, and\n`agentBearerExchangeUrl`. The manager binds every returned coordinate, re-keys the secret family onto\nthe returned UID, and launches `agent-bearer --exchange-url <base>`. When the hook is absent a\nsignerless manager refuses the user-mode spawn rather than authoring a local grant the host knows\nnothing about.\n\nBoth managed-agent operations ride the one verified `POST /manager-service-authority` transport as\n`kind: \"manager-managed-agent-enrollment\"` and `kind: \"manager-managed-agent-prepare-retirement\"`.\nStock `cotal auth-service` answers both itself, because it owns the actor ledger and the space's\nprovisioning authority. An enrollment writes the managed grant at a fresh UID with the supervising\nactor as its parent, provisions that UID's durables, and returns the daemon's public exchange URL as\n`agentBearerExchangeUrl`; a daemon started without `--exchange-public-port` refuses enrollment. A\nretry with the same token digest answers the same UID while the supervising actor's current grant\ncovers it, and a fresh enrollment's refusal otherwise. While the agent's grant stands, an enrollment\nwith another digest is refused with `conflict` until that lifecycle's retirement is prepared. A\nprepare-retirement releases the target UID's broker footprint and then revokes its grant, so the\nmanager's terminal rail finds the grant gone. A platform that keeps these writers in its own storage\nintercepts both kinds instead. It terminates its own public route, authenticates the human there,\nand asks the auth service for the decision at\n`POST /manager-service-authority/verify-enrollment` (exported as `VERIFY_ENROLLMENT_PATH` from\n`@cotal-ai/auth`) with the `Bearer <cap>` from `auth-service.json` and only `{ owner, request }`. That\ndoor has the managed retirement door's guards, derives the caller's scope from the local ledger rather\nthan the body, checks the manager gate and registration proof in-process, and answers\n`{ authorized: true, owner, actor, instanceId, serveEpoch }`. `authorizeRemoteManagedAgentEnrollment`\nand `authorizeRemoteManagedAgentPrepareRetirement` are exported too, for a host that composes the\ndecision without the HTTP hop. Both require ledger scope `supervise`; `spawn` and `admin` do not\nimply it.\n\nA host that runs managed agents on its own hosted runtime adds two more kinds on the same transport.\n`kind: \"manager-managed-agent-runtime-create\"` asks the host to create the runtime for one agent it\nalready enrolled, and `kind: \"manager-managed-agent-runtime-status\"` reads that runtime's state. Both\ncarry the manager envelope plus `target: { owner, actor, lifecycleUid }`, the coordinate the\nenrollment returned. Both schemas are closed. An unknown top-level or target field, including\n`providerRef`, `handle`, or `name`, is refused as `bad-request`, because the host alone issues and\nholds provider references. There is no stop, adopt, or probe kind: stop goes through\nprepare-retirement. Stock dispatch refuses both kinds with `unimplemented`, and the verify-enrollment\ndoor decides them. `authorizeRemoteManagedAgentRuntimeCreate` and\n`authorizeRemoteManagedAgentRuntimeStatus` apply the enrollment door's checks: host space, a target\nowner equal to the authenticated owner, the manager actor's own ledger row with `supervise`, the open\ngate, the current serve epoch, and the registration proof. Each returns only\n`{ owner, instanceId, actor, target }`. The door touches no provider and writes nothing. The host\nmatches the decision to its own intent record and performs the create afterwards. The host answers\nwith `state` (`reserved`, `creating`, `bound`, `create-unknown`, `closing`, or `closed`), `readiness`\n(`ready`, `bound-not-ready`, or `none`), and an optional `retirementPhase`. A manager builds requests\nwith `remoteManagerClient.remoteManagedAgentRuntimeRequest` and binds the answer with\n`remoteManagedAgentRuntimeState`.\n\nAn enrollment result may also carry `runtimeIntent: { state: \"reserved\" }` when the host reserved a\nhosted runtime for the agent. It is display-only. Older hosts omit it, the manager binds both shapes\nto the same material, and nothing reads it as authority.\n\nNo stock door lets a platform control holder launch or retire an agent for a signed-in user. The\nmanaged-agent kinds above act only under the authenticated owner and refuse a caller that is not\nthat user, so a platform could only run a user's agent by holding the user's login or by enrolling\nthe agent under its own owner. Both are refused. The\n[delegated user launch intent](design/delegated-user-launch-intent.md)\ndesign and SPEC \xA713.16 define the smallest addition. The user admits one launch or one retirement\non the host's authenticated route. The holder consumes that intent once, from its current\nregistration, epoch and lifecycle. The host then enrolls the agent under the user's `u_` owner with\nthe user's own actor as its ledger parent, so the envelope walk, membership and channel lists match\nwhat the user's own manager would produce. Retirement keeps the prepare, provider closure and\nterminal barrier order, and the host finishes it when the holder is gone. A launch the host had to\nundo keeps its agent name held until the host process that ran it confirms it has stopped, and\nwhile the name is held the host also refuses it to the user's own manager. `@cotal-ai/auth` ships the\ntwo decisions, `authorizeDelegatedUserIntentAdmission` and `authorizeDelegatedUserIntentExecution`,\nfor a host that owns an intent store and those writers to compose on its own routes. Both read the\nholder's gate through the handle's `observeManagerGate`, present with `platformControl`, which reads\nover the context's own connection, so the host opens no second data-account connection. It is an\nobservation for the decision: the consuming CAS and the writers still apply their own checks. The\nconsuming CAS pins the incarnation of the host process that runs the flight as the executor, never\nthe auth plane's, because the two can restart independently. `platformControl.host` names that\nprocess's reverse-DNS endpoint, the closure digest of its \xA713.7 cluster and the contract artifacts\nregistration reads, and the auth plane self-authorizes that one name. Registration reads the closure\nmanifest `{ v: 1, root, members }` at `clusterDigest`, then the cluster document at the manifest's\n`root`, and verifies each against its digest. `artifacts` therefore carries both, and `clusterDigest`\nis the digest of the manifest. `members` stays empty: SPEC \xA713.7 lists every reachable artifact\nthere, but this implementation registers single-document clusters only and refuses a manifest that\nlists members. A manifest with any field beyond `v`, `root` and `members` is refused too, as the\ncontract store refuses it. `singleDocumentClosure(document)` returns that manifest and its closure\ndigest. The instance id is a lifecycle token, `[a-z0-9]{26,32}`. The minimal construction below has\none command over the void schema. A host copies it, replaces `document` with its real cluster, and\npasses `host` as `platformControl: { observeAssignment, host }`.\n\n```ts\nimport { mintLifecycleUid, singleDocumentClosure, VOID_SCHEMA_DIGEST } from \"@cotal-ai/core\";\n\nconst document = {\n urn: \"com.example.host\",\n revision: 1,\n attributes: [],\n events: [],\n commands: [{\n name: \"ping\", class: \"ephemeral\", targeted: false, capability: \"host.ping\",\n inputDigest: VOID_SCHEMA_DIGEST, outputDigest: VOID_SCHEMA_DIGEST,\n }],\n};\nconst { manifest, closureDigest } = singleDocumentClosure(document);\nconst host = { endpoint: \"com.example.host\", clusterDigest: closureDigest, artifacts: [document, manifest] };\nconst instanceId = mintLifecycleUid(); // first start only; later starts reuse the persisted id\n```\n\n`registerHostIncarnation(instanceId)` publishes the artifacts, registers that instance through the\nceremony the plane runs for itself, and returns `{ instanceId, processEpoch }` with the epoch that\nregistration committed. The host calls it at every start with its persisted instance id, before it\nadmits or recovers any flight, so a restart fences its predecessor. The first registration of an\ninstance commits epoch 0, which is open and serving like any later epoch, and each later start of\nthat instance commits the previous epoch plus one. A consumer compares epochs for equality and never\nreads 0 as absent or not ready. A start\nwhose confirming read of the gate finds that a later start of the same instance registered is\nrefused with `conflict`. The returned epoch is a committed coordinate and stays current only until\nthe next start registers, which can happen before the call returns. `observeHostGate(instanceId)`\nis a point-in-time read of an executor's gate, and answers null for an absent gate; a sweeper\ndecides on it. `awaitHostFence(instanceId, processEpoch)` resolves with the gate once it is no\nlonger open at that epoch, and with null once it is absent. It takes any non-negative safe integer\nepoch, 0 included, and refuses any other value with `bad-request`. The host arms it with its returned\nincarnation before it admits or recovers any flight, and stops serving when it resolves. It polls\nthe gate, because no runtime credential may watch the auth bucket. The\nhost's launch writer first activates the agent's lifecycle at the pinned UID through the handle's\n`activateManagedLifecycle`, before any ledger row or durable, and its compensation runs the same\ncall before the terminal barrier, so a launch whose agent never exchanged its bearer still reaches\nthe terminal barrier at that UID. The launched agent exchanges its bearer on the context's public\nface, so the host starts it with `publicFace`, and its retirement writer ends at\n`POST /managed-lifecycle/retire` with the handle's `cap`. Stock dispatch refuses both kinds as\n`unimplemented`. The holder's composition passes\n`remoteAuthority.executeDelegatedUserIntent`, which posts the execution request and binds the answer\nwith `parseRemoteDelegatedUserIntentExecutionResult`. It then starts the agent with\n`startAgent({ ..., delegatedIntent: { intentId, owner, parent } })` and retires it with\n`retireDelegatedAgent(name, intentId)`, which stops the agent and removes its local secret family\nonly after the host confirms `retired: true` for its exact target. A delegated agent's stop, exit or\nfailed launch keeps its name held until that retirement confirms, and a secret file the retirement\nfails to remove keeps it held too.\n\nRemote user-mode managers must also supply `remoteAuthority.authorizeAdmin`. The manager builds each\nrequest only from the caller tuple parsed from the broker-authenticated endpoint subject, then relays\nthat tuple over the current registered manager lifecycle. HTTPS does not separately authenticate the\nrelayed caller. The host authenticates the manager operator, binds the request to the current open\nmanager gate, registration proof, serving epoch, and identity nkeys, then reads the caller's unified\nauthoritative row fresh. It returns only the manager owner and `authorized: boolean`, with every request\ncoordinate echoed. Missing, revoked, narrowed, foreign-owner, and stale-lifecycle callers all return\n`false`; malformed coordinates or corrupt and unavailable authority state fail the operation. The\nparticipant never reads or mirrors the host ledger, and the remote branch has no local fallback. The\nsame callback gates all `manager.admin` handlers, any-mode cross-owner control, and `ps` or `inspect`\ncross-owner visibility. Launch keeps its owner-equality policy.\n\nThe remote authority's instance executor remains the scoped maintenance credential for clean service\nderegistration and exact instance registration operations. It carries no records-stream consumer\nlifecycle authority. The manager's boot `goalidx` sweep uses the authenticated host operation, which\nreturns parsed `goalidx.manager.<owner>.>` entries for that owner only. The host keeps the sealed\nconsumer connection and its create/delete rights. The five-minute executor already renews through\n`remoteAuthority.renewExecutor`. The signerless supervisor, serve, goal-writer, session-ledger and\nper-run driver credentials do not yet have a complete remote renewal and adoption path. The\n[hosted runtime contract](design/hosted-runtime-contracts.md) records the bounded additions and\ntheir ownership; it is not a shipped pooled service.\n\n**Signer isolation needs an OS sandbox.** The default pty runtime\nruns agent children under the *same* OS uid and the *same* `workspaceRoot`, so mode-0600 on\nthe trust records does not stop a hostile same-uid agent from reading their absolute paths. The reference\n[deploy](deploy.md) tree does not solve this: it mounts the signer into the agent's own container, so\nits phase-1 boundary isolates agents from each other, not the signer from the agent. A hosted\ncomposition must run the manager/minter that holds the signer in a different uid, container, or mount\nnamespace from the agent children, which mount no signer at all; that split is future\nhosted-composition work, so until it (or a remote/injected minter) exists, do not run untrusted\nagents under this manager.\n\n### Delegated seats outside the manager's filesystem\n\nThe [portable lifecycle bootstrap](design/portable-lifecycle-bootstrap.md)\ndesign and SPEC \xA713.17 define how a managed agent that `enrollManagedAgent` already enrolled starts\nin a child that cannot see the manager's filesystem. The ordinary `spawn` path writes the token and\nsentinel under the manager's workspace root and hands the runtime a launch whose bearer command and\nmaterial file are paths on that filesystem, so such a child needs this path instead.\n\nThe delegation boundary is one optional runtime method. A runtime that implements\n`Runtime.spawnDelegated(launch, handoff)` receives two values and no paths: a `DelegatedSeatLaunch`\n(connector name, persona text, model and the other launch choices) and a `ManagedLifecycleHandoff`\n(space, owner, actor, the host-chosen `lifecycleUid`, broker and IdP pins, the pinned exchange base,\nthe sentinel, the channel lists, and the raw actor token). The manager enrolls once, as it does\ntoday, and builds the handoff from what it holds. It never sends the token to the host, never copies\na file from its workspace or secret store, and never builds a local launch for that seat. No signer,\nissuer or callout record, loopback capability, provisioner or manager credential, control token, or\nmanager path crosses. A spawn choice that only the manager's host can honour is refused before\nenrollment: `--resume`, a manifest agent's `continuity: exact`, `--cwd`, and any shared MCP server,\nwhether from `--share-tools` or the config default (`--share-tools none` passes).\n\nA delegated manager also refuses a caller-supplied persona `config` override in `resolve-agent`,\n`spawn` and direct `Manager.startAgent` calls. The shared persona resolver refuses before checking\nor reading that path, with `failed-precondition` on the wire and one fixed execution-mode message.\nPass a named catalog persona instead. This does not change locally executing managers. A resolved\nmanifest can still use its own validated, host-materialized persona resource. The wire schemas\naccept no internal `resolved` field that could bypass the config refusal.\n\nThe runtime creates one provider resource under `managedRuntimeKey(target)`, writes the handoff into\nit as one 0600 file and the persona beside it, and runs the stock bootstrap there:\n`cotal spawn --config <persona-file> --space <space> --name <actor> --expect-owner <owner> --expect-lifecycle-uid <uid>`\nwith `COTAL_MANAGED_HANDOFF_FILE` naming the file. `delegatedSeatCommand` builds that argv. The\n`cotal` entry reads the file into memory, deletes it and drops the variable before it parses flags,\nprints help or loads extensions, so every outcome, a refusal of its own flags included, leaves no\nfile. It refuses before any broker connection or exchange request when the\nspace, owner, actor, or lifecycle UID differ from the expected values, and then runs the\nenrollment-redeem consumer: it registers the mesh in its own home, writes the token to its own 0600\nfile, and exchanges it through `agent-bearer --exchange-url` unchanged. It never enrolls, redeems, or\nmints a token or UID.\n\nReadiness is still mesh presence. A create whose answer is lost leaves the handle running, so the\nlaunch settles uncertain and stays held; the manager never retries it. A provider read that finds no\nresource under the key is not an exit, because the create may still land. Every close by\n`managedRuntimeKey` is fenced: it completes only once the create was answered or the provider refuses\nany later create under the key. A provider that names its own resources may run the create as a\ndurable operation keyed by `managedRuntimeKey` and close through the identifier its authenticated\ncreate response returned, kept where the host can read it without the manager. Only that response\nbinds an identifier to the key; one derived from the key or found by name or listing is never closed\nor adopted, and while the response is unknown the launch stays held. Every stop, the reap of a child\nwhose parent exited and `Manager.stop({ withAgents: true })` included, runs `prepareAgentRetirement`\nfor the UID-exact target, then `stop()` on the handle `spawnDelegated` returned, then the terminal\nbarrier. `preparePreservation` refuses a cut that holds a delegated seat, and a `Manager.stop()` after\na refused cut retires the seat through the same steps. After the manager is gone\nthe host runs the same steps, makes the same fenced close by `managedRuntimeKey`, and finishes at\n`MANAGED_RETIRE_PATH`. Supply `spawnDelegated` only from a runtime whose host can make that fenced\nclose without the manager. The enrollment redeem\n(`COTAL_ENROLLMENT_FILE`) stays for lifecycles whose token the host generated itself; a host cannot\nmint one for a manager-enrolled lifecycle because it holds only the digest.\n\n### Delegated recovery contracts\n\nCore exports `RemoteDelegatedRecoveryRequest`, `RemoteDelegatedRecoveryResult`,\n`RemoteDelegatedRecoveryLaunchRequest`, their closed parsers, and\n`parseDelegatedRecoveryDescriptor`. `Runtime.rebindDelegated` describes an optional same-child\nrebind. These are contracts for SPEC \xA713.18, not an enabled recovery path. The current Manager\ndoes not call the new method or reconstruct delegated rows. Its public `adoptRuntimeHandle`\nreturns a native handle without inserting a managed row.\n\nA supporting host retains the descriptor in the original user-intent consuming CAS, binds each\nclaim to the original instance and current registration, and supplies complete fenced scans.\nThe manager needs its original persistent secret store and a runtime that can verify the same\nchild execution after process loss. It must retain held aliases before reconciling accepted goals.\nMissing storage or unverified child identity keeps recovery held; neither permits another launch.\nThe request parsers validate data, not fresh host authority or durable transactions. Existing\nhosts continue to refuse the opt-in launch extension rather than silently ignore it.\n\n## Provisioning a space (one-space reference shape)\n\n```ts\nimport { createSpaceAuth, setupSpaceStreams, ensureDefaultDeliveryClass, mintCreds, newIdentity } from \"@cotal-ai/core\";\nconst auth = await createSpaceAuth(space); // trust bundle (in-memory seeds)\nconst provisionerCreds = await mintCreds(auth, newIdentity(), \"provisioner\");\nawait setupSpaceStreams({ servers: brokerUrl, space, creds: provisionerCreds });\n// SPEC section 4: write the default delivery class at space creation so it is wire-discoverable,\n// never inferred from the resolution fallback. A daemon-backed space is \"durable\".\nawait ensureDefaultDeliveryClass({ servers: brokerUrl, space, creds: provisionerCreds, deliveryClass: \"durable\" });\nconst deliveryCreds = await mintCreds(auth, newIdentity(), \"delivery\");\n// put deliveryCreds into your SecretStore under deliveryCredsKey(space, { injected: true })\n// (@cotal-ai/workspace) before booting delivery \u2014 the key is per-space, not the bare kind.\n```\n\nRendering the broker config for a user-auth space is `serverConfig(broker, spaces, { storeDir,\nmaxFileStore?, extraAccounts })`, where `extraAccounts` must include the callout account from\n`createCalloutAuth` so the auth-service has a broker account to answer on. That account never shares\nthe data account. `maxFileStore` is an optional positive integer byte cap; any other value throws.\n\nBroker trust and space accounts are separate authorities: `createBrokerAuth` mints the one\noperator + system account a broker trusts, `createSpaceAccountAuth(broker, space)` signs each\ntenant's data account under it, and `serverConfig(broker, spaces, opts)` renders them all into one\nconfig. A host composition can therefore provision several spaces on one broker today. `cotal up`\nrenders that config from every tenant the root's auth directory holds, so booting one space keeps\nthe broker trusting its siblings, and it refuses to render at all while any account record is\nunreadable. The rest of the CLI lifecycle is still broker-wide: `down`, `clean` and `backup` refuse\non a multi-space root rather than scoping to one tenant, and the per-space lifecycle is the\nremaining multi-space operator layer. See\n[Known gaps](#hosted-composition-gaps).\n\n## Hazardous provisioning primitives\n\n`mintCreds`, the full `Profile`/`CredentialKind` matrix, `createSpaceAuth`, and `stripSpaceAuth` are\nlow-level operator primitives. Handle them as account-authority material:\n\n- A holder of a `SpaceAuth` (or a `stripSpaceAuth` bundle, which **keeps** the data signing seed) is\n a fully-trusted tenant-account authority: it can mint `admin`, `provisioner`, and destructive\n profiles, not merely `supervisor`, and mint a DM-reading identity. `createSpaceAuth`'s full result\n holds operator, system, and account seeds in memory.\n- Choose `profile` and `MintOpts` from **server-side constants**, never from tenant input. `MintOpts`\n can widen the bounded TTL defaults; cap it at your boundary. `CREDENTIAL_LIFETIMES` is a policy\n record, not an authorization boundary.\n- Never log signer material or export it into env. Do not co-locate signer access with an untrusted\n connector/runtime process at the same OS uid (file permissions do not contain a same-uid reader;\n see the manager's isolation note). Segregate per tenant; rotate on compromise\n (`rotateDataAccountSigningKey`).\n\n## Hosted composition gaps\n\nThe primitives above are present as exports, but three capabilities are **not** cleanly composable\nfrom the public contract today. Each is tied to work in flight; a host either waits for the seam or\nscopes the capability out. None is a wire concern.\n\n1. **Delivery immediate live eviction and a fully-hosted membership feed.** The renewable\n `membership-rw.creds` is now a `SecretStore` kind. `startMembership` reads it through the\n injected store, and the manager re-signs it there. The graph-feed writer therefore renews on a hosted\n backend (its data connection adopts each generation on a preflight-proven 75% timer). What still\n reads from a fixed on-disk path are the *static* `membership-observer.creds` and\n `connection-evictor.creds` ($SYS creds, minted at the `up` that provisions the account and renewed by `up --rotate-sys`) and `membership.json`\n (`{accountId}`, non-secret config); those, plus the private provisioning wrapper, keep immediate\n live eviction and a fully-hosted feed a partial gap. A host that re-mints the observer into the\n injected store under the same system signer and scope has it adopted by the running feed at 75%\n of its lifetime. Missing files degrade membership to\n traffic-only and make live eviction refuse (loudly). The supported delivery contract here is the\n Plane-3 durable backstop.\n2. **Supervisor signer isolation.** `ManagerOptions.secretStore` now injects the one `SecretStore` the\n manager reads/writes every secret through, including the composed `SpaceAuth`\n signer (the split trust records), its daemon-cred renewal, and its per-agent kinds. What remains is process\n isolation: the manager still decrypts the signer in-process at its uid, so untrusted agent children\n must run under a different uid/container/mount namespace or behind a future remote signer.\n3. **Per-space lifecycle on a shared broker.** The trust layer is multi-space\n (`createBrokerAuth` + `createSpaceAccountAuth` + N-space `serverConfig`, persisted as\n `broker.json` + `account.<key>.json`) and `cotal up` renders the whole tenant list, but there is\n no per-space provisioning verb and no per-space teardown/backup/restore: the CLI's broker-wide\n lifecycle verbs refuse on a multi-space root, naming the tenants.\n This is the remaining multi-space operator layer.\n4. **A non-Better-Auth production IdP.** The exchange core (`createIdpBridge`) is EdDSA-generic, but\n the stock provider and login client are Better-Auth-endpoint-shaped, `cotalAuthProvider`\n self-registers on import (colliding with a host-owned provider under `resolveAuthProvider`), and\n the login flow speaks Better Auth's device-code endpoints. A different IdP is a host-built auth\n composition on the low-level primitives, not a configuration change (see\n [the IdP callout contract](identity-and-auth.md#the-idp-callout-contract)).\n\n## Hosted durability\n\nSpace-durable **coordination** state (chat/DM/task history, live presence, membership runtime, the\ndurable ACL registry, leases) lives in **JetStream**, written by the delivery daemon and the\nendpoints. It is broker-resident and needs no host-side durable path.\n\nWhat is **not** in JetStream, and is hosting-critical, is trust and authorization state a host must\nplace and keep:\n\n| state | class | where today | hosted injection |\n|---|---|---|---|\n| full `SpaceAuth` trust chain (`auth/broker.json` + `auth/account.<key>.json`, composed; a stripped signer bundle may instead be mounted at the legacy `auth/auth.json` key) | signing authority | `SecretStore` | `SecretStore` (manager + renewal) |\n| auth kinds: callout account/creds/xkey, issuer private keys, owner-derivation secret, data-signer projection | signing/identity authority | four `SecretStore` kinds | `SecretStore` (auth-service) |\n| auth plane instance identity (instance id + serve nkey seed) | restart identity | root `.cotal/space.<hex>/auth-instance.json` | `SecretStore` (`startAuthService`) |\n| `delivery.creds` | standing scoped cred | `SecretStore` or `--creds` | `SecretStore` (delivery) |\n| actor ledger, IdP pin | authorization + trust config | ambient `userAuthStateDir(findCotalRoot(), space)` | none (root-relative; not `store`/`COTAL_HOME`) |\n| `membership-rw.creds` | standing scoped cred | `SecretStore` | `SecretStore` (delivery + manager renewal) |\n| membership-observer / connection-evictor creds + `membership.json` | scoped $SYS creds / config | workspace filesystem | none (see gap 1) |\n| manager agent creds, actor tokens, sentinel creds | lifecycle authority | `SecretStore` | `SecretStore` (manager `secretStore`) |\n| `~/.cotal/meshes/space.<key>.json` record (holds IdP trust pins/root pointers) | non-secret, integrity-critical | machine home | process-global `COTAL_HOME` only |\n| auth-health, renewal records | non-secret diagnostics | workspace filesystem | `workspaceRoot` |\n\nThe `SpaceAuth` trust chain and the auth-service store kinds are **separate** identities/projections,\nnever parts of one document. `auth-service.json` (the live exchange capability) is ephemeral runtime\nstate, not durable, but is sensitive while the daemon runs. `@cotal-ai/workspace` is machine-local\noperator tooling by design; personas, PID files, and the `current-mesh` pointer are truly local and\nmust **not** sit on a hosted durable path. Everything classed above as an authority is what a hosted\ncomposition must provision and persist: signer-bearing server secrets now have `SecretStore` seams;\nthe remaining non-injectable rows are the explicit ambient `workspaceRoot`/cwd paths above.\n\n## See also\n\n- [Substrate stability](stability.md): what v0.3 and the 0.x packages guarantee, and the projected v0.4 break.\n- [Identity and auth](identity-and-auth.md): the profile matrix, the signer, and the IdP callout contract.\n- [Delivery daemon](delivery-daemon.md): the Plane-3 durable backstop.\n- [Deploy](deploy.md): the reference container against an external broker.\n\n## Hosted run revocation\n\nThe authenticated manager-service authority door accepts `manager-run-revoke` with the registered\nmanager envelope and `revoke: { runId, reason }`. The issuing host reads the admission's recorded\nowner and requires the verified requester to be that owner or to hold `admin` in its fresh actor\nledger row. It verifies the current manager registration proof, space, account and epoch. It accepts\nno owner or attribution from the body.\n\n`AuthAuthorityPlane.revokeManagerRun` implements the operation. `AuthProvider.requestRemoteRunRevoke`\ntransports it, and `remoteRunHosting` supplies `RunHostingContext.revokeRun` for a signerless\nmanager. The callback returns the stored `RunRevocation`, including on repeats. A host composing\nits own remote authority must supply that callback to revoke. No signer reaches the participant.\nCLI and MCP revoke verbs are a follow-up described in the [run-start design](design/user-auth-run-start.md).\n\n### Private tool-result prerequisites (not serving)\n\n`@cotal-ai/auth` exports `createToolResultRevocationCustody`,\n`recordToolResultRevocation`, `snapshotToolResultRevocations` and one-shot\n`consumeToolResultSnapshot`. These are host-only prerequisites, not material issuance,\nservice registration or a browser/private-result path. `requireToolResultClockPrerequisite`\nalways refuses `unavailable`: no native runtime clock-domain/generation/suspension proof\nexists, and no numeric delta is invented.\n\nCustody exclusively creates a **fresh** auth store after a private fsynced intent/generation.\nExisting stores or journals are never adopted. Only a standalone broker and owner-private local ext4 journal parent are supported. Only the creating process may use its sealed,\nnon-reconnecting reader/writer custody. Unknown writes, missing/changed journals, stream\nidentity/configuration drift or connection loss invalidate it. Crash/rebind recovery stays\nunavailable. Routine broker restart, upgrade, network loss or process restart therefore leaves\ngate 1 permanently unavailable for that space. Recognize this as `revocation custody lost; no reopen from current INFO`.\nNo retirement/reprovision recovery protocol exists in this increment. Gate 2 cannot assume\ncustody survives broker maintenance. The trusted composition supplies current authenticated consent/target authorizers\nand exclusively holds account/provisioning keys. Independently running privileged provisioners,\nuncontrolled legacy writers and existing roots have no qualified custody and are unsupported.\nThis is not protection from a malicious host administrator or copied signing keys.\nThe separate internal custody profile uses two non-expiring, non-reconnecting credentials,\nwithout planeCandidate or a renewal timer. With the two sealed scanners this is four standing\nconnections across three profiles. The CREATE provisioner still expires and closes after CREATE.\nA leaked custody JWT plus user seed no longer has a 900-second expiry bound. Stopping the\nservice closes its own sockets, not copied credentials. Signing-key removal invalidates copied\ncredentials as well. This remains inside the trusted-process/account-signer residual.\n\nRevocation creates only `revocation.<targetDigest>.<consentId>` with native expected-last-subject\nsequence zero, no operation/TTL headers and independently fsynced acknowledgement receipts.\nConsent bytes must match the recorded target and expiry. Persistence uncertainty reports effect\noutcome `unknown`. Acknowledged writes report `executed` even if receipt or later custody checks\nfail. Refusals before publication report `not-executed`. No automatic repeat follows uncertainty.\nMarkers persist indefinitely in this\nslice. A separate bounded-retention successor must prove consent expiry plus\n`EP_AUTH_MARKER_TTL_MS` and snapshot safety before a pruner/TTL may exist.\n\nA snapshot fixes T0 before IO and a leader W once, walks only through W, bounds count/bytes/time,\nand compares exact native sequence/subject/payload evidence to custody receipts. The committed-W\nartifact is a process-private invocation seal binding that evidence, account, generation and\nnative created/config identity. It cannot be parsed, copied, reused or used after custody loss.\nNo current INFO equality or operator safe flag establishes historical integrity. Journals are\nexclusive 0600 files with file and directory fsync, checked before/after IO, not adopted on restart.\nClustered brokers and leader failover are unsupported. Any connection loss refuses; current INFO cannot restore custody.\n\nA short-lived create-only provisioner holds API discovery and exact auth-stream CREATE, then closes.\nSeparate package-internal grants split revocation-only publish from INFO/MSG.GET reader authority.\nRetained issuance writers **already can enumerate** auth metadata. Raw ACLs cannot enforce CAS,\nDEL/TTL/schema/target conventions. Every raw MSG.GET profile carries SPEC13.9's caller-selected\nreply **foreign-subject response-injection** residual, even with only its own inbox subscription.\nRoutine PR CI runs `smoke:tool-result-prerequisites`: bounded fixtures, broker-observed\nnon-expiring exact grants, a no-renewal timer probe, census and actual KICK/key-removal\nrefusals. It does not wait for credential expiry and is not serving acceptance.\n`pnpm smoke:tool-result-custody-lifetime` additionally waits `AUTHORITY_CLIENT_TTL_SEC + 15`\nseconds and checks unchanged identities plus record/snapshot success. Run it explicitly or\nthrough the manual/weekly Custody lifetime workflow. Historical lifetime receipts remain\nsource-pinned evidence, not a grade for a composed successor.\n\n\n## Native service custody guards\n\nOrdinary lifecycle executors refuse a UID reservation carrying `kind: \"native-service-uid\"`.\nThe reservation has closed `native`, `assignment` and `creation` objects. It is not an ordinary\n`{ owner, actor, mintedBy }` audit, and a reader must not project it into one. Its assignment\nbinds the full account, space, owner, endpoint, instance and serve-duty tuple. The actor is derived\nfrom that tuple, and the creation operation and request digest remain fixed.\n\nThese guards run before ordinary root reuse, activation, gate and head changes, mint finalization,\nand native retirement. Ordinary connect and currency reads also refuse service custody, including\nrows whose head or credential mirror is missing. Existing ordinary reservation bytes are unchanged.\nClosed head, gate and credential parsers continue to reject service markers. Without\n`serviceEndpoints`, no product path writes a service reservation or mints a marked root. The service\naccess-view contract and the sealed native access/callout composition are shipped; only a context\nopened with `serviceEndpoints` composes them, with the service host's approval and registration\ndescribed below. Lifecycle retirement and restore remain unwired.\n\nThe package-internal service prerequisite reader is separate from ordinary connect and lifecycle\ncurrency readers. It leader-reads immutable UID custody, the exact active service head, native\n`gate.<uid>` open at generation1, and the exact active root credential. All three custody mirrors\nmust match the full reservation digest. Service head `managerInstance` equals immutable assignment\n`instanceId`, and native `processEpoch` is fixed at1. These are not ordinary enrollment coordinates\nor the separate endpoint gate/witness registration epochs. Creating/cancelling heads and their\nRootPlan recovery obligations remain outside this read-only active path. Missing or malformed\nnative gate evidence cannot be replaced by an endpoint gate. This is no writer, bootstrap, root\nmint/abort, rotation, cancellation or terminal-retirement authority, and not an atomic multi-key\nsnapshot. Positive fixtures raw-seed synthetic marked records, not production service admission.\nThe account is trusted host configuration. Sealed readers do not attest the broker account or\ndetect a cross-account pair of legitimately constructed handles. The authority plane constructs the\nservice callout's registry, reader and contract reads from its own data account.\n\nA package-internal custody writer is not exported; in the product only the service endpoint host's\ndecide and revoke reach it, and the proposals they act on come only from the owner-proved public\nauthority route of a context opened with `serviceEndpoints`. Bootstrap\nwrites the reservation, the pending-root endpoint gate, a creating head, a frozen activation native\ngate opened at generation1, a RootPlan fixed once by head CAS, the planned active root, an\nidentical-bytes native gate touch and the active head stamp. It rechecks the endpoint gate at open\nand before the plan, root and stamp writes. Cancellation denies the endpoint gate first, then\nwrites the cancelling head and freezes the native gate under the cancel operation. An unstamped\nplanned root is fenced by a create of revoked bytes or by CAS of the exact active row. A stamped\nroot is retained, and every cancellation result is nonterminal. An absent reservation leaves\nendpoint deny residue and reports unconfirmed; a foreign or deleted reservation refuses without a\nwrite. Each write is a per-key create or revision CAS under a sealed host whose genuine plane-claim\nhold is re-read before and after the write, plus a fatal latch. That is local readiness control,\nnot broker writer exclusion, so a paused writer can still land one late conditional write. Each\nrequest is bounded by the remaining original deadline. In bootstrap, once the plan step has fixed\nor read the RootPlan, requests are also bounded by its expiry. Cancellation keeps only its own\ndeadline, so an accepted cancellation can still proceed after the root expires. At its bound the\nwriter stops waiting for a request but does not cancel it at the broker. The writer issues every\nendpoint-gate request itself, so no endpoint-gate request starts after an invocation returns, and\nit re-reads the endpoint gate before every plan, root and stamp write attempt. Bootstrap's final\nactive-root proof is several read-only reads behind one bounded wait. If that wait ends at its\nbound, the remaining reads can still run after the invocation returns; none of them writes.\nBootstrap holds instead of reporting an active root whose expiry its final synchronous check\nobserves, with no await between that check and the return. The result can still be stale by the\ntime the caller acts on it. The caller supplies that deadline and the\nreservation; nothing here authenticates a host, approves serving, retires terminally, releases an\nalias or rotates a root.\n\nA second package-internal module builds service host operations on that writer: register\nadmission, decide (approve or refuse), revoke, a create-only name binding, and the operation and\nproposal reads. It shares the\nwriter's sealed host, plane-claim hold check and fatal latch, and each invocation opens its own\ncustody host over a per-invocation effect registry. That host writes only the invocation's scope,\nthe exact keys discovery located before the effect registry opened, and the effect registry is\nhanded the same key set so a broker grant can match it. A step that needs a key outside the scope\nholds as `scope-mismatch` before anything is issued; replaying the same request discovers what is\nstored and scopes it. Each effect registry rides its own connection, whose credential may publish\nonly on that key set's subjects (subject confinement, not operation confinement; the engine itself\nonly creates and CASes those keys), never renews or reconnects, and expires at the invocation's original deadline\nrounded up to the whole second. With agreeing clocks and timely scheduling the broker's\nwhole-second timer closes it within about 1999 ms after that deadline; that is granularity, not a\nguaranteed close time, and no close proves an earlier write cannot still settle. Losing the\nconnection fences the invocation. Discovery's credential has no write at all. The module's records and their write order are in SPEC \xA713.1. Results name their operation once a binding exists or may exist: `pending`, `held` and\n`refused` all carry `operationId`. A result can carry `debt`, the capacity releases its operation\nstill owed as far as that invocation observed. A result without `debt` does not mean nothing is\nowed; the operation read is the authoritative view of the debt. A `held` result by itself implies\nneither a binding nor an authorized granter: a tripped engine, or a granter without a current\ninteractive row, gets one `held` answer for a register, decide or revoke before any read of the\nrequest's keys. `denied` means the invocation wrote nothing, and either the request can never bind\nor the invocation's own read found no binding. That read is point-in-time; another invocation of the\nsame request already in flight can still bind it later. A revoke result reports `denyNew` when it\nhas evidence: an endpoint-gate read, which gives `unknown` when the read failed or found another\nassignment's gate row, or an assignment that is already fenced, which gives `landed`. The early `held` answers of a tripped engine and of a\ngranter without a current interactive row carry no `denyNew`, and a result without `denyNew` says\nnothing about deny-new. Completed results\nsay whether this invocation applied them or returned a stored record. A stored approval returns\nunchanged, and its `approvedGateRevision` is null unless its own gate CAS was observed. The index\ndoes not export this module; the typed request, result and view parsers in the service host contract\nare public.\n\n`openAuthAuthorityPlane` and `startAuthService` take `serviceEndpoints: { startup: { kind: \"normal\" } }`\nto host these operations; it is off by default and no CLI flag sets it. With it, the plane and the\nhosted handle gain `serviceEndpointHost(request)`, which answers `decide`, `revoke`, `name-bind`, the\nfour caller-policy operations and the two reads in process. A hosted context rejects it as\n`unavailable` while it is not ready. That function has no route or capability of its own; register\nadmission is reachable only through the\npublic route below. The host authenticates no caller: the embedder must authenticate the operator or\nowner granter it passes. `name-bind` needs an operator granter and `expectedRevision: 0`. It creates\nthe endpoint's name binding once, with your `claimReference`, and only the identical request recovers\nit; nothing updates, transfers or releases a name. The host checks no domain control, so verify the\nclaim, or delegate a namespace you control, before you bind. The host runs over the plane's claim hold, data account and\nactor ledger directory. A restore or missing startup refuses the plane open, which unwinds and\nreleases the claim. If that unwind cannot close an effect connection, it stops there: the claim\nstays held, the plane's clients, scanners and cleanup timer stay live, and there is no handle to\nretry. Only the process exiting ends them, after which the existing crash reclaim applies. Its discovery connection is an ordinary renewing read-only client: a broker loss\nfails its reads until the stock client reconnects, and those calls answer `unavailable` with nothing\nwritten. A renewal failure closes it permanently, and the host stays unavailable until the plane is\nopened again. Both plane fatal paths trip the host, and either one trips it even while the\nplane closes, without making the plane fatal. Plane close refuses new host calls from the\nmoment it is called, waits for every call that already entered, from entry through discovery and\neffect open, closes discovery and then releases the claim. An effect connection that failed to\nclose stays owned past its call: close closes it again first, and rejects with the claim still held\nwhile it stays open, so you can retry close. Until a retry succeeds or the process exits, discovery,\nthe administrative rail, both scanners and the remaining clients stay live, so no successor can\ntake over the claim.\n\nWith `serviceEndpoints`, both faces also serve `/service-endpoint-authority`, and without it that\nroute answers 404. `GET` returns the receiver's account and space. `POST` takes a `{request, proof}`\nenvelope of at most 2 MiB. An IdP proof derives the owner, who needs a current interactive cli or web\nrow; register needs one, and so may activate. A service's renewal proof authorizes activate, renew\nand status, and its access bearer authorizes ready and status; neither is owner authority. A typical\nsequence: the service registers and gets `approval-required`; you bind its name with an operator\n`name-bind`; the owner approves with `decide`; the service replays the same register and gets\n`registered` with its witness and snapshot; it activates and gets a `credential` with an access bearer\nof at most 60 s and a renewal proof of at most 24 h, neither outliving the native root or approval;\nit connects to the broker with the bearer through the composed native service callout, binds its\nresponder, calls `ready`, and renews before the bearer expires. The auth provider's\n`serviceEndpointInfo` and `serviceEndpointAuthority` hooks are the client, and accept a service proof\nas well as the signed-in login.\n\nEvery effectful request is keyed by its requestId and body. Keep one requestId per operation and\nwitness, store it with the witness and the renewal material before you send it, and reuse it with\nthe same body after a restart: a replay is safe, a changed body under the same requestId is a\n`conflict`, and one instance accepts at most 64 distinct requests, so fresh keys per refresh or\nrestart exhaust it. A register requestId stays bound to the body and proposal its first answer\nadmitted: after approval, replay that exact request, and after a refusal or expiry, propose again\nunder a new requestId. A 503 means the request may be bound: replay the same request, or read its\nstatus with `target: { kind: \"operation\", operation, requestId }`; the selector names the operation\nsince this release, and the old `{ kind: \"operation\", requestId }` no longer parses. An owner reads\nstatus only while it holds an interactive row, and a service proof reads only its own instance.\n`registered` and `ready` in a status mean only that the request's registration is current. A\npre-activation re-registration sends `expected: { state: \"registered\", witness }` and yields a new\nwitness; a register that another request overtakes before its barrier freezes answers `conflict`,\nso read the instance and expect its current witness. A register whose gate moved under it without a\nnew registration, such as a freeze another barrier then aborted, also answers `conflict` with the\nwitness unchanged: if the instance still shows the witness you sent, replay the same request. A\nregister held inside the native barrier, or while another invocation of the same request is still\nrunning, answers 503; replay the same request. Once a service has activated, its registration\ncan no longer change. Commands must be ephemeral and declare\nno governed trait.\n\n`caller-grant` takes an owner granter with an interactive row who holds the approved instance, and a\ngrantee is a current exact owner, actor and lifecycle UID. A managed grantee also needs its actual\nsame-owner parent chain, with each current ancestor holding active covering grants for the same\nservice approval. The operation records those immutable grant ids and lifecycle UIDs in `parentRefs`\nand rechecks them before append. An admin scope never substitutes for endpoint caller rights. Every\ncapability must name that instance with `instanceOnly: true`, carry no journal access and no `any` or\n`ledger` target, and sit within the approval's caller ceiling. The answer is `granted` with the grant\nas stored. Ordinary user-auth agent connects now derive their endpoint rows from this policy through\ncore's native grant builder, including reserved Describe for each authorized exact instance. The\ncallout rechecks grantee lifecycle, named ancestor grants, current name, assignment, root, activation,\napproval and ceiling, then checks the policy evidence again after signing and native-key recording.\nThe original connection expiry is at most 60 seconds from issuance, and capped by root and approval\nexpiry. These are fresh leader reads,\nnot a linearized revoke fence. Existing issued connections keep their original expiry, and caller\nrevocation remains deny-new, not terminal absence proof.\nOne instance indexes at most 256 grants over its life, including attempts refused after indexing, and\na full index answers `resource-exhausted`; a new instance is the remedy. `caller-revoke` admits the\ngrant's source owner, its grantee owner and an operator. It answers `pending` with\n`denyNew: \"landed\"` and `notBefore`, in epoch seconds, 71 s after the broker time of the grant's\ncutoff marker. It never answers `revoked`, and the result parser refuses that arm.\n`caller-policy-read` gives an owner its own grantees, and another owner's grantee only filtered to a\nservice it holds; `caller-policy-list` needs a service selector.\n\nCaller grants need `max_payload >= 2098176` (2 MiB plus 1 KiB), `max_control_line >= 4096`,\nand an auth stream with `max_msg_size: -1`. Broker generation accepts an explicit\n`serverConfig(broker, accounts, { ...options, maxPayload: 2098176 })`; omission leaves the native\n1 MiB default unchanged. No startup path raises limits automatically.\n\nFor a **standalone broker only**, pass `serviceEndpoints.callerPayload.observerCreds` to\n`startAuthService` (or the corresponding authority-plane option). The callback returns fresh\nhost-private credentials from the broker owner's\n`mintServiceCallerPayloadObserverCreds({ pub: broker.sys.pub, signingSeed: broker.sys.signingSeed })`.\nKeep that registered SYS signer with the broker owner, never in the data account, a sandbox or a\nparticipant client. The helper returns an in-memory, at-most-60-second VARZ-only credential.\nThe callback only supplies credentials, owns no authority resource, and may finish after its\n10-second observation deadline has refused the operation. It is not a cancellation callback.\n\nStartup authenticates native VARZ, matches the observer and data connection's broker incarnation,\nchecks config/system-account evidence and live stream limits, then writes an exact-key assertion\nbound to that broker, configuration and genuine plane claim. Configured clusters, gateways and\nleaf links refuse, regardless of connected peer count. This is not multi-broker proof. A default\n1 MiB broker fails startup with the required byte limit and creates no assertion.\n\nPositive caller issuance repeats the observation and checks the exact assertion revision, complete\nplane hold and effective authority again before native JWT release. Withdrawal, config changes or\nclaim loss refuse new issuance. Reopening needs an explicit bootstrap to reassert for the new claim;\na prior-plane broker-bound assertion cannot authorize another append. Legacy assertion syntax still\nparses for existing trusted storage fixtures but alone never enables production caller issuance.\nShutdown drains in-flight observations and closes their owned connections before releasing the claim.\nReads and revokes do not need a positive assertion. A revoke whose policy row would not fit holds\n`payload-precondition` without writing it, and a failed live-limit check withdraws the assertion.\n\nGive each caller operation a fresh random requestId, record it with the exact body before you send\nit, and replay that body after a lost reply, a 503 or a `held` answer. Never switch to a new requestId\nwhile an earlier answer is unknown. A settled `refused` or `expired` ends the attempt, and a grant\nreplayed after its 10-minute window adopts a grant that landed and otherwise answers `expired`.\n\nCaller issuance is generic, not tied to Linear: the same provider-independent contract discovery and\ninvoke path works for ordinary and managed user-auth callers. Controlled policy append currently\nrequires the application's approved service-host instance. Native control endpoints retain their\nexisting ledger/scope and typed authority contracts, not fabricated application approvals. Scoped\nDescribe is not available, and complete terminal retirement and restore remain unsupported. A host revoke refuses new activation, renewal, readiness\nand connections, but a connection already open keeps its original expiry of at most 60 s. Approval\nruns service bootstrap and revoke runs cancellation, while complete observer evidence and writer\nexclusion remain unavailable. Do not seed service reservations in an existing space, adopt old\nservice-shaped rows, or use ordinary retirement to make them terminal. A signing-key or raw-store\nholder can bypass helper guards, so schema rejection alone does not make mixed-version writers safe.\nRestore must quarantine service work until a separately supported reconciliation protocol is\navailable.\n"
65364
+ "body": "# Embedding Cotal\n\n> **Guide** (informative) \xB7 **For:** implementers building a service on top of Cotal \xB7 **Prereqs:** [Architecture](architecture.md), [Identity and auth](identity-and-auth.md), [Delivery daemon](delivery-daemon.md)\n\nThe `cotal` binary in this repo is one composition root: an operator CLI. A separate service\n(for example a hosted, multi-tenant Cotal) does not fork this repo. It writes its **own**\ncomposition root that depends on the published `@cotal-ai/*` packages and imports the surfaces it\nwants. `bin/cotal.ts` uses the same composition pattern. This page is the contract for that: what is a real library\nexport you can build against, how to boot the server-side daemons from those exports, and where the\ncurrent export surface stops short of a fully hosted composition.\n\nThis is the \"guarded substrate\" boundary in practice. Nothing here reveals or assumes a specific\nhost; it documents the public seams any embedder composes.\n\n## What you embed\n\nThe supported reference shape here is **one broker operator serving one space** (one tenant: a\ndedicated data account, under an operator that also holds the system account and a quarantined\nauth-callout account) plus three standalone processes. The trust layer itself composes many spaces\nunder one broker operator today (`createBrokerAuth` + `createSpaceAccountAuth` + N-space\n`serverConfig`); what does not exist yet is the per-space **lifecycle** on a shared broker (see\n[Known gaps](#hosted-composition-gaps)). The three processes:\n\n| daemon | package | what it is |\n|---|---|---|\n| auth-service | `@cotal-ai/auth` | the NATS auth callout, the IdP token exchange, and JWKS. Plane 1 to Plane 2. |\n| delivery | `@cotal-ai/delivery` | the Plane-3 durable backstop: fan-out writer plus trusted reader, per space. |\n| supervise | `@cotal-ai/manager` | the per-machine agent lifecycle (spawn/despawn/attach), per space. |\n\n`mint`, `deliver`, and `auth-service` expose their behavior as direct library primitives, and the\nsupported one-space bootstrap below re-composes from exported low-level primitives. `supervise` and\nthe full `up` orchestration are **not** public runners: `up` also does broker bring-up, restore,\nprocess and registry management, and lifecycle work, and `supervise`'s orchestration is private (see\n[Supervisor signing authority](#supervisor-signing-authority)).\n\n## Interactive Manager primary\n\nA trusted composition can pass `{ interactiveManagerSelection: \"local-primary\" }` as the third\nargument of `runAuthService(args, store, options)`. `AuthServiceRuntimeOptions` is exported by\n`@cotal-ai/auth`. The third argument still accepts the native daemon lifecycle hooks. No CLI\nflag or exchange field sets the policy, and unsupported runtime values refuse before startup effects. `openAuthAuthorityPlane` accepts the same option beside its fresh\n`localManager` reader; `OpenAuthAuthorityPlaneOptions` is exported too.\n\nThe opt-in chooses the genuine local Manager up front for an unpinned human `manager-caller`\nexchange. It reads the persisted local identity and requires the existing open gate, serve\nprincipal, registration revision and status epoch checks. An absent, unreadable or noncandidate\nlocal Manager refuses without scanning or substituting remote Managers. Explicit selectors win,\nand managed-secret exchanges and launching or issued instance bindings keep their existing rules.\nThe option adds no scopes, registry reads or inventory rows.\n\nThis routes all unpinned interactive Manager control, not only `ps`: slot reads, stop, attach,\ninput, spawn, models, hosted run verbs and generic Manager describe/invoke share the view. It\nchooses local even when the person has a live remote Manager. A host must decide whether that\nrouting is appropriate; a local Manager without execution cannot forward a spawn or run to the\nperson's remote Manager. The person selects that Manager explicitly for those operations.\n\nAbsent the option, selection is unchanged. `startAuthService` still has no local Manager and\nkeeps its remote-only composition. Supplying this programmatic option does not enable it in stock\n`up`, restart or recovery paths; a host must compose those paths separately after adopting the\npublished API. `ps` still lists only the selected Manager's managed seats, not HTTP-hosted sessions.\n\n## Account connection census\n\n`mintAccountCensusCreds` creates a host-only SYS user with at most five minutes of authority.\nIt can request CONNZ for one DATA account and subscribe only to an account-qualified reply prefix.\nIt grants neither KICK nor account events. The host supplies a registered SYS signer; the helper\ndoes not find, persist or renew that signer, and the broker verifies its authority on connection.\n`observeAccountCensusWithCreds` requires this narrow scope and a pinned server key. It reads one\nbounded page. Malformed, incomplete, multiple-server or cluster-declared replies are unknown,\nwith no connection list. A complete result describes only that server's observed account page.\nThe host still needs independent topology and restore guarantees, deny-new, a fixed cutoff and\nlifecycle fencing before using an observation for retirement. These helpers do not grant service\nregistration or terminal retirement.\n\n## Service authority contracts\n\n`@cotal-ai/core` exports closed service-authority request, proof and result types and their\nruntime parsers. The parsers bind a result to the submitted request and registration witness.\n`AuthProvider.serviceEndpointAuthority` is an optional provider hook for this protocol. A provider\nwithout the hook must refuse service setup. The user-auth provider implements the HTTP client,\nincluding `serviceEndpointInfo` for the DATA-account binding. It uses the pinned exchange origin,\nrefuses redirects and does not retry an unknown mutation outcome. Server-side HTTP failures\nremain unconfirmed even when their bodies resemble a protocol denial. A registered snapshot must\nname the submitted closure set. Inline artifacts include each closure manifest as well as its\ndocuments, within the envelope byte limit; native per-closure walk limits still apply. The client does not transmit\nthe local host capability or read callout signing material. Local setup needs the client sentinel\nprojection written by `prepareServer`; a missing projection refuses rather than reading server secrets.\n\n`@cotal-ai/auth` exports host request and view types with `parseServiceHostRequest`. Parsing checks\nstructure only, not owner consent, current grants or admission state. The host handlers and complete native\nservice lifecycle are not implemented yet, so these interfaces do not enable service registration.\nThe native endpoint gate also carries service approval and activation fields. Registration and\nserving guards retain those fields and refuse expired or revoked approval; these guards are not\na replacement for the missing host lifecycle. Internal host gate transitions bind approval to a\ncurrent native root and make activation monotonic. Registration completion is stamped in the\nreopen CAS; a definite abort preserves the previous stamp. They do not provide consent, public handlers\nor terminal retirement. Pre-activation registration requires an empty issuance family, including\ndeleted records, and release rechecks fixed expiry after asynchronous work.\n\n`UserTokenIssuer.issueServiceRenewal` signs a separate registration-bound renewal proof.\n`validateServiceRenewalProof` checks its signature, fixed expiry and full witness binding.\nIt is not a connect bearer. These primitives do not authenticate a current approval or native root;\nthe host must do that before issuing or accepting authority. No client refresh loop is provided yet.\n\n`UserTokenIssuer.issueServiceAccess` signs a serve-only user bearer with the full registration\nwitness and a fixed deadline of at most 60 seconds. Validation requires empty scope and rejects\nordinary parent, manager, session and transfer claims. Ordinary exchange and permission construction\nrefuse this view before actor lookup. Callout\nrefuses it unless a sealed native service authority is configured. The private composition binds\nthe current assignment, native root, registered serving grant and receipt before an encrypted\nresponse. Normal authority-plane startup does not enable it yet; public registration, restore\nreadiness and coordinated root retirement remain required.\n\nInternal service-access reads retain native credential-root authorization and check the activated\nregistration, assignment, current name and fixed expiry. They do not create a serving grant or\nrelease a native JWT. The private callout composition performs native issuance and final\nauthorization when explicitly configured.\n\nService serving-issuance evidence binds the authenticated broker user key, server key and client id.\nThe native release fence rechecks that record and the gate after its CAS. Ordinary serving and\ncredential-root records cannot use this service binding. This is not proof of broker acceptance;\nproduction authority-plane wiring and terminal retirement remain unfinished.\n\n`mintServiceCalloutJwt` is a host-only mint seam. It uses the native serving grant, the actual\nprincipal name and tags, the authenticated broker user key and a separate client inbox nonce.\nExpiry stays bounded by the original admission and access deadline. It does not authenticate the\ncallout request, record natkey evidence or release an encrypted response; the host must do those.\n\nNative service receipts bind the same epcred connection and registration witness to the broker user\nkey. Recording rechecks the evidence, receipt, native authority and fixed expiry. Expired service\nreceipts are retained, not pruned on elapsed JWT time. Qualified retirement and production\nauthority-plane wiring are still required before enabling the service path for clients.\n\nInternal service-name bindings are create-only and return owner and revision from one native read.\nThe trusted host must authenticate the operator and verify the namespace claim before writing.\nNative auth, delivery and manager names keep their existing authority; they cannot be application\nservice bindings. No transfer, release or public name-binding handler is implemented yet.\n\nInternal caller-policy storage uses the sealed native registry and binds each row to one account,\nspace and grantee UID. Grant ids are immutable; an append cannot replace or reactivate one.\nInitial writes require no prior native key history. A deletion arriving after an absence read\ncannot recreate a policy or a pending service gate.\nRevocation begins with a revision-pinned deny-new intent that unrelated appends preserve.\nThese are storage operations, not entitlement checks; the service endpoint host below is their only\nwriter. The effective resolver and native caller issuance are described under the service endpoint\nhost below. Receipts and terminal caller revocation remain unsupported.\n\nScoped Describe callbacks also receive an optional immutable local context with the actual subject,\ncomplete-envelope fingerprint, serving identity and receive-relative deadline information.\n`parseDescribeObservation` shares native syntax and full-JSON fingerprint checks with trusted observers.\nNeither the context nor a fingerprint proves broker receipt to a remote authority. The remote\nobserver and policy projection are not implemented yet. The client contract admits an explicit\nread-only `describe-view` operation using only service-access proof. It checks witness, observation,\nprincipal-policy basis and remaining validity, requires no-store responses, and bounds preparation,\nHTTP streaming and validation by the same local deadline. Cancellation cannot restart that deadline.\nThe native handler and serving adapter are still required before this can authorize a remote view.\n\n`parseEndpointCapabilities` normalizes the native caller capability shape. `endpointCapabilitiesWithin`\nchecks route, instance, target and journal containment using the native grant builders. The host\nstill authenticates the granter and checks current grantee, parent and approval records.\nCross-principal relative-target comparisons refuse when containment is unproved.\n\n`authorizeServeGrant` accepts a read-only `ServiceSpecReader`. A trusted provider can supply\nverified registration and contract reads without constructing a writable KV adapter. The function\nstill derives the complete serving surface and creates the process-local grant.\n\n## Owner-bound platform supervisors\n\nA host can start one user's supervisor without retaining that user's login. Pass\n`platformSupervisor: { authorizePlatformAdmin, observeAssignment }` to `startAuthService`, then call\n`handle.platformSupervisorAuthority(owner)` inside the trusted authority process. The first callback\nmust authenticate the platform's administrative caller through the host's own authority. The second\nreads that owner's recorded `PlatformSupervisorAssignment` fresh on every call. An absent,\nended, expired, foreign-account or wrong-revision assignment refuses issuance.\n\nTo end a supervisor, record its assignment `ended` and call\n`handle.endPlatformSupervisorAssignment(owner)`. It rechecks platform-admin authority and refuses\nunless your record already reads `ended`. It retires the manager gate and the issued supervisor and\nexecutor generations. After that, registration, activation and renewal refuse material returned\nearlier, even before it expires, with `platform supervisor assignment for owner <owner> has ended`\nor the retired-gate refusal. Stop the worker as well: an open broker connection lives until its\ncredential's capped expiry.\n\nThe assignment has `kind: \"platform-supervisor\"`, `scope: [\"supervise\"]`, one derived owner,\none manager instance, one lifecycle UID, a revision and a finite `expiresAt` in Unix seconds. It is\nnot an interactive or managed-agent ledger row and grants no `admin`. The returned door is bound to\nthat owner and accepts `PlatformSupervisorAuthorityRequest` with the same owner, assignment revision\nand an existing `RemoteManagerAuthorityRequest`. It serves only `prepare`, `activate`, `renew` and\n`renewStandingBundle`. Unknown fields, generic profiles, mint, human exchange and another owner\nare refused before issuance. Do not give the worker the auth handle, loopback capability or signer.\nThe host carries only the bound door over its authenticated worker channel.\n\nUse `remoteManagerClient` and `registerRemoteManagerAuthority` from `@cotal-ai/manager` for the\nexisting registration ceremony. The result keeps the stock manager material shape and five nkeys.\nAll returned credentials expire no later than the assignment. The supervisor and executor ceilings\nare recorded in the native issued store. The recorded permission set must be byte-identical to the\nauthority plane's signed native permission set at issuance and every renewal, and must never widen.\nThese JWTs are signed by the authority plane, not returned by the user-token callout. The door\nadds no human bearer, callout view, owner ledger grant or enrollment authority. Run admission,\nhost-backed enrollment and shutdown maintenance need their own host composition.\n\nSee [the issuance design](design/user-auth-run-start.md#platform-supervisor-registration).\n\n## The export surface\n\nEverything below is a real export of a published package, reachable from the package root (each\npackage publishes `.` via `dist/index.{js,d.ts}` and ships `files: [\"dist\"]`).\n`@cotal-ai/connector-claude-code` also publishes `./mcp`, which hosts managed Claude Code sessions\nin one process ([Connect Claude](connect-claude.md#host-sessions-in-one-process)).\n`@cotal-ai/core` also publishes `./browser-control` and `./browser-validators-build`\n([Browser control](#browser-control)). Type-only names are marked; import them with `import type`.\n\n`CotalEndpoint` accepts `environment` as an explicit presence label. Pass `null` to omit the label\nwithout reading `COTAL_ENVIRONMENT`. Omitting the option preserves the process environment default.\n\nShared connector sessions report `card.meta.host` and `card.meta.cwd` from the connector process\nwhen the session is constructed. They override agent-file values and are advisory display data.\nThe directory is a startup snapshot, not a model harness's later shell directory. Neither field\nchanges routing or access, and the connector reads no command-line arguments or environment dump\nfor these values. A connector whose process can host several sessions passes\n`{ withholdProcessCwd: true }` as `MeshAgent`'s third argument and reports each session's directory\nwith `setCwd` (`CotalEndpoint.setCardCwd`) once the harness supplies it; until then the field is\nabsent. 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.\n\n**Daemon runners and lifecycle**\n\n| symbol | package | purpose |\n|---|---|---|\n| `runAuthService(args, store?)` | `@cotal-ai/auth` | boot the auth-service daemon; `store` injects the secret material. |\n| `runDelivery(args, store?)` | `@cotal-ai/delivery` | boot the delivery daemon; `store` injects the scoped `delivery` cred. |\n| `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`. 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 activation that host runs at a delegated launch's pinned lifecycle UID. 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. |\n| `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. |\n| `startDeliveryService(inputs)` | `@cotal-ai/delivery` | start one account-scoped delivery instance and return a `HostedServiceHandle` with `readiness`, `drain`, and idempotent `close`. `close` rejects when the lease row is still held after it, by another holder or because the release did not commit, or when the release cannot be confirmed. The process runner remains the CLI entry. |\n| `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 }`. |\n| `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. |\n| `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. |\n| `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. |\n| `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/platform-pooled-control-authority.md) design describes. |\n| `ManagerStatus` *(type)* | `@cotal-ai/manager` | verified status payload, including optional advisory `host` and `version` from the answering Manager. Older payloads may omit them. |\n| `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. |\n| `createRuntime`, `Runtime` *(type)* | `@cotal-ai/manager` | resolve the spawn backend (pty built in). |\n| `liveKvEntries(kv, filterOrOptions?, 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. |\n\nThe remote manager authority parser accepts `renewStandingBundle` and `renewRunDriver` only with\nan assigned account nkey, the current manager process epoch, and a host-authenticated registration\nproof. A run renewal also names its active holder, takeover, epoch, fencing token, and the two\nexisting nkeys. The host must fresh-check those coordinates against its registration gate and run\njournal before issuing server-selected profiles. A host without that renewal authorization refuses\nthe request. Until the host issuer wires the operations and validates them on real connections,\nthe presence of these types is not an operational pooled renewal guarantee.\n\nAn execution-free control Manager retains native registration, goal recovery, session-ledger,\nrenewal and maintenance duties. Its status reports `execution: \"none\"`, `runtime: \"none\"`,\n`custody: \"none\"`, no children or connectors, and false `classSpawn`, `runHosting` and\n`terminalSessions`. Local NONE uses this existing status schema without a new wire field.\nExecution is recorded per instance. An incompatible record, retained seat/turn inventory or an\nolder unclassified instance refuses boot before recovery. NONE refuses caller-driven writes,\nincluding persona definition, purge, transcript transfer, preservation and resume, and resolves\nno model adapter. It starts no transcript receiver or sweep. Autonomous registration, lease,\nrenewal, session-ledger and goal maintenance still write their native control state.\n\nSelect the genuine participant Manager with a host-issued, instance-pinned `manager-caller`\nview for workflow and terminal operations. That Manager retains the children, workflow journal\nand terminal sessions. The control instance neither adopts roster peers nor proxies user runs.\nMissing participant selection or authority refuses. This capability adds no application routing.\n\nFor a genuine local control instance, pass `execution: \"none\"` without `pooled`, `remoteAuthority`\nor `runtime`. Startup requires that space's local signing trust, provider pin and user-mode mesh\nrecord. It keeps the native persisted local instance and serve identity; local-primary selection\nuses the existing gate. An existing unclassified identity, including a preclaimed identity with no\nexecution record, is refused. A NONE record cannot restart as runtime. Retained seat or turn\ninventory refuses before recovery. Do not remove identity or capability files to change modes.\n\nThe public `cotal supervise --execution none --space <space>` composition supports the same local\nmode. It validates arguments before connector seeding, resolves no Runtime, and rejects startup\nworkload flags and participant registrations. Omitted execution keeps the ordinary PTY behavior.\nThis does not opt an auth service into local-primary, change scopes or broaden native ps.\n\nRuntime remains the default, but local startup now claims its native identity and execution\nrecord during admission, before later authentication and registration checks. A failed start\ncan leave those files classified as runtime. Malformed identity or capability files also refuse\nearlier. These lifecycle timing changes do not change the instance's identity format or authority.\n\nWith `renewStandingBundle` configured, the manager renews all five standing credentials together.\nIt checks every returned credential for the held nkey and assigned account, test-connects each one,\nand adopts them only if the serve epoch has not moved. A refused or failed candidate leaves the\ncurrent credentials in place and records the refusal as cleanup debt until a later renewal succeeds.\nOn shutdown, after the standing context is drained, an expired maintenance executor is renewed\nthrough the existing scoped host operation so deregistration can finish without restarting duties.\n\nIssued-authority references, source coordinates and stores accept nonempty space tokens in\n`[A-Za-z0-9_-]`, including underscores. These boundaries reject input that would be rewritten by\nthe subject-token sanitizer. Endpoint names still use the separate DNS-label grammar.\n\n**Provisioning and minting** (all `@cotal-ai/core`)\n\n| symbol | purpose |\n|---|---|\n| `createBrokerAuth(label)` | mint BROKER trust: the operator and system account one nats-server trusts. One per broker, shared by every space on it. |\n| `createSpaceAccountAuth(broker, space)` | mint one space's own data account, signed by that broker's operator: the add-a-tenant primitive. |\n| `createSpaceAuth(space)` | the one-space convenience: broker trust + one account in a single composed bundle. |\n| `setupSpaceStreams({ servers, space, creds })` | create the space's JetStream streams. |\n| `ensureDefaultDeliveryClass({ servers, space, creds?, deliveryClass })` | write the space's default delivery class at creation so it is wire-discoverable (SPEC section 4). |\n| `serverConfig(broker, spaces, { storeDir, maxFileStore?, extraAccounts?, port?, host? })` | render the broker config: one operator, N space accounts. `storeDir` is required, `maxFileStore` caps JetStream file storage in bytes (omitted, nats-server's dynamic default applies), and `extraAccounts` preloads the auth-callout account. |\n| `mintCreds(auth, identity, profile, opts?)` | mint a scoped cred for any `Profile`. |\n| `mintMembershipObserverCreds`, `mintConnectionEvictorCreds` | mint the membership/eviction scoped creds. |\n| `provisionAgent`, `provisionAgentDurables` | create a principal's bind-only durables. |\n| `newIdentity`, `stripSpaceAuth` | a fresh nkey identity; a stripped signer bundle (data signing seed only). |\n| `Profile`, `CredentialKind`, `MintOpts`, `SpaceAuth` *(types)*, `CREDENTIAL_LIFETIMES` | the profile matrix and cred lifetime policy. |\n\n**Auth building blocks** (all `@cotal-ai/auth`)\n\n| symbol | purpose |\n|---|---|\n| `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. |\n| `createUserTokenIssuer`, `pinnedJwksResolver` | mint and verify the Cotal user bearer. |\n| `createIdpBridge` | exchange a verified IdP JWT for a Cotal bearer (see [the callout contract](identity-and-auth.md#the-idp-callout-contract)). |\n| `deriveOwnerToken`, `validateUserToken` | owner derivation; strict bearer validation. |\n| `cotalAuthProvider` | the self-registering `auth-provider` extension. |\n| `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. |\n| `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. |\n\n**Seams and the wire** (all `@cotal-ai/core` unless noted)\n\n| symbol | purpose |\n|---|---|\n| `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. |\n| `FsSecretStore`, `workspaceSecretStore(root)` | the filesystem default. **These live in `@cotal-ai/workspace`, not core.** |\n| `AuthProvider` *(type)*, `Connector` *(type)*, `Runtime` *(type)*, `Command` *(type)* | the extension contracts; implementations self-register on import. |\n| `registry` | the shared registry a composition root pulls surfaces into. |\n| `CotalEndpoint`, subjects, message types | the wire client and shapes. |\n| `ParsedArgs` *(type)* | the shape the daemon runners take (see below). |\n\nFor a Linux Unix-socket adapter, `peerCredentials(socket)` from `@cotal-ai/seat` returns\nkernel-observed peer `pid`, `uid` and `gid`. Compare these against the host's authorization\npolicy; request-supplied identity and process liveness do not replace that policy or a\nlifecycle fence. The helper starts no custodian and refuses unsupported platforms or a\nmissing native helper.\n\nThe runners take a CLI-shaped `ParsedArgs`, not a typed options object, so a host fabricates one:\n\n```ts\nconst args: ParsedArgs = { values: { space, server, port: \"0\" }, positionals: [], raw: [] };\n```\n\nFor an embedded delivery instance, use `startDeliveryService` instead. Its `HostedContextInputs`\ninclude the account public key and lifecycle UID, space, broker URL, injected store, stable\n`storeIdentity`, and an explicit `stateDir`. The store must declare that same injected identity.\nThe initial delivery credential must belong to the assigned account. The function returns only\nafter the delivery responder is bound. `close()` withdraws serving and releases only the lease\nowned by that instance. It closes both membership connections even when a disconnected drain\nfails, so they cannot reconnect after closure. It rejects when the lease row is still there\nafterwards, held by another instance or left by a release that did not commit, or when the broker\ncannot confirm the release. If the delivery connection drops or is down while the release is in\nflight, `close()` waits up to the broker-gone window (`COTAL_DELIVERY_BROKER_GONE_MS`, 15 seconds\nby default) from the drop for it to reconnect, and repeats the release once on the reconnected\nconnection before it decides. `drain()` returns the same promise. A successor is refused while such\na row lives, and the bucket TTL expires a row the instance left. Credential-expiry health state clears after\nsuccessful broker-verified adoption through the existing `reloadCreds` rail. A failed start\nrefuses locally without exiting the host process or stopping another account's delivery service.\nIf a health fault occurs during an asynchronous store read, startup rejects when the read returns\nand closes any resources created by that late completion.\n\n`startAuthService` takes the same `HostedContextInputs`. The store must declare the assigned\ninjected identity, and its data account must be the assigned account. The IdP pin and ledger live\nunder the explicit `stateDir`. The auth plane's instance identity holds its private serve seed, so it\nlives in the store under `authInstanceKey(space)`. A record an earlier release kept under `stateDir`\nmoves into the store on the first start.\nThe context never resolves a workspace root from the working directory and has no local manager, so\nonly remote manager gates can be selected. It returns after\nthe authority plane, the callout subscription and the loopback listener are bound. A start that\nfails closes the connections it opened and releases its plane claim, so a retry on the same space\ncan claim it. A fenced plane or\na lost broker connection makes that context `unavailable` and closes it without exiting the process.\nThe host writes no discovery file for it. The handle carries what `auth-service.json` holds for a\nCLI start: the loopback `url`, `publicUrl` when a public face runs, and the per-start `cap`. The cap\nalone authorizes the loopback host actions, lifecycle retirement and managed-agent enrollment\nverification, so keep it in the authority process. `publicFace` takes the CLI's public face inputs\n(`port`, `url`, `trustedProxy`, `advertisedServer`, `agentProvisioningUrl`) under the same rules,\nand a face without a port refuses to start.\n\nTwo optional inputs serve a platform composition. `platformControl: { observeAssignment }` adds\n`platformControlAuthority` to the handle. It is a typed in-process method, served on no listener,\nthat issues the manager-service request family for the one control manager the backend assigned to\nthis account, under a derived `p_` owner. It reads the assignment fresh on every call and refuses\nan IdP token, another account, a stale revision, another instance or lifecycle, and an instance\nanother owner registered. It refuses `prepare` and `activate` while the assignment's named\npredecessor is still registered or frozen. The same input adds `platformControlReadiness(instanceId)`,\nthe route for a host that needs to know whether its assigned control manager is serving. It returns\nthat instance's attributed `status` reply and refuses any instance the current assignment does not\nname, or one whose gate another owner holds. It reads over the context's own connection, whose\ngrant is the assigned instance's `describe` and `status` and its own reply rail. That connection\nrenews in process like the context's other connections and never leaves it, so the host lends no\nhuman or operator credential to a worker, mints no control instrument per read, and does not read\nliveness off the manager process. Without the input both members are `undefined`.\n`standingRenewableTtlSeconds` is forwarded unchanged to the authority plane, which bounds it to 5\nto 86400 seconds. It is a trusted-host input for the renewal rehearsal, and no request or CLI flag\nsets it. SPEC \xA713.1 and \xA713.6 define the view.\n\nThe auth plane can renew a registered manager's\nfive standing credentials from the current service registration. Run-driver renewal still\nrefuses without an authoritative activated-run reader, so these handles do not yet make a\ncomplete pooled auth and delivery host. A fresh auth plane can initialize without a\ndelivery-admin responder. Reclaiming a held claim from a dead predecessor needs the delivery\ninstance first: its admin rail must complete the broker connection-liveness sweep before the\nauth plane takes the claim. An absent or inconclusive oracle refuses the reclaim.\n\n### Remote MCP gateway\n\n`@cotal-ai/connector-core` exports `registerCotalGatewayTools(server, host, source?, options?)` for\na service that serves Cotal to a hosted MCP client over HTTP. A hosted client can send many\nconversations through one bearer and one MCP session, so a request does not say which conversation\nsent it. Each conversation calls `cotal_connect` and passes the `connection_id` it returns on every\nother tool call. A missing, unknown or other-account handle returns `CONNECTION_REQUIRED` and runs\nnothing. No call falls back to another connection of the account.\n\nThe host implements `GatewayHost`:\n\n- `account(auth)` returns the account the request authenticated as, read from the SDK's `AuthInfo`.\n It throws when the request has none, and the tool then runs nothing.\n- `open(account, name, context?, selection?, approvalId?)` starts a connection only after the person\n approves its permissions. `selection` is a proposal. Throw `ApprovalRequired` with the request ID,\n approval page, expiry and proposal while they decide. The tool returns a structured pending result\n and a readable link, with no connection handle. `approvalId` resumes one unchanged request when\n `options.approvalResume` is enabled. It conveys no authority. Bind it to the authenticated account,\n space, name and request generation. An issued approval cannot grant a replacement connection.\n Throw `NameTaken` only for an authoritative name collision. A new request tries `name_2` and later\n variants up to `GATEWAY_NAME_ATTEMPTS`; a resumed approval never renames. The collision check and\n issue must be atomic. Other errors reach the caller without renaming and must not carry secrets.\n- `find(account, id)` and `close(account, id)` act only on a connection that account owns.\n\n`cotal_connect` refuses a name `assertValidName` rejects before it calls `open`. Its reply names\nthe channels the new connection reads, may join and may post to, from the configuration its grant\nproduced. `GATEWAY_TOOLS` names the mesh tools the gateway offers, `cotal_docs` included. A native\nembedder whose host issues each conversation its own grant may pass `options.tools` from\n`NATIVE_GATEWAY_TOOLS`, which adds spawn, despawn, persona, run and reconnect but not yield.\nRegistering a tool offers it to no connection by itself: a connection calls only the tools its\nhost lists in `GatewayConnection.tools`, default `GATEWAY_TOOLS`, and only those its credential's\ncapabilities allow. With `options.selection`, `cotal_connect` takes channels, a role and capabilities,\nbuilds a canonical proposal with core's `conversationSelection`, and hands it to `open`. Only the\nperson's decision in the authority's authenticated UI is consent. The approved selection may differ\nfrom the proposal and must stay within their current authority. The empty approved selection still\nincludes direct-message send and receive and own activity publication; disclose these before\napproval. A model flag, API bearer or ordinary tool approval cannot approve the request. Unsupported\napproval paths refuse before issuing a grant. One tool list serves every connection, so no\nadvertised text names one connection's channels. Each call runs against that connection's own\nconfiguration. Authentication, credential issuance and revocation and durable connection records\nstay with the host.\n\nA remote MCP client gives the server no way to start a turn, so the gateway adds `cotal_wait`. It\nreturns at once when a message that should wake the agent is buffered, and otherwise as soon as one\narrives, with the same reply `cotal_inbox` gives. Wake-worthy follows the agent's attention: direct\nmessages, role requests and @mentions always, other channel traffic only in `open`. With none, it\nreturns after `options.waitSeconds`, which defaults to `GATEWAY_WAIT_SECONDS` (50). Keep it under\nevery proxy and client timeout in front of the service. The reply is one JSON body, so nothing is\nsent until the wait ends: Claude Code gives a remote server 60 seconds to the first byte, Cloudflare\nends an origin response after about 100 seconds and claude.ai ends a tool call after 240. A wait\nwhose client closes the request stops listening, and a read whose client is gone by the time it\nwould clear messages clears nothing, so neither takes a message it cannot deliver. A wait ends\nwhen its connection closes, also while the broker is unreachable; it listens for `MeshAgent`'s\n`stopping` event. In `focus` an @mention is not buffered, so the gateway counts the ones\nsince the last read, and a wait that starts after one returns it at once. Every other routed tool\nexcept `cotal_inbox` adds a line to its result when wake-worthy messages are waiting.\n\n`handleGatewayRequest(host, { req, res, body?, auth?, source?, instructions?, approvalResume?, waitSeconds? })`\nserves one HTTP request to the endpoint with the package's own MCP server, so the embedding service\nnever imports an MCP SDK or a zod version of its own. The service authenticates the request, answers\n401 itself when that fails, and passes the result as `auth`. A framework that already parsed the JSON body passes it\nas `body`. The endpoint is stateless: each POST gets a fresh server and a JSON response, and every\nother method answers 405. A `cotal_wait` call holds its POST open for up to `waitSeconds`.\n`GATEWAY_INSTRUCTIONS` is the server instructions text sent when `instructions` is omitted.\n\n`serveGatewayStdio(host, { input?, output?, source?, instructions?, waitSeconds?, wake? })` serves the same\nsurface over stdio, for a local client such as Claude Desktop that launches one process for all of\nits conversations. A stdio request carries no authentication, so `host.account` receives `undefined`\nand names the local user. A client that cancels a call aborts its signal, so a cancelled wait stops\nlistening. The streams default to the process's own, and stdout then carries only MCP.\n\nAn embedder whose client can be woken passes `wake`, to `serveGatewayStdio` or in the options of\n`registerCotalGatewayTools`. The gateway calls `wake.signal(connection, preview)` when a connection\ngets something that would end a `cotal_wait` while no wait holds it. A true result means a reader\nheard the signal, not that it received complete message content. The preview never consumes mail.\n`cotal_connect` adds `wake.listening(connection)` to its reply. An embedder delivering full content\ncan return false from `wake.inboxNotice(connection)` to omit competing inbox-read advice from other\ntool results. Notification-only clients retain that advice.\n\n`wake.attach` receives a rearm callback. Call it without arguments when a replacement reader starts\nlistening. Call it with one connection after confirmed inline delivery to reoffer that connection's\nremaining work. Pass `false` as its second argument to clear the latch without immediately retrying\na refused handoff. Ordinary inbox reads and waits also clear the latch. An adapter offering complete\ninline messages can use `prepareInlineDelivery`: hold whole automatic messages, validate the final\ntransport size, recheck the batch before admission, then settle it with the native result. A failed\nor unknown admission releases the hold without committing. Never settle successfully on a preview,\nsocket write or queued native call. `GATEWAY_INSTRUCTIONS` combines `GATEWAY_CONNECTING` and\n`GATEWAY_LISTENING`, so an embedder can supply its own listening instructions.\n\n### Private tool results\n\n[SPEC \xA713.19](../SPEC.md#1319-private-tool-results) accepts a private, consent-bound read path for\nmanaged-agent tool results. It is not served or enabled. Core exports the closed\n`ToolResultConsent`, `ToolResultRevocation` and `ToolResultServingMaterial` contracts, their\nparsers, and the frozen `TOOL_RESULTS_CLUSTER` declaration. These parse structure and static\nbindings only. They do not verify a consent signature, establish human proof or read current\nrevocation and lifecycle state. The serving material's grant coordinates are not a branded\n`EpServeGrant` and confer no authority. `parseToolResultAnswer` also checks the UTF-8 byte count;\na schema's character limit alone is not the byte bound.\n\n`@cotal-ai/auth` exports `verifyToolResultHumanProof` for the consent prerequisite. It verifies\nan EdDSA IdP JWT against the existing issuer, audience and key pins, then requires integer-second\n`auth_time` no later than the decision and at most 300 seconds old, plus a signed `amr` array\ncontaining `pwd`. The host compares elapsed milliseconds without rounding its clock to seconds.\nJWT clock tolerance does not widen this window. Ordinary bearer exchange does\nnot require these claims or turn them into consent authority.\n\nThe loopback-only `POST /tool-result-human-proof/verify` takes only `{ idpToken, subject }` with\nthe host's current capability and no Origin header. The host supplies its authenticated session\nsubject. Success returns `{ authorized: true, sub, owner, authTime, exp }`; the owner derives from\nthe verified subject, which must match the session. The door verifies proof only. It does not\nsign consent, issue credentials, register a service or serve private content. A host must still\nrefuse platform keys and check current membership and lifecycle at consent admission.\n\nA host may opt into consent issuance with `startAuthService({ ..., toolResultConsents: { keyId,\nresolveAnchor } })`. Provision its Ed25519 nkey seed under `toolResultConsentKey(space)` in the\ninjected private store as `{ ver: 1, keyId, seed }`. The resolver reads that space's current\n`consents` anchor with scope `tool-results`. Missing, corrupt, revoked or mismatched signing\nmaterial refuses. Neither the private store nor the resolver belongs in a worker or client.\n\nThe loopback-only `POST /tool-result-consent/issue` requires the current host capability and no\nOrigin header. Its closed input is `{ id, idpToken, subject, accountPublicKey, holder, target,\nttlMs }`. The host resolves those coordinates from its current authenticated membership and\naccount assignment. `holder` carries `{ actor, lifecycleUid }`; `target` carries `{ owner,\nactor, lifecycleUid }`. Auth verifies the signed password proof and derives the owner itself,\nrequires a current same-owner interactive holder and managed target, and caps the window at\n24 hours. It returns only `{ consent }`, after a create-only `consent.<id>` auth-store write.\nDuplicate ids refuse. A failed or lost acknowledgement returns no consent and never retries.\nThe host must not label an unknown outcome completed or resubmit it automatically.\nOnly the auth mint writer holds consent-KV publish authority. The environment admission writer\nand remote manager issuer do not inherit that grant. Create-only enforcement remains in the recorder.\n\nThis door is absent from the public face. Without the opt-in signer it answers unavailable.\nIt installs no serving material, revocation store, caller view, capture or result reader.\n\nPublic tool arguments and results remain withheld. Auth and host issuance, Manager material,\nconnector capture and the browser caller must each be landed and qualified in the\n[design's owner order](design/private-tool-results.md#9-owner-order), then carried by a stock\nrelease before activation. Without a complete authoritative revocation snapshot and an established,\nenforced clock-skew bound, the serving commands must answer `unavailable`.\n\n### Browser control\n\n`@cotal-ai/core/browser-control` describes and invokes an endpoint from a strict-CSP page. The page\ncompiles no code: the host passes validators it generated at build time with\n`generateBrowserContracts` from `@cotal-ai/core/browser-validators-build` (Node only). The generator\ntakes each cluster document with its recorded digest and schema roots, recomputes every digest and\nrefuses a mismatch or a missing root before writing anything. It supports single-document closures\nonly, so a command whose closure spans several documents refuses. The generated module imports\nAjv's runtime helpers (`ajv/dist/runtime/*.js`), so the host's bundler must resolve `ajv` from\nwhere the host writes that module.\n\n`openBrowserManagerCaller` opens a Manager caller for ONE instance the person selected; there is no\nimplicit selection. It takes the broker's websocket URL, the space, the principal and lifecycle the\nbearer is bound to, the instance, and the Manager's contracts generated from\n`managerBrowserContractSource()` (`@cotal-ai/manager`). Authority comes from an `acquire` callback\nthat returns the person's own `manager-caller` bearer and the space's sentinel credential; an\nembedding's backend obtains the bearer from the stock exchange with the person's IdP proof. The\nbearer's space, principal, lifecycle and instance are checked before anything is dialled, and the\ndescribe is pinned to that instance. The view carries only the commands the person's ledger scope\ngrants. `spawn`, for example, carries `spawn`, `despawn`, `inspect`, the persona commands and\n`goal-result`, while operator reads such as `status` and `models` come only with `admin`; the broker\nrefuses a command the view does not carry. The connection never reconnects on its own; `reopen`\nacquires again and re-checks every pin.\n\n`followGoal` submits a goal command once under an envelope id the host persisted before calling\nit, and follows the goal to its terminal with the stock goal follow. Live progress settles the goal\nonly when it comes from the instance and epoch that accepted it; any other terminal settles nothing\nand prompts a read of the goal's own record. `reconcileGoal` reads an\naccepted goal back through the Manager's `goal-result` command, and `verifyGoalResultFact` checks\nthe returned fact, including its outcome digest, through Web Crypto. Nothing here retries. Three\noutcomes stay distinct: an error reply is a refusal; a goal accepted but not seen to finish answers\noutcome `unknown` and is read back, never submitted again; a request no instance answered throws\nthe stock unanswered error (`unansweredRequest`).\n\n`POST /manager-instances` on the auth service lists the signed-in person's own Manager instances. It\ntakes `{ idpToken }`, verifies it as the human exchange does and answers\n`{ v: 1, owner, complete: true, instances: [{ instanceId, placement, state }] }`. `placement` is\n`remote` or `local`; `state` is the verdict the `manager-caller` exchange selects on (`candidate`,\n`not open`, `not registered`, `not this owner's`, `unknown`). A `candidate` is registration\nevidence, not proof that the instance answers; a describe pinned to it is. The route refuses\nbrowser-origin requests like every exchange route, so a page reaches it through its own backend,\nand a failed gate read answers 503, never an empty list.\n\n### Remote manager client composition\n\n`@cotal-ai/manager` exports the `remoteManagerClient` namespace, containing the stock remote\nrequest builders and response validators, and `registerRemoteManagerAuthority` for registration\nwith a host-issued prepare credential. It returns the process epoch and registration revision its\nown registration committed. When a later start of the same instance registers before this start\nauthorizes its serve grant, this start is refused with `expired`.\n`RemoteManagerIdentityState` describes the five private\nmanager identities stored under an explicit account-local root. Use these public exports when\ncomposing `ManagerOptions.remoteAuthority`; do not copy CLI validators or import private modules.\n`managerClusterArtifacts()` returns the canonical document, manifest and their digests used by\nregistration. Pass its `[document, manifest]` pair as `contractArtifacts` to both\n`remoteManagerRegistrationProof(owner, state, contractArtifacts)` from `@cotal-ai/core` and\n`remoteManagerClient.remoteManagerAuthorityRequest(state, actor, \"activate\", { registrationProof, contractArtifacts })`.\nThe request builder takes each operation's coordinates as named fields of its last argument.\nThe host still validates the artifact closure and current registration before activation.\n`managerBrowserContractSource()` returns the same document with its closure digest and each\ndistinct schema root, the input `generateBrowserContracts` from\n`@cotal-ai/core/browser-validators-build` takes at a host's build. Its closure manifests are left\nout because they are not schemas; the generator recomputes every digest and refuses a mismatch.\nThe namespace includes closed standing/run renewal, admission, maintenance, enrollment,\nretirement and goal-index scan helpers. `publicIdentities(state)` returns the `identities` record every request\ncarries: the five public ids, in the order the host echoes them. `remoteRunHosting` builds the four `runHosting` callbacks from the registration\nand the transport you supply for the host's run admission, run attempt and authority requests, so\nyour manager sends the run requests the stock manager sends. The namespace provides no signer or\nnew grant. The host still owns authenticated issuance, current registration and activated-run\nobservations, and any guarded foreign-holder repair.\nAn embedding must preserve those checks and supply a supported runtime; the client exports alone\ndo not provide a pooled runtime, an authority service or a complete hosted context.\n\n### Long-lived endpoints take a bearer function\n\n`EndpointOptions.bearer` accepts either a string or a function, and the difference is not stylistic.\nA string is minted once, so when it expires (which it will: callout bearers live minutes) the\nendpoint has nothing to renew with. It will not present the dead token to the broker, since that is\na guaranteed denial that still costs a full auth-callout round trip. It refuses to reconnect, emits\n`warning` saying which case it is in, and retries on a widening backoff until the process\nre-authenticates and rebuilds it. Retry notices use `warning` rather than `error` because Node\nrethrows an unhandled `error` event and would kill a host the endpoint is still trying to recover.\n\nPass a **function** for anything that outlives one bearer. That is a renewal source: it is called\nahead of each expiry and again whenever a reconnect finds the cached bearer dead, and it requires\nexplicit `card.owner` and `card.actor`. The first-party surfaces already do this\n(`UserViewAuth.source`, the connector's `agentBearerCommand`). A string bearer is for a short\none-shot connection.\n\nA `MeshAgent` a host runs in its own process takes the same kind of source. Pass\n`userAuth.bearer`, a function returning a fresh bearer, instead of `userAuth.bearerCmd`, the argv a\nlaunched seat execs. Such an agent has no manager-call bearer, so its spawn, despawn, persona and\nrun calls return a refusal without reaching a manager.\n\nLong-lived hosts must also subscribe to the endpoint's `warning` event. It carries conditions the\nendpoint is surviving, including failed credential renewal, reconnect retries, a durable leave\nit keeps retrying after the broker refuses a durable channel's live subscription, and a channel\nregistry entry that does not decode to a valid config, such as a raw put of an unknown delivery\nclass or of `null` on the defaults key. The endpoint drops that entry from its cache and applies the\ndefault policy until a valid entry is written. A host may choose\nto ignore warnings for a one-shot endpoint whose awaited operation owns the verdict, but that choice\nshould be explicit. An unhandled warning is nonfatal and silent.\n\nThe `error` event carries a fault the endpoint cannot return from a call, such as a refused\nsubscription or a refused publish that no request was waiting on. Attach a listener before `start()`,\nsince Node throws on an unhandled `error`. A denial the broker returns to a request, such as an\nobserver's read of the DM stream, reaches only that call, which decides what it means, and is not\nemitted again as an `error`.\n\n## Booting the daemons\n\n### auth-service\n\n`runAuthService(args, store?)` reads its provisioned long-lived secret kinds (service keys, callout\naccount, issuer keys, owner secret) through the injected `SecretStore`; a host provisions those into\nthe store first. It is a **signer and identity authority**, not a scoped daemon: at runtime it holds\nthe data-account and callout-account signing seeds, the issuer's private JWKs, and the\nowner-derivation secret in process memory (`SecretStore.get` exports raw values). The IdP pin and the\nactor ledger are **not** store-injected: `runAuthService` resolves them under\n`userAuthStateDir(findCotalRoot(), space)`, a path relative to the process working directory, so a\nhost provisions those into that exact directory (neither `store` nor `COTAL_HOME` selects it). It\nalso writes an ephemeral `auth-service.json` discovery file there that carries the live exchange\ncapability. That file appears only after every plane is bound, so waiting on it is the readiness\nsignal: a host that also passes the daemon's pid to the provider's `ready()` gets a process-bound\nwait. The wait extends past the base timeout while that pid is alive, up to a fixed bound, and it\nends at once when the pid exits.\n\n```ts\nimport { runAuthService } from \"@cotal-ai/auth\";\n// store implements SecretStore over your secret backend; get() returns raw seeds into memory.\n// Provision the auth secret kinds into the store, AND the IdP pin + actor ledger under\n// userAuthStateDir(findCotalRoot(), space), before this call.\nawait runAuthService(\n { values: { space, server: brokerUrl, port: \"8081\" }, positionals: [], raw: [] },\n store,\n);\n```\n\n### delivery\n\n`runDelivery(args, store?)` runs from a **pre-minted scoped `delivery` cred** and never loads the\nsigner. Provide the cred either through the injected store (under\n`deliveryCredsKey(space, { injected: true })`) or with a\n`--creds` file; the two are mutually exclusive. The daemon re-fetches the cred from the store at 75%\nof its JWT lifetime and fails loud rather than riding to expiry, so **something must re-sign a fresh\ncred into that same store**. When that read finds the previous generation still there, the daemon\nreports the missed remint and retries in 60 seconds. The current cred stays live until its expiry,\nand the store is read once per retry rather than once per second.\n\n```ts\nimport { runDelivery } from \"@cotal-ai/delivery\";\nawait runDelivery({ values: { space, server: brokerUrl }, positionals: [], raw: [] }, store);\n```\n\nThat renewal is a **signer** operation, not the delivery daemon's:\n`remintDaemonCreds(root, space, store?, { preflight? })` (`@cotal-ai/workspace`) reads the `SpaceAuth`\nsigner **through the same resolved `store`** (`getSpaceAuth(store ?? workspaceSecretStore(root), space)`,\nkeys `auth/broker.json` + `auth/account.<key>.json`; the pre-split `auth/auth.json` monolith is\nmigration input and the container signer mount only) and re-signs the daemon creds (`delivery.creds` and the membership feed's\n`membership-rw.creds`) back into that store. The injected `store` is both the signer source and the\ncredential destination, never a split. `space` is **required** and validated against the store's signer, so a\nstore swapped to a different space cannot re-sign over the wrong broker's creds. `preflight` is a\ncaller-supplied proof that the broker accepts the credential. The reference `Manager` passes a\n`probeConnect` over its `servers`. It gates **every** candidate before overwriting the last-good,\nwhether the signer is a full bundle or a stripped projection: a bundle's JWT chain proves only that\nit is self-consistent and\nnamed the space, NOT that its account is the broker's *current* account for that space (two\n`createSpaceAuth(space)` calls yield same-named, different-account chains), so a same-label alternate\nsigner would otherwise mint a broker-dead cred and clobber the good one. The offline local repair (`doctor auth --fix`) has no preflight. It permits the overwrite only\nunder **authority continuity**: the candidate must be signed by the same account signing key (`iss`) as the current\n(already broker-accepted) cred. A same-label alternate account breaks continuity and is refused, full or\nstripped; a legitimate local re-sign is continuous and proceeds without a network. The reference\n`Manager` runs it on a schedule against its **own**\n`secretStore` (see below), so passing the manager and the delivery daemon the *same* store closes the\nrenewal loop end-to-end on an injected backend: the manager reads the signer from the store, re-signs\ninto it, and the daemon adopts each generation on a preflight-proven 75% timer. The stock\ncross-host composition cannot satisfy that by writing one filesystem and fingerprinting another:\n`Manager.start()` and every later remint challenge the daemon's `reloadStoreIdentity` and a\ndivergent pair is refused naming both stores. The identity is the store the daemon actually\nreloads: an injected coordinate, the workstation root only when `--creds` is\n`<root>/.cotal/<spaceSegment(space)>/delivery.creds` (matching the canonical arm), the\nfile's own directory for any other `--creds` path, or the workstation root. A filesystem\nstore is also named by a random id it records in `store.id` inside its own directory, so two\nhosts that use the same root path are two stores. No key and no `--creds` file may be that\nfile under any name, and a `store.id` that is a symbolic link or holds anything but a lowercase UUID is refused. Uninjected\n`--creds` that names one real workstation while process cwd resolves another is refused\nat start, naming both, because membership-rw still uses `findCotalRoot`. A `--creds`\npath that is not under any `.cotal` tree is not that case and is not refused here. It never\nwalks ancestors with `findCotalRoot`. No bound daemon is not a named\nstore, so start proceeds; a later daemon on a foreign store is refused on the next remint.\nThe first-party filesystem adapter declares its workspace-root identity on the store itself. Other\ninjected adapters declare their stable coordinate on `SecretStore.identity`, or name it in\n`COTAL_SECRET_STORE` on both processes. It never throws: it\nreturns per-file results (`skipped: \"no-auth\"` when the store holds no signer records),\nso the caller must check them or the cred still rides to expiry. A composition whose signer lives in\nKMS/Vault simply injects that store; no bespoke renewal is needed. A `--creds` file path must be\nreplaced atomically before the 75% read. The signer can now be injected behind the store seam, which\nresolves custody. The remaining hosted gap is signer **isolation**. The seed is still decrypted\nin-process at the manager's uid, so it needs an OS sandbox or remote signer.\n\n### Supervisor signing authority\n\n`@cotal-ai/manager` exports the `Manager` class; there is **no** `runSupervise(opts)` runner. The\nprivate CLI `runManager` also does broker-reachability checks, space/default resolution,\nroster/launch parsing and materialization, installed-extension resolution, signal handling, staged\npre-spawn, and the forever wait. A host composes that lifecycle itself around `Manager`:\n\n```ts\nimport { Manager } from \"@cotal-ai/manager\";\nconst mgr = new Manager({ space, servers: brokerUrl, workspaceRoot });\nawait mgr.start(); // then wire your own SIGINT/SIGTERM -> mgr.stop()\n```\n\n`stop()` runs once. A later call joins the stop in progress and settles with it, and a call that\nasks for a different `withAgents` than the running stop is refused.\n\nUnlike delivery, the manager is **not** a pre-minted-scoped-cred daemon (auth-service is also a\nsigner: it holds fewer artifacts than the full trust bundle, but its data-account signing seed still\ngrants complete data-account mint authority on compromise, so this is not least-privilege). On `start()`\nthe manager reads its space's full trust chain **through its `secretStore`** (`getSpaceAuth(this.secrets,\nthis.space)`, composed from `auth/broker.json` + `auth/account.<key>.json`; a container may instead\nmount a stripped signer bundle at the legacy `auth/auth.json` key) and **self-mints** its supervisor cred and renewals from the\ndata-account signing seed. In static mode it also mints every per-agent cred from that seed; in user\nmode agents instead receive callout-minted bearers, but the manager still holds the signing seed for\nits own creds and renewal. So a hosted supervisor is a **trusted per-tenant account-signer process**,\nnot a least-privilege connect client. It additionally requires a `~/.cotal/meshes/space.<key>.json`\nregistry record and the workspace user-auth marker to start in user mode. `ManagerOptions.secretStore`\ninjects the one `SecretStore` the manager uses for **the signer itself (the split trust\nrecords)**, daemon-credential renewal (`remintDaemonCreds`), and per-agent secrets,\ndefaulting to the workspace filesystem store; pass the delivery daemon the *same* store for end-to-end\nhosted renewal. The store declares the same identity on both processes, or both set\n`COTAL_SECRET_STORE` to the same coordinate. The manager\nremints no daemon credential when the daemon names a different store, including a daemon that binds\nafter start; it keeps running and serving its own agents, so one space can carry a manager on more\nthan one workspace root. That manager also stays off the space's renewal lease, so a manager or a\n`cotal doctor auth --fix` on the daemon's store can still take it. Pointing several managers at one coordinate is safe: the store identity\nalone cannot pick an owner (it carries no holder and no tiebreak, so every manager sharing the store\nmatches), so the manager that also holds the space's renewal lease is the one that remints and the\nrest skip it. Without that lease two owners would remint on independent timers with no ordering\nbetween them, and one write would land between the other's re-sign and its fingerprint-only\n`reloadCreds`. `cotal doctor auth --fix` takes the same lease before it re-signs, so a live manager\nand a local repair never race each other either. The signer IS now injectable: a hosted composition injects a KMS/Vault store and no\nsigning seed lands on the hosted disk. What remains is signer **isolation**. The seed is decrypted\nin-process at the manager's uid. That issue needs an OS sandbox or remote signer; it is no longer a\ncustody problem. The other knobs are `workspaceRoot` and the process-global `COTAL_HOME`.\n\nA store, runtime or extension the manager calls may reject with any value, including `null`. The\nmanager logs or refuses with that value's `message`, or with the value itself as text when it has\nnone, and keeps serving. A value it cannot read reports as `an unreadable rejection`.\n\n> Scope note: the **static-auth** operator paths (`cotal spawn`/`join`/`status`/`web`, via\n> `mesh-target` \u2192 `connect`/`preflight`) still read the signer from the local split records (sync\n> `loadSpaceAuth`). That is the single-machine composition, where the signer is on local disk by the\n> static-auth model; multi-tenant hosting runs **user mode**, which never mints from on-disk trust. The\n> store-injectable signer path is the hosted-server set: the manager, `remintDaemonCreds`, and delivery.\n\nThe typed remote-manager authority contract includes a one-shot terminal phase. A host implements\n`remoteAuthority.prepareAgentRetirement` to revoke the managed grant and finish its resumable\nrelease while preserving the UID, then `remoteAuthority.mintRetirementRequester` returns the\nhost-signed JWT for a fresh participant-owned nkey. The credential is pinned to the authenticated\nowner, server-derived manager serve principal, current instance epoch, and exact target lifecycle.\nThe manager then uses the existing auth `retireLifecycle` rail with the operation id derived by\n`managedRetirementOpId(target.lifecycleUid)`. This derivation is the reference remote-Manager\ncomposition's closed contract, not a rule for every retirement entry point; interactive retirement\nkeeps its existing operation identity and remains compatible. The `retireLifecycle` rail independently\nrecomputes the managed id from its broker-pinned target before any gate, head, intent, or barrier\naccess, so mint-time validation is not the terminal boundary. A failure keeps\nthe alias held. This does not expose the auth barrier or give the participant signer authority.\n\nIf the participant disappears after prepare, the host finishes the retirement itself on the auth\nservice's loopback face: `POST /managed-lifecycle/retire` (exported as `MANAGED_RETIRE_PATH` from\n`@cotal-ai/auth`) with the `Bearer <cap>` from `auth-service.json` and only\n`{ owner, actor, lifecycleUid }`. It has the interactive door's guards (POST only, no `Origin`, JSON,\ncapability, closed body) and is never served on the public face. The managed grant must already be\nrevoked at that uid, or it answers 409. It runs the same `managedRetirementOpId(uid)` operation as\nthe rail, and the rail and the door share one in-process flight, so a late participant request and\nthe host call converge on one barrier.\n\n| lifecycle head | answer |\n| --- | --- |\n| absent, or `retired` at another uid | `200 { retired: false, lifecycleUid, notStarted: true }` |\n| `active`/`retiring` at another uid | `409` |\n| `retired` at this uid | `200 { retired: true, lifecycleUid, alreadyRetired: true }` |\n| `active`/`retiring` at this uid | the barrier runs, then `200 { retired: true, lifecycleUid }` |\n\nDeprovisioning durables stays with `deprovisionAgent` and a `deprovisioner` credential. Pass `memberChannels` to both to also purge the retired lifecycle's durable membership rows on those concrete channels. The manager fills that list from the launch's concrete read channels and the delivery daemon's read-only `lifecycleMemberships` admin verb. When that verb cannot answer, rows on other channels stay retained, the teardown logs the inventory as incomplete, and retirement remains held pending retry rather than releasing the alias. Each teardown examines two exact consumers, one ACL key and the named member keys. KV deletion uses a native revision condition; a lost condition with a live replacement refuses rather than claiming absence. Consumer INFO checks before and after DELETE distinguish verified prior absence from disappearance. `acknowledged` counts native DELETE success replies. `disappeared` counts observed live-to-absent consumers and live KV rows whose conditional purge lost to a competing deletion. These KV rows were present at the first read, so they never count as prior `absent` or as this caller's `deleted`. The ACL and membership subtotals preserve that distinction. Neither establishes which concurrent caller uniquely removed a consumer, so `consumers.deleted` and the total `deleted` are `null` when a consumer disappears without a winner token. A repeat after verified absence reports zero, not an invented deletion. `refused` counts slots whose cleanup or state remains uncertain. A partial failure raises `DeprovisionError` carrying these bounded observations. The Manager's static sweep preserves unknown uniqueness rather than adding acknowledged requests as physical removals; its slot totals remain separate.\n\nA host that resumes retained managed actors also implements\n`remoteAuthority.validateRetainedAgent`. The participant sends back the actor token and sentinel it\nalready holds, plus the `nextRegistrationProof` returned by the activation response. That proof is\nhost-issued after registration and binds the manager owner, actor, lifecycle, identity nkeys, current\nregistration revision, and serving epoch. The host checks it against the current open manager gate,\nvalidates the retained secrets against its current managed row, and returns only the non-secret\nauthority shape. The manager binds every result coordinate and the returned authority back to its\ninventory before use. Do not copy the provider's `issuer.json` or `callout.json` into the participant\nstore. Both contain private signing or exchange authority.\n\nThe same composition supplies `remoteAuthority.agentBearerExchangeUrl`, the pinned public auth-service\nbase used by retained children. Remote adoption launches `agent-bearer --exchange-url <base>`; it must\nnot select the local `--dir` arm, which depends on a host-only auth-service process record.\n\nA host that lets a remote participant spawn FRESH managed agents implements\n`remoteAuthority.enrollManagedAgent`. The participant generates the standing actor token, writes it\nat mode 0600, and passes only its SHA-256 digest with the requested actor, label, role,\ncapabilities, and channel lists, so the plaintext secret never leaves the participant machine. There\nis deliberately no `lifecycleUid` input: the host selects the UID, because only the host sees the\nretirement tombstones that make a UID permanently unusable, and a participant-chosen UID could aim a\nfresh grant at a dead incarnation. The host authors the ledger grant, pre-creates the lifecycle-keyed\ndurables, clamps the requested lists to what the spawning owner already holds, and returns the owner,\nactor, chosen `lifecycleUid`, the space sentinel credentials, the effective lists, and\n`agentBearerExchangeUrl`. The manager binds every returned coordinate, re-keys the secret family onto\nthe returned UID, and launches `agent-bearer --exchange-url <base>`. When the hook is absent a\nsignerless manager refuses the user-mode spawn rather than authoring a local grant the host knows\nnothing about.\n\nBoth managed-agent operations ride the one verified `POST /manager-service-authority` transport as\n`kind: \"manager-managed-agent-enrollment\"` and `kind: \"manager-managed-agent-prepare-retirement\"`.\nStock `cotal auth-service` answers both itself, because it owns the actor ledger and the space's\nprovisioning authority. An enrollment writes the managed grant at a fresh UID with the supervising\nactor as its parent, provisions that UID's durables, and returns the daemon's public exchange URL as\n`agentBearerExchangeUrl`; a daemon started without `--exchange-public-port` refuses enrollment. A\nretry with the same token digest answers the same UID while the supervising actor's current grant\ncovers it, and a fresh enrollment's refusal otherwise. While the agent's grant stands, an enrollment\nwith another digest is refused with `conflict` until that lifecycle's retirement is prepared. A\nprepare-retirement releases the target UID's broker footprint and then revokes its grant, so the\nmanager's terminal rail finds the grant gone. A platform that keeps these writers in its own storage\nintercepts both kinds instead. It terminates its own public route, authenticates the human there,\nand asks the auth service for the decision at\n`POST /manager-service-authority/verify-enrollment` (exported as `VERIFY_ENROLLMENT_PATH` from\n`@cotal-ai/auth`) with the `Bearer <cap>` from `auth-service.json` and only `{ owner, request }`. That\ndoor has the managed retirement door's guards, derives the caller's scope from the local ledger rather\nthan the body, checks the manager gate and registration proof in-process, and answers\n`{ authorized: true, owner, actor, instanceId, serveEpoch }`. `authorizeRemoteManagedAgentEnrollment`\nand `authorizeRemoteManagedAgentPrepareRetirement` are exported too, for a host that composes the\ndecision without the HTTP hop. Both require ledger scope `supervise`; `spawn` and `admin` do not\nimply it.\n\nA host that runs managed agents on its own hosted runtime adds two more kinds on the same transport.\n`kind: \"manager-managed-agent-runtime-create\"` asks the host to create the runtime for one agent it\nalready enrolled, and `kind: \"manager-managed-agent-runtime-status\"` reads that runtime's state. Both\ncarry the manager envelope plus `target: { owner, actor, lifecycleUid }`, the coordinate the\nenrollment returned. Both schemas are closed. An unknown top-level or target field, including\n`providerRef`, `handle`, or `name`, is refused as `bad-request`, because the host alone issues and\nholds provider references. There is no stop, adopt, or probe kind: stop goes through\nprepare-retirement. Stock dispatch refuses both kinds with `unimplemented`, and the verify-enrollment\ndoor decides them. `authorizeRemoteManagedAgentRuntimeCreate` and\n`authorizeRemoteManagedAgentRuntimeStatus` apply the enrollment door's checks: host space, a target\nowner equal to the authenticated owner, the manager actor's own ledger row with `supervise`, the open\ngate, the current serve epoch, and the registration proof. Each returns only\n`{ owner, instanceId, actor, target }`. The door touches no provider and writes nothing. The host\nmatches the decision to its own intent record and performs the create afterwards. The host answers\nwith `state` (`reserved`, `creating`, `bound`, `create-unknown`, `closing`, or `closed`), `readiness`\n(`ready`, `bound-not-ready`, or `none`), and an optional `retirementPhase`. A manager builds requests\nwith `remoteManagerClient.remoteManagedAgentRuntimeRequest` and binds the answer with\n`remoteManagedAgentRuntimeState`.\n\nAn enrollment result may also carry `runtimeIntent: { state: \"reserved\" }` when the host reserved a\nhosted runtime for the agent. It is display-only. Older hosts omit it, the manager binds both shapes\nto the same material, and nothing reads it as authority.\n\nNo stock door lets a platform control holder launch or retire an agent for a signed-in user. The\nmanaged-agent kinds above act only under the authenticated owner and refuse a caller that is not\nthat user, so a platform could only run a user's agent by holding the user's login or by enrolling\nthe agent under its own owner. Both are refused. The\n[delegated user launch intent](design/delegated-user-launch-intent.md)\ndesign and SPEC \xA713.16 define the smallest addition. The user admits one launch or one retirement\non the host's authenticated route. The holder consumes that intent once, from its current\nregistration, epoch and lifecycle. The host then enrolls the agent under the user's `u_` owner with\nthe user's own actor as its ledger parent, so the envelope walk, membership and channel lists match\nwhat the user's own manager would produce. Retirement keeps the prepare, provider closure and\nterminal barrier order, and the host finishes it when the holder is gone. A launch the host had to\nundo keeps its agent name held until the host process that ran it confirms it has stopped, and\nwhile the name is held the host also refuses it to the user's own manager. `@cotal-ai/auth` ships the\ntwo decisions, `authorizeDelegatedUserIntentAdmission` and `authorizeDelegatedUserIntentExecution`,\nfor a host that owns an intent store and those writers to compose on its own routes. Both read the\nholder's gate through the handle's `observeManagerGate`, present with `platformControl`, which reads\nover the context's own connection, so the host opens no second data-account connection. It is an\nobservation for the decision: the consuming CAS and the writers still apply their own checks. The\nconsuming CAS pins the incarnation of the host process that runs the flight as the executor, never\nthe auth plane's, because the two can restart independently. `platformControl.host` names that\nprocess's reverse-DNS endpoint, the closure digest of its \xA713.7 cluster and the contract artifacts\nregistration reads, and the auth plane self-authorizes that one name. Registration reads the closure\nmanifest `{ v: 1, root, members }` at `clusterDigest`, then the cluster document at the manifest's\n`root`, and verifies each against its digest. `artifacts` therefore carries both, and `clusterDigest`\nis the digest of the manifest. `members` stays empty: SPEC \xA713.7 lists every reachable artifact\nthere, but this implementation registers single-document clusters only and refuses a manifest that\nlists members. A manifest with any field beyond `v`, `root` and `members` is refused too, as the\ncontract store refuses it. `singleDocumentClosure(document)` returns that manifest and its closure\ndigest. The instance id is a lifecycle token, `[a-z0-9]{26,32}`. The minimal construction below has\none command over the void schema. A host copies it, replaces `document` with its real cluster, and\npasses `host` as `platformControl: { observeAssignment, host }`.\n\n```ts\nimport { mintLifecycleUid, singleDocumentClosure, VOID_SCHEMA_DIGEST } from \"@cotal-ai/core\";\n\nconst document = {\n urn: \"com.example.host\",\n revision: 1,\n attributes: [],\n events: [],\n commands: [{\n name: \"ping\", class: \"ephemeral\", targeted: false, capability: \"host.ping\",\n inputDigest: VOID_SCHEMA_DIGEST, outputDigest: VOID_SCHEMA_DIGEST,\n }],\n};\nconst { manifest, closureDigest } = singleDocumentClosure(document);\nconst host = { endpoint: \"com.example.host\", clusterDigest: closureDigest, artifacts: [document, manifest] };\nconst instanceId = mintLifecycleUid(); // first start only; later starts reuse the persisted id\n```\n\n`registerHostIncarnation(instanceId)` publishes the artifacts, registers that instance through the\nceremony the plane runs for itself, and returns `{ instanceId, processEpoch }` with the epoch that\nregistration committed. The host calls it at every start with its persisted instance id, before it\nadmits or recovers any flight, so a restart fences its predecessor. The first registration of an\ninstance commits epoch 0, which is open and serving like any later epoch, and each later start of\nthat instance commits the previous epoch plus one. A consumer compares epochs for equality and never\nreads 0 as absent or not ready. A start\nwhose confirming read of the gate finds that a later start of the same instance registered is\nrefused with `conflict`. The returned epoch is a committed coordinate and stays current only until\nthe next start registers, which can happen before the call returns. `observeHostGate(instanceId)`\nis a point-in-time read of an executor's gate, and answers null for an absent gate; a sweeper\ndecides on it. `awaitHostFence(instanceId, processEpoch)` resolves with the gate once it is no\nlonger open at that epoch, and with null once it is absent. It takes any non-negative safe integer\nepoch, 0 included, and refuses any other value with `bad-request`. The host arms it with its returned\nincarnation before it admits or recovers any flight, and stops serving when it resolves. It polls\nthe gate, because no runtime credential may watch the auth bucket. The\nhost's launch writer first activates the agent's lifecycle at the pinned UID through the handle's\n`activateManagedLifecycle`, before any ledger row or durable, and its compensation runs the same\ncall before the terminal barrier, so a launch whose agent never exchanged its bearer still reaches\nthe terminal barrier at that UID. The launched agent exchanges its bearer on the context's public\nface, so the host starts it with `publicFace`, and its retirement writer ends at\n`POST /managed-lifecycle/retire` with the handle's `cap`. Stock dispatch refuses both kinds as\n`unimplemented`. The holder's composition passes\n`remoteAuthority.executeDelegatedUserIntent`, which posts the execution request and binds the answer\nwith `parseRemoteDelegatedUserIntentExecutionResult`. It then starts the agent with\n`startAgent({ ..., delegatedIntent: { intentId, owner, parent } })` and retires it with\n`retireDelegatedAgent(name, intentId)`, which stops the agent and removes its local secret family\nonly after the host confirms `retired: true` for its exact target. A delegated agent's stop, exit or\nfailed launch keeps its name held until that retirement confirms, and a secret file the retirement\nfails to remove keeps it held too.\n\nRemote user-mode managers must also supply `remoteAuthority.authorizeAdmin`. The manager builds each\nrequest only from the caller tuple parsed from the broker-authenticated endpoint subject, then relays\nthat tuple over the current registered manager lifecycle. HTTPS does not separately authenticate the\nrelayed caller. The host authenticates the manager operator, binds the request to the current open\nmanager gate, registration proof, serving epoch, and identity nkeys, then reads the caller's unified\nauthoritative row fresh. It returns only the manager owner and `authorized: boolean`, with every request\ncoordinate echoed. Missing, revoked, narrowed, foreign-owner, and stale-lifecycle callers all return\n`false`; malformed coordinates or corrupt and unavailable authority state fail the operation. The\nparticipant never reads or mirrors the host ledger, and the remote branch has no local fallback. The\nsame callback gates all `manager.admin` handlers, any-mode cross-owner control, and `ps` or `inspect`\ncross-owner visibility. Launch keeps its owner-equality policy.\n\nThe remote authority's instance executor remains the scoped maintenance credential for clean service\nderegistration and exact instance registration operations. It carries no records-stream consumer\nlifecycle authority. The manager's boot `goalidx` sweep uses the authenticated host operation, which\nreturns parsed `goalidx.manager.<owner>.>` entries for that owner only. The host keeps the sealed\nconsumer connection and its create/delete rights. The five-minute executor already renews through\n`remoteAuthority.renewExecutor`. The signerless supervisor, serve, goal-writer, session-ledger and\nper-run driver credentials do not yet have a complete remote renewal and adoption path. The\n[hosted runtime contract](design/hosted-runtime-contracts.md) records the bounded additions and\ntheir ownership; it is not a shipped pooled service.\n\n**Signer isolation needs an OS sandbox.** The default pty runtime\nruns agent children under the *same* OS uid and the *same* `workspaceRoot`, so mode-0600 on\nthe trust records does not stop a hostile same-uid agent from reading their absolute paths. The reference\n[deploy](deploy.md) tree does not solve this: it mounts the signer into the agent's own container, so\nits phase-1 boundary isolates agents from each other, not the signer from the agent. A hosted\ncomposition must run the manager/minter that holds the signer in a different uid, container, or mount\nnamespace from the agent children, which mount no signer at all; that split is future\nhosted-composition work, so until it (or a remote/injected minter) exists, do not run untrusted\nagents under this manager.\n\n### Delegated seats outside the manager's filesystem\n\nThe [portable lifecycle bootstrap](design/portable-lifecycle-bootstrap.md)\ndesign and SPEC \xA713.17 define how a managed agent that `enrollManagedAgent` already enrolled starts\nin a child that cannot see the manager's filesystem. The ordinary `spawn` path writes the token and\nsentinel under the manager's workspace root and hands the runtime a launch whose bearer command and\nmaterial file are paths on that filesystem, so such a child needs this path instead.\n\nThe delegation boundary is one optional runtime method. A runtime that implements\n`Runtime.spawnDelegated(launch, handoff)` receives two values and no paths: a `DelegatedSeatLaunch`\n(connector name, persona text, model and the other launch choices) and a `ManagedLifecycleHandoff`\n(space, owner, actor, the host-chosen `lifecycleUid`, broker and IdP pins, the pinned exchange base,\nthe sentinel, the channel lists, and the raw actor token). The manager enrolls once, as it does\ntoday, and builds the handoff from what it holds. It never sends the token to the host, never copies\na file from its workspace or secret store, and never builds a local launch for that seat. No signer,\nissuer or callout record, loopback capability, provisioner or manager credential, control token, or\nmanager path crosses. A spawn choice that only the manager's host can honour is refused before\nenrollment: `--resume`, a manifest agent's `continuity: exact`, `--cwd`, and any shared MCP server,\nwhether from `--share-tools` or the config default (`--share-tools none` passes).\n\nA delegated manager also refuses a caller-supplied persona `config` override in `resolve-agent`,\n`spawn` and direct `Manager.startAgent` calls. The shared persona resolver refuses before checking\nor reading that path, with `failed-precondition` on the wire and one fixed execution-mode message.\nPass a named catalog persona instead. This does not change locally executing managers. A resolved\nmanifest can still use its own validated, host-materialized persona resource. The wire schemas\naccept no internal `resolved` field that could bypass the config refusal.\n\nThe runtime creates one provider resource under `managedRuntimeKey(target)`, writes the handoff into\nit as one 0600 file and the persona beside it, and runs the stock bootstrap there:\n`cotal spawn --config <persona-file> --space <space> --name <actor> --expect-owner <owner> --expect-lifecycle-uid <uid>`\nwith `COTAL_MANAGED_HANDOFF_FILE` naming the file. `delegatedSeatCommand` builds that argv. The\n`cotal` entry reads the file into memory, deletes it and drops the variable before it parses flags,\nprints help or loads extensions, so every outcome, a refusal of its own flags included, leaves no\nfile. It refuses before any broker connection or exchange request when the\nspace, owner, actor, or lifecycle UID differ from the expected values, and then runs the\nenrollment-redeem consumer: it registers the mesh in its own home, writes the token to its own 0600\nfile, and exchanges it through `agent-bearer --exchange-url` unchanged. It never enrolls, redeems, or\nmints a token or UID.\n\nReadiness is still mesh presence. A create whose answer is lost leaves the handle running, so the\nlaunch settles uncertain and stays held; the manager never retries it. A provider read that finds no\nresource under the key is not an exit, because the create may still land. Every close by\n`managedRuntimeKey` is fenced: it completes only once the create was answered or the provider refuses\nany later create under the key. A provider that names its own resources may run the create as a\ndurable operation keyed by `managedRuntimeKey` and close through the identifier its authenticated\ncreate response returned, kept where the host can read it without the manager. Only that response\nbinds an identifier to the key; one derived from the key or found by name or listing is never closed\nor adopted, and while the response is unknown the launch stays held. Every stop, the reap of a child\nwhose parent exited and `Manager.stop({ withAgents: true })` included, runs `prepareAgentRetirement`\nfor the UID-exact target, then `stop()` on the handle `spawnDelegated` returned, then the terminal\nbarrier. `preparePreservation` refuses a cut that holds a delegated seat, and a `Manager.stop()` after\na refused cut retires the seat through the same steps. After the manager is gone\nthe host runs the same steps, makes the same fenced close by `managedRuntimeKey`, and finishes at\n`MANAGED_RETIRE_PATH`. Supply `spawnDelegated` only from a runtime whose host can make that fenced\nclose without the manager. The enrollment redeem\n(`COTAL_ENROLLMENT_FILE`) stays for lifecycles whose token the host generated itself; a host cannot\nmint one for a manager-enrolled lifecycle because it holds only the digest.\n\n### Delegated recovery contracts\n\nCore exports `RemoteDelegatedRecoveryRequest`, `RemoteDelegatedRecoveryResult`,\n`RemoteDelegatedRecoveryLaunchRequest`, their closed parsers, and\n`parseDelegatedRecoveryDescriptor`. `Runtime.rebindDelegated` describes an optional same-child\nrebind. These are contracts for SPEC \xA713.18, not an enabled recovery path. The current Manager\ndoes not call the new method or reconstruct delegated rows. Its public `adoptRuntimeHandle`\nreturns a native handle without inserting a managed row.\n\nA supporting host retains the descriptor in the original user-intent consuming CAS, binds each\nclaim to the original instance and current registration, and supplies complete fenced scans.\nThe manager needs its original persistent secret store and a runtime that can verify the same\nchild execution after process loss. It must retain held aliases before reconciling accepted goals.\nMissing storage or unverified child identity keeps recovery held; neither permits another launch.\nThe request parsers validate data, not fresh host authority or durable transactions. Existing\nhosts continue to refuse the opt-in launch extension rather than silently ignore it.\n\n## Provisioning a space (one-space reference shape)\n\n```ts\nimport { createSpaceAuth, setupSpaceStreams, ensureDefaultDeliveryClass, mintCreds, newIdentity } from \"@cotal-ai/core\";\nconst auth = await createSpaceAuth(space); // trust bundle (in-memory seeds)\nconst provisionerCreds = await mintCreds(auth, newIdentity(), \"provisioner\");\nawait setupSpaceStreams({ servers: brokerUrl, space, creds: provisionerCreds });\n// SPEC section 4: write the default delivery class at space creation so it is wire-discoverable,\n// never inferred from the resolution fallback. A daemon-backed space is \"durable\".\nawait ensureDefaultDeliveryClass({ servers: brokerUrl, space, creds: provisionerCreds, deliveryClass: \"durable\" });\nconst deliveryCreds = await mintCreds(auth, newIdentity(), \"delivery\");\n// put deliveryCreds into your SecretStore under deliveryCredsKey(space, { injected: true })\n// (@cotal-ai/workspace) before booting delivery \u2014 the key is per-space, not the bare kind.\n```\n\nRendering the broker config for a user-auth space is `serverConfig(broker, spaces, { storeDir,\nmaxFileStore?, extraAccounts })`, where `extraAccounts` must include the callout account from\n`createCalloutAuth` so the auth-service has a broker account to answer on. That account never shares\nthe data account. `maxFileStore` is an optional positive integer byte cap; any other value throws.\n\nBroker trust and space accounts are separate authorities: `createBrokerAuth` mints the one\noperator + system account a broker trusts, `createSpaceAccountAuth(broker, space)` signs each\ntenant's data account under it, and `serverConfig(broker, spaces, opts)` renders them all into one\nconfig. A host composition can therefore provision several spaces on one broker today. `cotal up`\nrenders that config from every tenant the root's auth directory holds, so booting one space keeps\nthe broker trusting its siblings, and it refuses to render at all while any account record is\nunreadable. The rest of the CLI lifecycle is still broker-wide: `down`, `clean` and `backup` refuse\non a multi-space root rather than scoping to one tenant, and the per-space lifecycle is the\nremaining multi-space operator layer. See\n[Known gaps](#hosted-composition-gaps).\n\n## Hazardous provisioning primitives\n\n`mintCreds`, the full `Profile`/`CredentialKind` matrix, `createSpaceAuth`, and `stripSpaceAuth` are\nlow-level operator primitives. Handle them as account-authority material:\n\n- A holder of a `SpaceAuth` (or a `stripSpaceAuth` bundle, which **keeps** the data signing seed) is\n a fully-trusted tenant-account authority: it can mint `admin`, `provisioner`, and destructive\n profiles, not merely `supervisor`, and mint a DM-reading identity. `createSpaceAuth`'s full result\n holds operator, system, and account seeds in memory.\n- Choose `profile` and `MintOpts` from **server-side constants**, never from tenant input. `MintOpts`\n can widen the bounded TTL defaults; cap it at your boundary. `CREDENTIAL_LIFETIMES` is a policy\n record, not an authorization boundary.\n- Never log signer material or export it into env. Do not co-locate signer access with an untrusted\n connector/runtime process at the same OS uid (file permissions do not contain a same-uid reader;\n see the manager's isolation note). Segregate per tenant; rotate on compromise\n (`rotateDataAccountSigningKey`).\n\n## Hosted composition gaps\n\nThe primitives above are present as exports, but three capabilities are **not** cleanly composable\nfrom the public contract today. Each is tied to work in flight; a host either waits for the seam or\nscopes the capability out. None is a wire concern.\n\n1. **Delivery immediate live eviction and a fully-hosted membership feed.** The renewable\n `membership-rw.creds` is now a `SecretStore` kind. `startMembership` reads it through the\n injected store, and the manager re-signs it there. The graph-feed writer therefore renews on a hosted\n backend (its data connection adopts each generation on a preflight-proven 75% timer). What still\n reads from a fixed on-disk path are the *static* `membership-observer.creds` and\n `connection-evictor.creds` ($SYS creds, minted at the `up` that provisions the account and renewed by `up --rotate-sys`) and `membership.json`\n (`{accountId}`, non-secret config); those, plus the private provisioning wrapper, keep immediate\n live eviction and a fully-hosted feed a partial gap. A host that re-mints the observer into the\n injected store under the same system signer and scope has it adopted by the running feed at 75%\n of its lifetime. Missing files degrade membership to\n traffic-only and make live eviction refuse (loudly). The supported delivery contract here is the\n Plane-3 durable backstop.\n2. **Supervisor signer isolation.** `ManagerOptions.secretStore` now injects the one `SecretStore` the\n manager reads/writes every secret through, including the composed `SpaceAuth`\n signer (the split trust records), its daemon-cred renewal, and its per-agent kinds. What remains is process\n isolation: the manager still decrypts the signer in-process at its uid, so untrusted agent children\n must run under a different uid/container/mount namespace or behind a future remote signer.\n3. **Per-space lifecycle on a shared broker.** The trust layer is multi-space\n (`createBrokerAuth` + `createSpaceAccountAuth` + N-space `serverConfig`, persisted as\n `broker.json` + `account.<key>.json`) and `cotal up` renders the whole tenant list, but there is\n no per-space provisioning verb and no per-space teardown/backup/restore: the CLI's broker-wide\n lifecycle verbs refuse on a multi-space root, naming the tenants.\n This is the remaining multi-space operator layer.\n4. **A non-Better-Auth production IdP.** The exchange core (`createIdpBridge`) is EdDSA-generic, but\n the stock provider and login client are Better-Auth-endpoint-shaped, `cotalAuthProvider`\n self-registers on import (colliding with a host-owned provider under `resolveAuthProvider`), and\n the login flow speaks Better Auth's device-code endpoints. A different IdP is a host-built auth\n composition on the low-level primitives, not a configuration change (see\n [the IdP callout contract](identity-and-auth.md#the-idp-callout-contract)).\n\n## Hosted durability\n\nSpace-durable **coordination** state (chat/DM/task history, live presence, membership runtime, the\ndurable ACL registry, leases) lives in **JetStream**, written by the delivery daemon and the\nendpoints. It is broker-resident and needs no host-side durable path.\n\nWhat is **not** in JetStream, and is hosting-critical, is trust and authorization state a host must\nplace and keep:\n\n| state | class | where today | hosted injection |\n|---|---|---|---|\n| full `SpaceAuth` trust chain (`auth/broker.json` + `auth/account.<key>.json`, composed; a stripped signer bundle may instead be mounted at the legacy `auth/auth.json` key) | signing authority | `SecretStore` | `SecretStore` (manager + renewal) |\n| auth kinds: callout account/creds/xkey, issuer private keys, owner-derivation secret, data-signer projection | signing/identity authority | four `SecretStore` kinds | `SecretStore` (auth-service) |\n| auth plane instance identity (instance id + serve nkey seed) | restart identity | root `.cotal/space.<hex>/auth-instance.json` | `SecretStore` (`startAuthService`) |\n| `delivery.creds` | standing scoped cred | `SecretStore` or `--creds` | `SecretStore` (delivery) |\n| actor ledger, IdP pin | authorization + trust config | ambient `userAuthStateDir(findCotalRoot(), space)` | none (root-relative; not `store`/`COTAL_HOME`) |\n| `membership-rw.creds` | standing scoped cred | `SecretStore` | `SecretStore` (delivery + manager renewal) |\n| membership-observer / connection-evictor creds + `membership.json` | scoped $SYS creds / config | workspace filesystem | none (see gap 1) |\n| manager agent creds, actor tokens, sentinel creds | lifecycle authority | `SecretStore` | `SecretStore` (manager `secretStore`) |\n| `~/.cotal/meshes/space.<key>.json` record (holds IdP trust pins/root pointers) | non-secret, integrity-critical | machine home | process-global `COTAL_HOME` only |\n| auth-health, renewal records | non-secret diagnostics | workspace filesystem | `workspaceRoot` |\n\nThe `SpaceAuth` trust chain and the auth-service store kinds are **separate** identities/projections,\nnever parts of one document. `auth-service.json` (the live exchange capability) is ephemeral runtime\nstate, not durable, but is sensitive while the daemon runs. `@cotal-ai/workspace` is machine-local\noperator tooling by design; personas, PID files, and the `current-mesh` pointer are truly local and\nmust **not** sit on a hosted durable path. Everything classed above as an authority is what a hosted\ncomposition must provision and persist: signer-bearing server secrets now have `SecretStore` seams;\nthe remaining non-injectable rows are the explicit ambient `workspaceRoot`/cwd paths above.\n\n## See also\n\n- [Substrate stability](stability.md): what v0.3 and the 0.x packages guarantee, and the projected v0.4 break.\n- [Identity and auth](identity-and-auth.md): the profile matrix, the signer, and the IdP callout contract.\n- [Delivery daemon](delivery-daemon.md): the Plane-3 durable backstop.\n- [Deploy](deploy.md): the reference container against an external broker.\n\n## Hosted run revocation\n\nThe authenticated manager-service authority door accepts `manager-run-revoke` with the registered\nmanager envelope and `revoke: { runId, reason }`. The issuing host reads the admission's recorded\nowner and requires the verified requester to be that owner or to hold `admin` in its fresh actor\nledger row. It verifies the current manager registration proof, space, account and epoch. It accepts\nno owner or attribution from the body.\n\n`AuthAuthorityPlane.revokeManagerRun` implements the operation. `AuthProvider.requestRemoteRunRevoke`\ntransports it, and `remoteRunHosting` supplies `RunHostingContext.revokeRun` for a signerless\nmanager. The callback returns the stored `RunRevocation`, including on repeats. A host composing\nits own remote authority must supply that callback to revoke. No signer reaches the participant.\nCLI and MCP revoke verbs are a follow-up described in the [run-start design](design/user-auth-run-start.md).\n\n### Private tool-result prerequisites (not serving)\n\n`@cotal-ai/auth` exports `createToolResultRevocationCustody`,\n`recordToolResultRevocation`, `snapshotToolResultRevocations` and one-shot\n`consumeToolResultSnapshot`. These are host-only prerequisites, not material issuance,\nservice registration or a browser/private-result path. `requireToolResultClockPrerequisite`\nalways refuses `unavailable`: no native runtime clock-domain/generation/suspension proof\nexists, and no numeric delta is invented.\n\nCustody exclusively creates a **fresh** auth store after a private fsynced intent/generation.\nExisting stores or journals are never adopted. Only a standalone broker and owner-private local ext4 journal parent are supported. Only the creating process may use its sealed,\nnon-reconnecting reader/writer custody. Unknown writes, missing/changed journals, stream\nidentity/configuration drift or connection loss invalidate it. Crash/rebind recovery stays\nunavailable. Routine broker restart, upgrade, network loss or process restart therefore leaves\ngate 1 permanently unavailable for that space. Recognize this as `revocation custody lost; no reopen from current INFO`.\nNo retirement/reprovision recovery protocol exists in this increment. Gate 2 cannot assume\ncustody survives broker maintenance. The trusted composition supplies current authenticated consent/target authorizers\nand exclusively holds account/provisioning keys. Independently running privileged provisioners,\nuncontrolled legacy writers and existing roots have no qualified custody and are unsupported.\nThis is not protection from a malicious host administrator or copied signing keys.\nThe separate internal custody profile uses two non-expiring, non-reconnecting credentials,\nwithout planeCandidate or a renewal timer. With the two sealed scanners this is four standing\nconnections across three profiles. The CREATE provisioner still expires and closes after CREATE.\nA leaked custody JWT plus user seed no longer has a 900-second expiry bound. Stopping the\nservice closes its own sockets, not copied credentials. Signing-key removal invalidates copied\ncredentials as well. This remains inside the trusted-process/account-signer residual.\n\nRevocation creates only `revocation.<targetDigest>.<consentId>` with native expected-last-subject\nsequence zero, no operation/TTL headers and independently fsynced acknowledgement receipts.\nConsent bytes must match the recorded target and expiry. Persistence uncertainty reports effect\noutcome `unknown`. Acknowledged writes report `executed` even if receipt or later custody checks\nfail. Refusals before publication report `not-executed`. No automatic repeat follows uncertainty.\nMarkers persist indefinitely in this\nslice. A separate bounded-retention successor must prove consent expiry plus\n`EP_AUTH_MARKER_TTL_MS` and snapshot safety before a pruner/TTL may exist.\n\nA snapshot fixes T0 before IO and a leader W once, walks only through W, bounds count/bytes/time,\nand compares exact native sequence/subject/payload evidence to custody receipts. The committed-W\nartifact is a process-private invocation seal binding that evidence, account, generation and\nnative created/config identity. It cannot be parsed, copied, reused or used after custody loss.\nNo current INFO equality or operator safe flag establishes historical integrity. Journals are\nexclusive 0600 files with file and directory fsync, checked before/after IO, not adopted on restart.\nClustered brokers and leader failover are unsupported. Any connection loss refuses; current INFO cannot restore custody.\n\nA short-lived create-only provisioner holds API discovery and exact auth-stream CREATE, then closes.\nSeparate package-internal grants split revocation-only publish from INFO/MSG.GET reader authority.\nRetained issuance writers **already can enumerate** auth metadata. Raw ACLs cannot enforce CAS,\nDEL/TTL/schema/target conventions. Every raw MSG.GET profile carries SPEC13.9's caller-selected\nreply **foreign-subject response-injection** residual, even with only its own inbox subscription.\nRoutine PR CI runs `smoke:tool-result-prerequisites`: bounded fixtures, broker-observed\nnon-expiring exact grants, a no-renewal timer probe, census and actual KICK/key-removal\nrefusals. It does not wait for credential expiry and is not serving acceptance.\n`pnpm smoke:tool-result-custody-lifetime` additionally waits `AUTHORITY_CLIENT_TTL_SEC + 15`\nseconds and checks unchanged identities plus record/snapshot success. Run it explicitly or\nthrough the manual/weekly Custody lifetime workflow. Historical lifetime receipts remain\nsource-pinned evidence, not a grade for a composed successor.\n\n\n## Native service custody guards\n\nOrdinary lifecycle executors refuse a UID reservation carrying `kind: \"native-service-uid\"`.\nThe reservation has closed `native`, `assignment` and `creation` objects. It is not an ordinary\n`{ owner, actor, mintedBy }` audit, and a reader must not project it into one. Its assignment\nbinds the full account, space, owner, endpoint, instance and serve-duty tuple. The actor is derived\nfrom that tuple, and the creation operation and request digest remain fixed.\n\nThese guards run before ordinary root reuse, activation, gate and head changes, mint finalization,\nand native retirement. Ordinary connect and currency reads also refuse service custody, including\nrows whose head or credential mirror is missing. Existing ordinary reservation bytes are unchanged.\nClosed head, gate and credential parsers continue to reject service markers. Without\n`serviceEndpoints`, no product path writes a service reservation or mints a marked root. The service\naccess-view contract and the sealed native access/callout composition are shipped; only a context\nopened with `serviceEndpoints` composes them, with the service host's approval and registration\ndescribed below. Lifecycle retirement and restore remain unwired.\n\nThe package-internal service prerequisite reader is separate from ordinary connect and lifecycle\ncurrency readers. It leader-reads immutable UID custody, the exact active service head, native\n`gate.<uid>` open at generation1, and the exact active root credential. All three custody mirrors\nmust match the full reservation digest. Service head `managerInstance` equals immutable assignment\n`instanceId`, and native `processEpoch` is fixed at1. These are not ordinary enrollment coordinates\nor the separate endpoint gate/witness registration epochs. Creating/cancelling heads and their\nRootPlan recovery obligations remain outside this read-only active path. Missing or malformed\nnative gate evidence cannot be replaced by an endpoint gate. This is no writer, bootstrap, root\nmint/abort, rotation, cancellation or terminal-retirement authority, and not an atomic multi-key\nsnapshot. Positive fixtures raw-seed synthetic marked records, not production service admission.\nThe account is trusted host configuration. Sealed readers do not attest the broker account or\ndetect a cross-account pair of legitimately constructed handles. The authority plane constructs the\nservice callout's registry, reader and contract reads from its own data account.\n\nA package-internal custody writer is not exported; in the product only the service endpoint host's\ndecide and revoke reach it, and the proposals they act on come only from the owner-proved public\nauthority route of a context opened with `serviceEndpoints`. Bootstrap\nwrites the reservation, the pending-root endpoint gate, a creating head, a frozen activation native\ngate opened at generation1, a RootPlan fixed once by head CAS, the planned active root, an\nidentical-bytes native gate touch and the active head stamp. It rechecks the endpoint gate at open\nand before the plan, root and stamp writes. Cancellation denies the endpoint gate first, then\nwrites the cancelling head and freezes the native gate under the cancel operation. An unstamped\nplanned root is fenced by a create of revoked bytes or by CAS of the exact active row. A stamped\nroot is retained, and every cancellation result is nonterminal. An absent reservation leaves\nendpoint deny residue and reports unconfirmed; a foreign or deleted reservation refuses without a\nwrite. Each write is a per-key create or revision CAS under a sealed host whose genuine plane-claim\nhold is re-read before and after the write, plus a fatal latch. That is local readiness control,\nnot broker writer exclusion, so a paused writer can still land one late conditional write. Each\nrequest is bounded by the remaining original deadline. In bootstrap, once the plan step has fixed\nor read the RootPlan, requests are also bounded by its expiry. Cancellation keeps only its own\ndeadline, so an accepted cancellation can still proceed after the root expires. At its bound the\nwriter stops waiting for a request but does not cancel it at the broker. The writer issues every\nendpoint-gate request itself, so no endpoint-gate request starts after an invocation returns, and\nit re-reads the endpoint gate before every plan, root and stamp write attempt. Bootstrap's final\nactive-root proof is several read-only reads behind one bounded wait. If that wait ends at its\nbound, the remaining reads can still run after the invocation returns; none of them writes.\nBootstrap holds instead of reporting an active root whose expiry its final synchronous check\nobserves, with no await between that check and the return. The result can still be stale by the\ntime the caller acts on it. The caller supplies that deadline and the\nreservation; nothing here authenticates a host, approves serving, retires terminally, releases an\nalias or rotates a root.\n\nA second package-internal module builds service host operations on that writer: register\nadmission, decide (approve or refuse), revoke, a create-only name binding, and the operation and\nproposal reads. It shares the\nwriter's sealed host, plane-claim hold check and fatal latch, and each invocation opens its own\ncustody host over a per-invocation effect registry. That host writes only the invocation's scope,\nthe exact keys discovery located before the effect registry opened, and the effect registry is\nhanded the same key set so a broker grant can match it. A step that needs a key outside the scope\nholds as `scope-mismatch` before anything is issued; replaying the same request discovers what is\nstored and scopes it. Each effect registry rides its own connection, whose credential may publish\nonly on that key set's subjects (subject confinement, not operation confinement; the engine itself\nonly creates and CASes those keys), never renews or reconnects, and expires at the invocation's original deadline\nrounded up to the whole second. With agreeing clocks and timely scheduling the broker's\nwhole-second timer closes it within about 1999 ms after that deadline; that is granularity, not a\nguaranteed close time, and no close proves an earlier write cannot still settle. Losing the\nconnection fences the invocation. Discovery's credential has no write at all. The module's records and their write order are in SPEC \xA713.1. Results name their operation once a binding exists or may exist: `pending`, `held` and\n`refused` all carry `operationId`. A result can carry `debt`, the capacity releases its operation\nstill owed as far as that invocation observed. A result without `debt` does not mean nothing is\nowed; the operation read is the authoritative view of the debt. A `held` result by itself implies\nneither a binding nor an authorized granter: a tripped engine, or a granter without a current\ninteractive row, gets one `held` answer for a register, decide or revoke before any read of the\nrequest's keys. `denied` means the invocation wrote nothing, and either the request can never bind\nor the invocation's own read found no binding. That read is point-in-time; another invocation of the\nsame request already in flight can still bind it later. A revoke result reports `denyNew` when it\nhas evidence: an endpoint-gate read, which gives `unknown` when the read failed or found another\nassignment's gate row, or an assignment that is already fenced, which gives `landed`. The early `held` answers of a tripped engine and of a\ngranter without a current interactive row carry no `denyNew`, and a result without `denyNew` says\nnothing about deny-new. Completed results\nsay whether this invocation applied them or returned a stored record. A stored approval returns\nunchanged, and its `approvedGateRevision` is null unless its own gate CAS was observed. The index\ndoes not export this module; the typed request, result and view parsers in the service host contract\nare public.\n\n`openAuthAuthorityPlane` and `startAuthService` take `serviceEndpoints: { startup: { kind: \"normal\" } }`\nto host these operations; it is off by default and no CLI flag sets it. With it, the plane and the\nhosted handle gain `serviceEndpointHost(request)`, which answers `decide`, `revoke`, `name-bind`, the\nfour caller-policy operations and the two reads in process. A hosted context rejects it as\n`unavailable` while it is not ready. That function has no route or capability of its own; register\nadmission is reachable only through the\npublic route below. The host authenticates no caller: the embedder must authenticate the operator or\nowner granter it passes. `name-bind` needs an operator granter and `expectedRevision: 0`. It creates\nthe endpoint's name binding once, with your `claimReference`, and only the identical request recovers\nit; nothing updates, transfers or releases a name. The host checks no domain control, so verify the\nclaim, or delegate a namespace you control, before you bind. The host runs over the plane's claim hold, data account and\nactor ledger directory. A restore or missing startup refuses the plane open, which unwinds and\nreleases the claim. If that unwind cannot close an effect connection, it stops there: the claim\nstays held, the plane's clients, scanners and cleanup timer stay live, and there is no handle to\nretry. Only the process exiting ends them, after which the existing crash reclaim applies. Its discovery connection is an ordinary renewing read-only client: a broker loss\nfails its reads until the stock client reconnects, and those calls answer `unavailable` with nothing\nwritten. A renewal failure closes it permanently, and the host stays unavailable until the plane is\nopened again. Both plane fatal paths trip the host, and either one trips it even while the\nplane closes, without making the plane fatal. Plane close refuses new host calls from the\nmoment it is called, waits for every call that already entered, from entry through discovery and\neffect open, closes discovery and then releases the claim. An effect connection that failed to\nclose stays owned past its call: close closes it again first, and rejects with the claim still held\nwhile it stays open, so you can retry close. Until a retry succeeds or the process exits, discovery,\nthe administrative rail, both scanners and the remaining clients stay live, so no successor can\ntake over the claim.\n\nWith `serviceEndpoints`, both faces also serve `/service-endpoint-authority`, and without it that\nroute answers 404. `GET` returns the receiver's account and space. `POST` takes a `{request, proof}`\nenvelope of at most 2 MiB. An IdP proof derives the owner, who needs a current interactive cli or web\nrow; register needs one, and so may activate. A service's renewal proof authorizes activate, renew\nand status, and its access bearer authorizes ready and status; neither is owner authority. A typical\nsequence: the service registers and gets `approval-required`; you bind its name with an operator\n`name-bind`; the owner approves with `decide`; the service replays the same register and gets\n`registered` with its witness and snapshot; it activates and gets a `credential` with an access bearer\nof at most 60 s and a renewal proof of at most 24 h, neither outliving the native root or approval;\nit connects to the broker with the bearer through the composed native service callout, binds its\nresponder, calls `ready`, and renews before the bearer expires. The auth provider's\n`serviceEndpointInfo` and `serviceEndpointAuthority` hooks are the client, and accept a service proof\nas well as the signed-in login.\n\nFor a serving connection, `serviceEndpointRenewal({ store, dir, witness })` returns that loop as a\nbearer function: pass its `bearer` as `EndpointOptions.bearer` and its `sentinelCreds` with it. The\nfirst pull activates on the signed-in login and every later pull renews on the stored renewal proof.\nIf that proof has expired, as after a machine was off for more than a day, the next pull replays the\nsame activate request on the signed-in login, which mints again while the root and the approval stand.\nIt keeps one activate and one renew requestId per witness, written to `store` before the first send,\nand replays them, with fresh material each time, for as long as the root and the approval allow.\nConcurrent pulls share one request. It owns no timer. A transport failure, `rate-limited`, a missing\nor rejected sign-in, or a store failure leaves it `unavailable`, and the next pull replays the same\nrequest; any other denial from the space ends it, and later pulls send nothing. `state()` reports\nexpiries only, errors are fixed sentences, and `close()` aborts a pull in flight.\n\nEvery effectful request is keyed by its requestId and body. Keep one requestId per operation and\nwitness, store it with the witness and the renewal material before you send it, and reuse it with\nthe same body after a restart: a replay is safe, a changed body under the same requestId is a\n`conflict`, and one instance accepts at most 64 distinct requests, so fresh keys per refresh or\nrestart exhaust it. A register requestId stays bound to the body and proposal its first answer\nadmitted: after approval, replay that exact request, and after a refusal or expiry, propose again\nunder a new requestId. A 503 means the request may be bound: replay the same request, or read its\nstatus with `target: { kind: \"operation\", operation, requestId }`; the selector names the operation\nsince this release, and the old `{ kind: \"operation\", requestId }` no longer parses. An owner reads\nstatus only while it holds an interactive row, and a service proof reads only its own instance.\n`registered` and `ready` in a status mean only that the request's registration is current. A\npre-activation re-registration sends `expected: { state: \"registered\", witness }` and yields a new\nwitness; a register that another request overtakes before its barrier freezes answers `conflict`,\nso read the instance and expect its current witness. A register whose gate moved under it without a\nnew registration, such as a freeze another barrier then aborted, also answers `conflict` with the\nwitness unchanged: if the instance still shows the witness you sent, replay the same request. A\nregister held inside the native barrier, or while another invocation of the same request is still\nrunning, answers 503; replay the same request. Once a service has activated, its registration\ncan no longer change. Commands must be ephemeral and declare\nno governed trait.\n\n`caller-grant` takes an owner granter with an interactive row who holds the approved instance, and a\ngrantee is a current exact owner, actor and lifecycle UID. A managed grantee also needs its actual\nsame-owner parent chain, with each current ancestor holding active covering grants for the same\nservice approval. The operation records those immutable grant ids and lifecycle UIDs in `parentRefs`\nand rechecks them before append. An admin scope never substitutes for endpoint caller rights. Every\ncapability must name that instance with `instanceOnly: true`, carry no journal access and no `any` or\n`ledger` target, and sit within the approval's caller ceiling. The answer is `granted` with the grant\nas stored. Ordinary user-auth agent connects now derive their endpoint rows from this policy through\ncore's native grant builder, including reserved Describe for each authorized exact instance. The\ncallout rechecks grantee lifecycle, named ancestor grants, current name, assignment, root, activation,\napproval and ceiling, then checks the policy evidence again after signing and native-key recording.\nThe original connection expiry is at most 60 seconds from issuance, and capped by root and approval\nexpiry. These are fresh leader reads,\nnot a linearized revoke fence. Existing issued connections keep their original expiry, and caller\nrevocation remains deny-new, not terminal absence proof.\nOne instance indexes at most 256 grants over its life, including attempts refused after indexing, and\na full index answers `resource-exhausted`; a new instance is the remedy. `caller-revoke` admits the\ngrant's source owner, its grantee owner and an operator. It answers `pending` with\n`denyNew: \"landed\"` and `notBefore`, in epoch seconds, 71 s after the broker time of the grant's\ncutoff marker. It never answers `revoked`, and the result parser refuses that arm.\n`caller-policy-read` gives an owner its own grantees, and another owner's grantee only filtered to a\nservice it holds; `caller-policy-list` needs a service selector.\n\nCaller grants need `max_payload >= 2098176` (2 MiB plus 1 KiB), `max_control_line >= 4096`,\nand an auth stream with `max_msg_size: -1`. Broker generation accepts an explicit\n`serverConfig(broker, accounts, { ...options, maxPayload: 2098176 })`; omission leaves the native\n1 MiB default unchanged. No startup path raises limits automatically.\n\nFor a **standalone broker only**, pass `serviceEndpoints.callerPayload.observerCreds` to\n`startAuthService` (or the corresponding authority-plane option). The callback returns fresh\nhost-private credentials from the broker owner's\n`mintServiceCallerPayloadObserverCreds({ pub: broker.sys.pub, signingSeed: broker.sys.signingSeed })`.\nKeep that registered SYS signer with the broker owner, never in the data account, a sandbox or a\nparticipant client. The helper returns an in-memory, at-most-60-second VARZ-only credential.\nThe callback only supplies credentials, owns no authority resource, and may finish after its\n10-second observation deadline has refused the operation. It is not a cancellation callback.\n\nStartup authenticates native VARZ, matches the observer and data connection's broker incarnation,\nchecks config/system-account evidence and live stream limits, then writes an exact-key assertion\nbound to that broker, configuration and genuine plane claim. Configured clusters, gateways and\nleaf links refuse, regardless of connected peer count. This is not multi-broker proof. A default\n1 MiB broker fails startup with the required byte limit and creates no assertion.\n\nPositive caller issuance repeats the observation and checks the exact assertion revision, complete\nplane hold and effective authority again before native JWT release. Withdrawal, config changes or\nclaim loss refuse new issuance. Reopening needs an explicit bootstrap to reassert for the new claim;\na prior-plane broker-bound assertion cannot authorize another append. Legacy assertion syntax still\nparses for existing trusted storage fixtures but alone never enables production caller issuance.\nShutdown drains in-flight observations and closes their owned connections before releasing the claim.\nReads and revokes do not need a positive assertion. A revoke whose policy row would not fit holds\n`payload-precondition` without writing it, and a failed live-limit check withdraws the assertion.\n\nGive each caller operation a fresh random requestId, record it with the exact body before you send\nit, and replay that body after a lost reply, a 503 or a `held` answer. Never switch to a new requestId\nwhile an earlier answer is unknown. A settled `refused` or `expired` ends the attempt, and a grant\nreplayed after its 10-minute window adopts a grant that landed and otherwise answers `expired`.\n\nCaller issuance is generic, not tied to Linear: the same provider-independent contract discovery and\ninvoke path works for ordinary and managed user-auth callers. Controlled policy append currently\nrequires the application's approved service-host instance. Native control endpoints retain their\nexisting ledger/scope and typed authority contracts, not fabricated application approvals. Scoped\nDescribe is not available, and complete terminal retirement and restore remain unsupported. A host revoke refuses new activation, renewal, readiness\nand connections, but a connection already open keeps its original expiry of at most 60 s. Approval\nruns service bootstrap and revoke runs cancellation, while complete observer evidence and writer\nexclusion remain unavailable. Do not seed service reservations in an existing space, adopt old\nservice-shaped rows, or use ordinary retirement to make them terminal. A signing-key or raw-store\nholder can bypass helper guards, so schema rejection alone does not make mixed-version writers safe.\nRestore must quarantine service work until a separately supported reconciliation protocol is\navailable.\n"
65365
65365
  },
65366
65366
  {
65367
65367
  "slug": "examples",