# Service Object Identity Migration

**Status:** superseded on `2026-04-28 14:35 UTC` by
[`Session-Bound Invocation Context`](../proposals/session-bound-invocation-context-proposal.md)
and the active selected backlog
[`docs/backlog/session-bound-invocation-context.md`](session-bound-invocation-context.md).
The Big Chunk 1 synthetic routing/lifecycle proof remains useful historical
coverage, but Big Chunk 2 subject/proof root opening and shared-service
service-object migration should not proceed on the active mainline path.

Historical plan for replacing caller-selected service-visible identity with
kernel-routed service object capabilities and userspace-verified subject
capabilities.

This backlog intentionally uses large implementation chunks. Each chunk should
land as a coherent reviewed branch with one focused end-to-end QEMU proof plus
the affected host tests, rather than splitting the transition into dozens of
small branches that each require full verification.

## Design Target

The final model has two separate authorities:

- **Subject/proof capabilities**: issued by trusted userspace services such as
  `SessionManager`, service-principal issuers, workload-identity issuers,
  anonymous/guest issuers, or `AuthorityBroker`.
- **Service object capabilities**: minted by a trusted service root/factory
  after it validates subject/proof authority and policy context.

The kernel enforces generic capability mechanics only:

- live generation-tagged cap-table entries;
- endpoint/object routing;
- receiver immutability across copy, move, IPC transfer, and spawn;
- trusted mint authority;
- revocation/lifetime checks;
- generic queue, byte, cap-count, and scheduling bounds.

Userspace services enforce policy:

- trusted issuer selection;
- subject facts, roles, sessions, guests, service accounts, and workloads;
- external subject admission and local/pseudonymous principal mapping;
- audit context;
- quota bucket selection;
- application object records and facets;
- whether a subject must stay live for a given object.

Request fields remain data. They must never select service authority.

## External Subject Alignment

This migration must preserve the identity model already described in
`docs/proposals/user-identity-and-policy-proposal.md`,
`docs/proposals/oidc-and-oauth2-proposal.md`, and
`docs/backlog/local-users-management.md`.

External subjects enter capOS only through an admission pipeline:

1. An external verifier validates the provider assertion, such as an OIDC ID
   token, passkey assertion, certificate chain, cloud workload token, or remote
   gateway-authenticated claim.
2. Admission normalizes the external key as provider kind, issuer, tenant, and
   subject, then either maps it through `ExternalIdentityBinding` to an
   existing local principal or admits it as an explicitly configured
   pseudonymous/guest/service principal.
3. `SessionManager`, a service-principal issuer, or a workload-identity issuer
   mints the local subject/proof capability. Imported provider groups, roles,
   tenant IDs, `acr`, `amr`, device posture, and token age are ABAC inputs for
   this mint decision, not downstream object authority.
4. A service root validates the local subject/proof capability and policy
   context before minting a service object capability.

Consequences:

- Service objects store verified local subject facts and audit context, not raw
  external tokens or provider-specific claim bags.
- A provider claim can influence the object minted at open time only through
  trusted admission, broker, or verifier capability paths.
- A stale, disabled, or unbound external subject must fail before a service
  object is minted.
- Remote gateways translate connection authentication into local subject/proof
  caps; ordinary application services should not authorize directly from
  network connection identity.
- The same object-capability migration should work for local password/passkey
  sessions, OIDC users, cloud workload identities, service accounts,
  anonymous/guest sessions, and future remote cap transports.

## Network Transparency Alignment

The first implementation is local to one kernel and one endpoint object graph,
but it must not block future network-transparent capability transport.

Design constraints for this migration:

- Do not serialize kernel receiver selectors, cap-table handles, endpoint
  object ids, generation values, or server cookies as portable object names.
- Treat service object capabilities as live references. A future remote bridge
  should export/import them through connection-local tables, not through global
  URLs or raw selector strings.
- Preserve Cap'n Proto-style disconnect behavior: if the local endpoint,
  server, or remote connection dies, imported references become broken and calls
  fail explicitly rather than silently rebinding to a new server.
- Keep persistent restore separate from live object routing. If a service needs
  a durable object reference, restore should go through a capability-bearing
  persistence/naming service that can authorize and mint a fresh live object.
- Keep subject admission separate from transport identity. A remote bridge may
  authenticate a TLS/OIDC/certificate/session channel, but application services
  should still receive local subject/proof caps and service object caps.
- Keep object equality out of the first implementation. If future remote
  transport needs equality, expose it through a deliberate service or transport
  protocol rather than assuming global kernel object identity is comparable
  across hosts.

The local receiver-cookie model should therefore be an implementation detail
behind local `ServiceRef` capabilities. The portable concept is the authority to
call a typed object reference, not the routing selector used by one kernel.

