Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Session-Bound Invocation Context

Selected milestone backlog for replacing caller-selected endpoint identity without continuing the Service Object Identity Migration.

The detailed design lives in docs/proposals/session-bound-invocation-context-proposal.md.

Design Target

The final model has one live session context per process:

  • Process.session_context is immutable after spawn.
  • Endpoint calls deliver privacy-preserving caller session metadata to the server. Subject details are not disclosed unless the caller explicitly asks for disclosure through the service call and a broker/service disclosure scope allows the requested fields.
  • Broker-granted capabilities decide which service roots/facets a process may invoke.
  • Services key user-facing state by caller session plus service-local records.
  • Request payload fields are data and cannot select authority.
  • Cross-session raw transfer is governed by cap transfer scope: same_session, cross_session_shareable, or service_regrant_only. If a cap crosses sessions, the receiver session supplies the future invocation subject context.

The existing service-object routing proof remains historical coverage for receiver-cookie spoofing, lifecycle, and transfer behavior. It is not the application authority model.

Gate 1: Process Session Invariant

Visible proof: a focused QEMU session/process smoke shows a spawned shell and child process have exactly one immutable session context, inherited by default, while an attempt to inject or use a second independent invocation subject fails.

Implementation scope:

  • Add process-owned session context metadata with explicit system/service session support.
  • Make ProcessSpawner select the child session context through inherit or a trusted broker/session-manager path.
  • Prevent ordinary processes from holding or using multiple independent UserSession values as ambient invocation subjects.
  • Keep SessionContext internal to process/session mechanics. Do not expose principal, profile, account, role, tenant, auth-factor, external-claim, or display fields through endpoint defaults or proof-only shortcuts.
  • Add host tests for spawn/session validation and QEMU proof output for child inheritance.
  • Hostile proof cases must show that copied UserSession caps, payload data, shell strings, and manifest grant data cannot install a second process session or select another child session outside the trusted broker/session path.
  • Define the fail-closed freshness rule used by later endpoint work: normal endpoint calls from dead, revoked, or stale workload sessions fail except explicit recovery, logout, or renewal caps.
  • Preserve existing anonymous/operator shell behavior while making guest shell behavior explicitly manifest-gated and narrow.

Verification gate:

  • make fmt-check
  • cargo test-config
  • relevant host tests for session metadata
  • focused QEMU process/session proof
  • one existing login or shell proof touched by the session path

Status 2026-04-28 17:01 UTC: the kernel now gives each process an immutable SessionContext, ProcessSpawner inherits the caller context by default, and trusted broker/session paths can mint launchers fixed to a validated child context. make run-session-context proves a copied UserSession cap cannot relabel the child invocation context and that a broker profile mismatch fails closed. It also proves an expired guest session cannot refresh a broker shell bundle. Endpoint-delivered caller-session metadata, payload spoofing, and field-granular disclosure remain Gate 2 work.

Gate 2: Endpoint Caller Session Metadata And Disclosure

Visible proof: an endpoint server receives only an opaque service-scoped caller session reference by default, rejects payload attempts to spoof user, session, role, or participant identity, and receives bounded subject details only when the caller explicitly requests disclosure and a broker/service disclosure scope permits the requested fields.

Implementation scope:

  • Extend endpoint delivery metadata with an opaque service-scoped caller session reference and minimal freshness/liveness information.
  • Add an explicit disclosure mechanism for bounded subject fields, such as a per-call disclosure flag or a SessionDisclosure cap, and require a matching broker/service disclosure scope before fields are delivered.
  • Decide the first freshness enforcement point needed to close the open session expiry review finding.
  • Keep endpoint receiver metadata internal and non-authority-bearing.
  • Add hostile endpoint tests proving request bytes cannot override caller session context or force subject disclosure.
  • Add transfer-scope tests proving a same_session cap cannot cross into another session, while a cross_session_shareable cap invokes under the receiver’s session context after transfer.
  • Default transfer scope is fail-closed for cross-session movement: user/session-local caps use same_session or service_regrant_only, while cross_session_shareable must be explicitly chosen by the service or broker.
  • Add expiry/revocation cases before shared-service migration: broker refuses fresh bundles for stale sessions, stale normal endpoint invocations fail or report the documented freshness failure, and service-scoped session refs cannot be replayed as authority.

Verification gate:

  • make fmt-check
  • cargo test-lib
  • cargo test-config
  • focused endpoint/session QEMU proof
  • make run-spawn

