# Proposal: Userspace Authority Broker and Init-Owned Shutdown


## Problem

The current shell authentication path uses a kernel `AuthorityBroker`
capability. The shell starts with anonymous authority, calls the broker for an
anonymous bundle, then calls it again after password login for an operator
bundle. That works, but it places session policy, launcher policy, and shell
bundle construction inside the kernel.

That is the wrong long-term boundary. The kernel should provide primitive
mechanisms: process creation, capability transfer, endpoint rendezvous, memory,
terminal I/O, and process lifecycle. Login policy, operator profiles, service
allowlists, and shell bundles are userspace policy and should be owned by init
or an init-managed service.

Shutdown exposes the same issue. A shutdown command should not be a raw kernel
poweroff capability passed to the shell. The natural capOS behavior is that the
kernel halts when init and all remaining processes are gone. Shutdown policy
should therefore be implemented as init-owned lifecycle orchestration: stop
services, wait for them, release authorities, and then let init exit.

## Current State

Implemented pieces today:

- The kernel starts one init process from the boot manifest.
- Init reads `BootPackage`, validates the init-owned service graph, spawns
  services, records exported capabilities, and waits for children.
- The shell receives a terminal and anonymous authority, then upgrades after
  password login.
- `AuthorityBroker` is a kernel capability implemented in
  `kernel/src/cap/authority_broker.rs`.
- `ProcessHandle` supports `wait`, but not termination.
- There is no init-owned lifecycle control capability yet.

The consequence is a mixed trust boundary: init owns service graph execution,
but the kernel still owns shell session bundle policy.

## Goals

- Move authority-broker policy out of the kernel.
- Make init, or an init-managed broker service, responsible for authenticated
  shell bundles.
- Keep shell unauthenticated authority minimal.
- Make shutdown an init-owned control operation, not a direct kernel shutdown
  cap.
- Preserve the kernel rule that the system halts naturally when the last
  process exits.
- Keep all authority transfer explicit and inspectable through capabilities.

## Non-Goals

- Do not add ambient service names or a global service registry.
- Do not give shell raw `ProcessSpawner` before authentication.
- Do not add a kernel "kill everything" syscall.
- Do not introduce restart policy, persistence, or crash recovery in this
  proposal.
- Do not solve multi-user policy; this proposal only moves the current local
  operator/anonymous policy out of the kernel.

## Proposed Architecture

Init starts two policy-facing services:

- `authority-broker`: userspace service that owns shell bundle policy.
- `shell`: interactive shell, initially anonymous.

Init also keeps a private lifecycle table for services it spawned. That table
contains process handles, service names, restart policy state, and shutdown
ordering metadata. Init does not expose the raw table. It exposes attenuated
control capabilities.

### Capability Graph

```mermaid
flowchart TD
    Kernel[Kernel primitives] --> Init[init]
    Kernel --> Terminal[TerminalSession]
    Kernel --> Spawner[ProcessSpawner]
    Kernel --> Sessions[SessionManager]
    Kernel --> Audit[AuditLog]
    Kernel --> Creds[CredentialStore]

    Init --> Broker[authority-broker service]
    Init --> Shell[shell]
    Init --> Services[managed services]

    Broker --> ShellAnon[anonymous shell bundle]
    Broker --> ShellOp[operator shell bundle after login]
    Init --> Shutdown[init-owned ShutdownControl]
    Broker --> ShellOp
    Shutdown --> ShellOp
```

The shell talks to the broker over an endpoint. Before login, the broker returns
an anonymous bundle with no service-management authority. After login, the
broker returns an operator bundle that includes a restricted launcher and, if
policy allows, an init-owned shutdown control capability.

## Interfaces

The exact schema can evolve, but the minimum shape should separate broker
policy from init lifecycle control.

```capnp
interface AuthorityBroker {
    shellBundle @0 (sessionCapId :UInt32, profile :Text)
        -> (launcherIndex :UInt16,
            sessionIndex :UInt16,
            hasShutdownControl :Bool,
            shutdownControlIndex :UInt16);
}

interface ShutdownControl {
    shutdown @0 () -> ();
}

interface ProcessHandle {
    wait @0 () -> (exitCode :Int64);
    terminate @1 (reason :Text) -> ();
}
```