## Non-Negotiable Invariants

- Only trusted mint paths create a new service object identity.
- Ordinary copy, move, IPC transfer, and spawn preserve the same service object.
- A child that receives an object capability acts through that same object,
  unless a service method explicitly mints a new delegated object.
- Endpoint routing is derived from the invoked capability, not from request
  bytes, shell text, manifest user input, process id, user name, role name, or
  numeric labels.
- The kernel never interprets users, accounts, roles, tenants, service
  accounts, rooms, NPCs, moderators, file owners, or workload names.
- Server cookies used for object dispatch must be generation-safe and must not
  be raw pointers in the first implementation.
- Move transfer remains transactional in capOS: a failed delivery or canceled
  receive rolls back reserved source authority rather than silently dropping it
  before adoption.
- Application rights should be represented by typed interfaces or narrower
  object capabilities, not by generic permission bitmasks.

## Big Chunk 1: Core Service-Object Routing And Lifecycle

Visible proof: a synthetic service in QEMU mints two distinct object
capabilities, routes calls by kernel-delivered receiver cookie, transfers one
object through IPC and process spawn, and proves copied/moved/spawned handles
still reach the same object record. The proof also injects forged identity and
selector-like bytes into request payloads and shows they do not affect routing.

Implementation scope:

- Make service-object terminology explicit in cap-table and endpoint code while
  preserving compatibility with current hold metadata.
- Introduce or formalize endpoint-scoped receiver records with generation-safe
  server cookies.
- Add a trusted mint interface/path owned by endpoint owner or explicit mint
  authority.
- Deliver receiver cookie plus interface/method/payload/cap grants to the
  server.
- Preserve receiver identity across copy, move, IPC transfer, and spawn.
- Add lifecycle behavior for receiver close/revoke and stale-generation reuse
  sufficient for the synthetic proof. Broader release/exit cleanup remains in
  Big Chunk 4.
- Add a synthetic service-object demo, manifest, shell/host harness, and hostile
  checks.

Verification gate:

- `make fmt-check`
- `cargo test-lib`
- `cargo test-config`
- focused QEMU proof target for the synthetic service-object routing demo
- `make run-spawn` or another focused spawn/transfer proof that exercises the
  modified grant path

Review notes:

- 2026-04-28: `workplan/service-object-routing-core` added the first focused
  service-object routing proof, but does not close this whole chunk. The branch
  introduced `CapGrantMode.serviceObject` as the explicit spawn-grant spelling
  for endpoint-scoped service object facets, kept `clientEndpoint` as
  compatibility spelling, added receiver-cookie preservation host checks, and
  added `make run-service-object-routing` for a synthetic two-object QEMU proof
  with payload spoofing, copy and move service-object IPC transfer, and nested
  spawn delegation. The proof also rejects service-object minting through the
  legacy ProcessSpawner endpoint-result facet path, keeping that compatibility
  exception scoped to `clientEndpoint`. At that checkpoint, generation-safe
  server cookie representation beyond fixed demo constants and explicit
  receiver lifecycle/close/revoke coverage still remained.
- 2026-04-28 14:10 UTC: commit `a4655f0` completed Big Chunk 1. The focused
  demo now encodes service receiver cookies as receiver-index plus generation,
  stores service-side object records, proves close and revoke rejection for
  later calls, and queues a stale alpha call before reusing the alpha record
  slot so the stale generation is rejected instead of dispatching to the reused
  record.
- Review must inspect `capos-lib/src/cap_table.rs`,
  `kernel/src/cap/endpoint.rs`, `kernel/src/cap/ring.rs`,
  `kernel/src/cap/transfer.rs`, `capos-config/src/ring.rs`,
  `capos-rt/src/ring.rs`, `capos-rt/src/client.rs`, and the new demo.
- Do not migrate chat, adventure, or stdio in this chunk. The synthetic proof
  should isolate the kernel/runtime semantics first.

## Big Chunk 2: Subject/Proof Authority And Service Root Opening

Visible proof: a root service accepts a trusted local subject/proof capability,
validates it through a verifier or broker capability, mints a service object,
and rejects fake same-shape subject objects, expired/stale proofs, wrong
audience, and payload identity spoofing. A spawned child that receives only the
object cap can use the object but cannot open sibling objects.

Implementation scope:

- Add minimal schema/runtime surface for a local subject/proof verifier. Keep
  it local and bounded; do not require full remote cryptographic identity yet.
- Model the verifier result in the same shape used by external admission:
  local or pseudonymous principal id, principal kind, auth strength, policy
  profile, resource profile, audit context, and optional claim-derived ABAC
  attributes.