Status 2026-04-28 17:43 UTC: commit 687511a implements the first Gate 2 slice. Endpoint delivery includes only a service-scoped opaque caller-session reference, epoch, and live/stale flags by default; it does not expose principal, profile, account, role, tenant, auth-factor, external-claim, display-name, or source-network fields. Normal endpoint calls from stale process sessions fail closed before transfer preparation or enqueue. make run-session-context proves a live child endpoint call carries nonzero opaque metadata despite spoofed user, session, and role payload labels, then proves the same child cannot invoke the endpoint after its session expires.

Status 2026-04-28 18:38 UTC: commit f0cb74b implements Gate 2 transfer-scope enforcement. Cap holds now distinguish same_session, cross_session_shareable, and service_regrant_only transfer policy. Endpoint IPC, endpoint returns, and spawn grants reject cross-session movement unless the scope permits it; fixed-session broker/launcher paths can regrant service_regrant_only caps. make run-session-context proves same-session spawn denial, raw IPC denial for a service-regrant-only UserSession, and receiver-session invocation after an allowed endpoint-cap transfer. Remaining Gate 2 work at that checkpoint was the explicit field-granular disclosure mechanism.

Status 2026-04-28 19:33 UTC: commit 0f92d77 completes the Gate 2 explicit disclosure mechanism. CALL SQEs carry a field-granular disclosure request mask, capability holds carry service/broker disclosure scope, and endpoint delivery exposes only the requested-and-allowed subject fields. The focused QEMU proof covers all three privacy cases: request without scope exposes no fields, scope without request exposes no fields, and request plus matching scope exposes only allowed fields while narrowing broader requests. Gate 3 is the chat session-keyed migration.

Gate 3: Chat Session-Keyed Migration

Visible proof: make run-chat shows chat membership keyed by an opaque service-scoped caller session reference and broker-granted chat capability, with no user-facing badge or receiver selector. Payload identity spoofing, unauthorized subject disclosure, and unauthorized cross-session participant id reuse fail closed.

Implementation scope:

  • Replace legacy chat receiver-selected member identity with session-keyed records.
  • Treat ChatRoot possession plus caller session context as sufficient for join, subject to broker/profile policy.
  • Keep global principal/account metadata private by default. If chat needs display name or guest/operator class, obtain it through explicit disclosure with a matching disclosure scope. If it only needs narrower behavior, use a broker-granted chat facet that encodes policy without revealing subject fields.
  • Add a narrower moderator facet if moderator behavior is needed; do not use payload roles or generic rights bits.
  • If chat supports multiple participant records per session, make returned participant ids server data scoped to the caller session, not transferable authority.
  • Decide chat cap transfer scope explicitly. Plain ChatRoot may be same-session or broker-shareable; participant-like state must not raw-transfer across sessions unless chat defines a share/regrant method. If chat accepts a share, future calls use the receiver session as the invocation subject.
  • Update shell examples and chat docs.

Verification gate:

  • make fmt-check
  • cargo test-config
  • cargo test-lib
  • make run-chat
  • hostile chat spoofing QEMU coverage

Status 2026-04-28 20:06 UTC: commit dc7ece4 implements the Gate 3 chat session-keyed migration. chat-server now serves with endpoint caller metadata, derives an opaque live caller-session key, and uses that key for member records, channel membership, sends, leaves, and polls. Calls without a live session key fail closed. system-chat.cue no longer assigns static chat badges to the shell or bot, and make run-chat proves normal chat runs through operator-session chat-client processes while the attempted delegated endpoint relabel remains rejected. The handle join field is request data only, not membership authority. After review, chat-visible sender labels are also service-assigned member-N values, so request handles do not drive displayed sender identity.

Gate 4: Shared-Service And Legacy Cleanup

Visible proof: normal shared-service demos no longer expose caller-selected service-visible identity, and service-object identity planning is retired from the active path.

Implementation scope:

  • Apply the session-keyed model to adventure player/NPC state and terminal/stdio child bridges where they currently use legacy receiver metadata as identity.
    • Stdio bridges should bind parent-side servicing to opaque live endpoint caller-session metadata and reject a bridge that later changes caller session, without asking the child to disclose global subject fields.
  • Remove normal shell and manifest syntax that lets a caller select a badge or receiver selector.
  • Keep low-level receiver metadata only as internal endpoint transport state or hostile-test fixture.
  • Update docs/capability-model.md, docs/architecture/ipc-endpoints.md, docs/security/trust-boundaries.md, demos, and status pages.

Verification gate:

  • make fmt-check
  • cargo test-lib
  • cargo test-config
  • make run-smoke
  • make run-spawn
  • make run-chat
  • make run-adventure
  • make docs