`AuthorityBroker` can be implemented as a userspace service using endpoint IPC
instead of a kernel cap. `ShutdownControl` is produced by init, not by the
kernel. `ProcessHandle.terminate` is a primitive lifecycle operation, but the
kernel only targets one process handle; init owns the policy that decides which
handles to terminate and in what order.

## Shutdown Flow

1. Shell starts anonymous and does not hold `ShutdownControl`.
2. User runs `login`.
3. Shell obtains an operator bundle from the userspace broker.
4. If policy allows, the bundle includes `ShutdownControl`.
5. User runs `shutdown`.
6. Shell invokes `ShutdownControl.shutdown`.
7. Init stops accepting new service operations.
8. Init asks managed services to terminate in dependency order.
9. Init waits for all service handles to exit.
10. Init releases remaining capabilities and exits.
11. The kernel observes no remaining runnable user processes and halts through
    the existing last-process-exited path.

This keeps final machine shutdown in the kernel, but keeps shutdown authority
and orchestration in userspace.

## Broker Migration Plan

### Phase 1: Define Userspace Interfaces

- Add schema for endpoint-served `AuthorityBroker` and `ShutdownControl`.
- Keep the kernel broker temporarily for compatibility.
- Add runtime clients for both interfaces.
- Add QEMU proof that an anonymous shell cannot call shutdown.

### Phase 2: Init-Owned Shutdown

- Extend init with a lifecycle table for spawned services.
- Add a private init service endpoint for shutdown requests.
- Add `ProcessHandle.terminate` or equivalent single-process lifecycle
  primitive.
- Make init terminate and wait for managed services before exiting.
- Add QEMU proof that `shutdown` after login exits QEMU cleanly.

### Phase 3: Userspace Authority Broker

- Implement `authority-broker` as a userspace service.
- Grant it only the policy inputs and capabilities needed to mint shell
  bundles.
- Have shell obtain anonymous and operator bundles from that service.
- Keep shell without raw `ProcessSpawner`; it should receive only restricted
  launch authority.
- Add QEMU proof that pre-login shell cannot spawn privileged services and
  post-login shell can run the expected demo commands.

### Phase 4: Retire Kernel Broker

- Remove `kernel/src/cap/authority_broker.rs`.
- Remove `KernelCapSource::AuthorityBroker`.
- Remove kernel-side broker bundle construction and tests.
- Update docs so the kernel boundary is again primitive-only.

## Security Properties

- Shell starts without shutdown authority.
- Shutdown authority is granted only after an authenticated session is proven.
- The broker cannot invent kernel powers; it can only delegate capabilities it
  received from init.
- Init remains the root of service lifecycle policy.
- Kernel process termination remains per-handle, not global.
- Service shutdown is auditable because it flows through init and named process
  handles.

## Open Questions

- Should `ShutdownControl.shutdown` be one-way, or should it return staged
  progress events before init exits?
- Should services receive a graceful `StdIO` close, a typed lifecycle signal,
  or only `ProcessHandle.terminate`?
- Should the broker be a separate process, or should init directly expose the
  broker endpoint until service supervision is stronger?
- How should restart policies interact with shutdown mode?
- Should shutdown require a fresh authentication event, or is the current
  operator session sufficient?

## Verification

Required QEMU proofs:

- Anonymous shell: `shutdown` is denied or unavailable.
- Operator shell: login returns shutdown authority.
- Shutdown command causes init to terminate managed services and exit.
- QEMU exits through the existing last-process halt path.
- Existing adventure/chat demo still works before shutdown.

Host tests should cover:

- Broker policy decisions for anonymous vs operator profiles.
- Init shutdown ordering over a synthetic lifecycle table.
- Manifest validation rejecting direct shell access to privileged lifecycle
  primitives before login.