- Bind subject/proof data to audience, purpose, and freshness enough for the
  proof.
- Add service-root open semantics over the core service-object mint path.
- Store verified subject, audit context, quota placeholder, policy mode, and
  optional liveness link in service-owned object records.
- Add hostile checks for fake subject providers and request-field identity
  spoofing.
- Add explicit delegation behavior: raw object-cap transfer preserves the same
  object; explicit service delegation mints a new object only through a service
  method.

Verification gate:

- `make fmt-check`
- `cargo test-config`
- relevant host tests for subject/proof encode/decode and validation
- focused QEMU proof target for subject/proof service-root open
- one existing session proof such as `make run-login` or
  `make run-ssh-public-key-auth` if touched by the subject path

Review notes:

- Avoid broad cryptographic protocol work in this chunk. The target is local
  issuer-verifiable subject/proof authority, not production remote federation.
- Keep application role policy out of the kernel and out of generic rights
  bits.
- Do not bypass `ExternalIdentityBinding` or admission policy when adding
  external-subject tests. If a fixture models OIDC, passkey, cloud, or
  certificate input, it must first resolve to a local subject/proof cap before
  any service object opens.

## Big Chunk 3: Shared-Service Migration

Visible proof: existing shared-service demos run without caller-selected
service-visible identity. Chat, stdio/terminal child bridges, and adventure
receive service object capabilities directly or open them through root/factory
interfaces. Existing shell workflows still work, but children cannot choose or
rewrite the object identity they receive.

Implementation scope:

- Convert `Chat` into root/object interfaces such as `ChatRoot` and
  `ChatParticipant`, with subject binding at the root boundary.
- Convert stdio or terminal child bridges that depend on endpoint-client
  identity into service object caps or a narrowed terminal/session object.
- Convert adventure player/NPC authority to service objects, including room
  speech over migrated chat object caps.
- Update shell launch examples and spawn grant parsing so ordinary grants name
  existing capabilities only.
- Preserve compatibility only where a focused legacy smoke still needs it, and
  mark it as transitional.
- Add hostile smokes proving request-field identity spoofing, child relabeling,
  and unauthorized sibling minting fail.

Verification gate:

- `make fmt-check`
- `cargo test-config`
- `cargo test-lib`
- `make run-chat`
- `make run-adventure`
- focused stdio/terminal or shell proof touched by the migration
- one hostile service-object delegation QEMU proof

Review notes:

- This is deliberately a large branch. Avoid stopping after only chat unless
  the branch becomes too risky to review coherently.
- If adventure or stdio exposes an implementation blocker, record it in
  `REVIEW_FINDINGS.md` with concrete remediation before merging partial
  migration.

## Big Chunk 4: Legacy Compatibility Retirement And Naming Cleanup

Visible proof: no normal shell, manifest, shared-service demo, or docs path
exposes caller-selected service-visible identity. Internal names match the
implemented model where the field is only an endpoint-scoped receiver selector.

Implementation scope:

- Rename internal fields, docs, and diagnostics from legacy identity language to
  receiver-selector terminology where behavior has migrated.
- Remove compatibility grant syntax and manifest fields that can no longer be
  used by supported smokes.
- Remove the default MOTD adventure launch commands that still expose explicit
  legacy receiver selectors, or replace them with service-object-safe commands
  after the shared-service migration.
- Tighten validation so service object authority cannot be constructed from
  user input.
- Add release/exit cleanup coverage for service object caps with queued calls,
  in-flight returns, server-owned object records, and receiver revocation.
- Update `docs/capability-model.md`, `docs/architecture/ipc-endpoints.md`,
  `docs/security/trust-boundaries.md`, `docs/proposals/service-object-capabilities-proposal.md`,
  `docs/proposals/user-identity-and-policy-proposal.md`, `docs/status.md`,
  and relevant backlog files, including notes about future network-transparent
  import/export and persistent restore boundaries.

Verification gate:

- `make fmt-check`
- `cargo test-lib`
- `cargo test-config`
- `cargo test-ring-loom` if ring metadata changes
- `make run-smoke`
- `make run-spawn`
- `make run-chat`
- `make run-adventure`
- `make docs`
- generated-code check if schema or generated bindings changed

Review notes:

- This branch should close the compatibility migration or explicitly preserve
  only low-level hostile-test fixtures.
- Do not leave user-facing syntax or docs that imply clients may choose service
  object identity.

## Deferred Work

- Remote capability transport and network-transparent object references.
- Production cryptographic subject proof protocols.
- Persistent restore of service objects across server restart.
- Full quota service and scheduling-context donation policy.
- Cross-host federation and external identity mapping.