Status 2026-04-28 20:48 UTC: the guest-bundle cleanup slice narrows one Gate 4 identity/policy leak without touching adventure content. SessionManager.guest now requires an explicit manifest guest seed, AuthorityBroker.shellBundle returns no default guest service endpoints, and guest launchers use a resource-profile launcherProfile instead of the full manifest binary list. The default guest profile has an empty launcher; the session-context proof uses a dedicated one-binary guest profile for session-context-child; and the default smoke proof covers manifest-without-guest-seed denial.

Status 2026-04-28 21:36 UTC: the session-expiry review finding is closed for current shell/broker authority. Endpoint CALLs already required live caller sessions. Retained broker-issued non-endpoint bundle caps now expire at their bound session boundary: RestrictedLauncher rejects spawn/list calls after the minted session expires, and broker-issued SystemInfo caps are session-bound wrappers. Raw process-spawner, capability-manager, and process-handle control caps opt into live caller-session dispatch for any path that still exposes them. make run-local-users proves an expired operator shell cannot keep launcher authority through an already-issued bundle, and make run-session-context proves the narrow guest proof launcher also fails closed after expiry.

Status 2026-04-28 22:02 UTC: the normal shell parser now rejects explicit client @... badge N grants and preserves delegated client endpoint identity when badge syntax is omitted. Default MOTD and adventure docs use omitted-badge launches, while hostile selector fixtures remain in low-level smoke coverage.

Status 2026-04-29 05:59 UTC: the focused chat manifest now routes the same kernel singleton chat_endpoint through init to the resident chat server that the broker facets into operator shell bundles. The focused chat shell no longer receives the resident chat-server export directly from system-chat.cue; the normal shell path uses the broker-issued operator bundle chat endpoint, while the resident bot keeps its manifest service grant.

Status 2026-04-29 06:17 UTC: terminal output is now behind the same live caller session dispatch gate as terminal input. Both UART-backed TerminalSession and socket-backed SocketTerminalSession require a live caller session for write, writeLine, and readLine, so stale shell sessions cannot keep a terminal bridge useful through write-only calls. TcpSocket.intoTerminalSession continues to return a move-only terminal cap, but the result hold is explicitly cross-session shareable because the Telnet gateway converts an accepted socket in its service session and then grants the terminal to the broker-minted shell session.

Status 2026-04-29 09:00 UTC: shell-serviced stdio bridge waits now bind to opaque live endpoint caller-session metadata during the active child wait and reject mismatched callers without asking the child to disclose global subject fields. Normal StdIO.close exits cleanly, rejected calls drain transferred caps before returning, and make run-session-context covers a transfer-bearing cross-session rejection. demos/service-common no longer exposes a badge-serving helper or badge field on EndpointCaller; new shared endpoint loop code uses EndpointUserData, with the old badge-named user-data alias kept for checked-in adventure compatibility while peer-owned adventure work migrates.

Status 2026-04-29 09:44 UTC: the non-adventure endpoint caller-session reference is widened to 128 bits while keeping scoped_ref as the low 64-bit compatibility half and adding scoped_ref_hi as the high half. Endpoint delivery fills both halves from independently domain-separated, nonzero hashes, keeps epoch separate, and non-adventure service/session-context/stdout bridge guards now require and compare both halves. This remains proof-grade opaque reference derivation; a true keyed secret, scope-key rotation, and rotation lifetime policy are still deferred.

Status 2026-04-29 10:20 UTC: endpoint caller-session references now use an entropy-backed boot secret and HMAC-SHA256 over a non-reused endpoint service-scope id plus the kernel session id. scoped_ref remains the low ABI field, but it is no longer value-compatible with the old unkeyed low-half hash; scoped_ref_hi is the high ABI field of the same keyed opaque reference. epoch stays a separate field and is also domain-separated under the boot key so stale/freshness audit correlation rotates with boot-key and endpoint-scope changes. References rotate on reboot and endpoint object replacement. Stable service-audit identity across service upgrades remains future work.

Status 2026-04-29 11:00 UTC: the session-context QEMU proof now calls two distinct endpoint service scopes from one child process/session before expiry and asserts their opaque caller-session reference tuples differ while both remain live. This covers endpoint-object replacement/scope changes at the demo-proof level; stable service-audit identity across upgrades remains future work.

Deferred Work

  • Remote capability transport and network transparency.
  • Durable account store and external identity binding persistence.
  • Full quota service and scheduling-context donation policy.
  • Explicit cross-session sharing UX and audit workflow.
  • Stable service-audit identity for endpoint caller-session references across intentional service replacement or upgrade.
  • Delegated-subject / act-on-behalf-of context. See docs/proposals/delegated-subject-context-proposal.md.