# Proposal: SystemInfo Capability

System-wide informational data (banner/MOTD today, hostname, help topics, and
on-ISO documentation later) exposed as a single typed capability instead of
ad-hoc per-feature kernel parameters.

**Status:** Phase 1 + Phase 2 implemented. Phase 1 introduced the
`SystemInfo` capability (renamed from `ShellConfig`, schema field `motd`)
and unified the print site so console and Telnet shells both call
`SystemInfo.motd()` themselves. Phase 2 then moved post-login authority
into `AuthorityBroker.shellBundle`: the broker mints `SystemInfo` plus a
profile-scoped `serviceEndpoints` list (`adventure` + `chat` for operator
shells, empty for guest and anonymous shells), so Telnet/SSH-launched
operator shells can run `chat-client`/`adventure-client` without per-transport
manifest forwarding. `chat` is the kernel-singleton chat endpoint
(`KernelCapSource::ChatEndpoint`) so all operator shells share one
chat-server queue; `adventure` is a fresh per-session endpoint.
**Last reviewed:** 2026-04-29 05:59 UTC.

## Problem

The pre-existing `ShellConfig` capability had a single method (`motd`) and
was distributed via manifest cap grants. That was already a capability
shape, but two things made it brittle:

- **The name claimed too little.** "Shell config" suggests configuration of
  the shell binary, but the data is system-wide and transport-agnostic
  (banner text doesn't belong to any one shell). Anything similar we
  wanted to expose later — hostname, help topics, manpages — would either
  squat on `ShellConfig` (wrong scope) or get its own one-method cap
  (proliferation).
- **The print site was asymmetric.** `init` printed the banner over COM1
  before launching the console foreground shell; the Telnet-spawned shell
  printed it itself after the gateway forwarded `shell_config` as a
  manifest grant. Two code paths, two places to keep consistent. The SSH
  Shell Gateway successor, and any future transport, would add a third.

The capability model already supports a clean fix: one cap, one print
site, room to grow.

## Design

### Interface

```capnp
interface SystemInfo {
    motd @0 () -> (text :Text);
    # Future:
    # hostname @1 () -> (name :Text);
    # helpTopics @2 () -> (topics :List(HelpTopic));
    # manPage @3 (name :Text) -> (page :ManPage);
}
```

Adding methods later is a Cap'n Proto-compatible change. Each future
addition gets its own kernel data source (or a userspace `SystemInfo`
service backed by storage, when persistence exists). Callers that only
need MOTD do not pay for the others.

### Data Source

`SystemInfo` is currently kernel-backed and reads from manifest
`kernelParams.motd` (renamed from `shellMotd`). Hostname will likely
come from `kernelParams.hostname` or a CloudMetadata-derived value;
help topics and manpages will eventually be served by a userspace
documentation service that holds a `SystemInfo` cap as one of its
exports. The kernel implementation is intentionally minimal — it owns
text the boot manifest already provided, and nothing else.

### Distribution

A process gains `SystemInfo` by listing it as a manifest cap source:

```cue
caps: [{
    name: "system_info"
    source: {kernel: "system_info"}
}]
```

Phase 1 granted the cap to:

- `init` — kept. `init` no longer reads `SystemInfo` itself, but the
  manifest spawn loop forwards init-held kernel-source caps to each
  service. The console foreground shell and any gateway service that
  receives `system_info` is reached through this forwarding, so init
  must hold the cap.
- The default console foreground shell (new — needed so the console
  shell can print MOTD itself).
- `telnet-gateway`, `restricted-shell-launcher`, and the SSH gateway
  terminal-host (was, kept — each forwards `system_info` to the
  child shell via `RestrictedShellLauncher`, same mechanism that
  forwards `creds`/`sessions`/`audit`/`broker`).

Phase 2 moved normal shell distribution into
`AuthorityBroker.shellBundle`: the broker mints a fresh `SystemInfo`
cap per session and returns it alongside the launcher, copied session,
and any profile-scoped service endpoint caps allowed for that profile.
`RestrictedShellLauncher` no longer requires a `system_info` pass-through grant.

### Print Site

The banner is printed by the shell on startup after it obtains its initial
shell bundle, across all transports:

```rust
// shell/src/main.rs
fn write_motd_from_bundle(...) -> Result<(), i64> {
    let mut system_info = SystemInfoClient::new(bundle.system_info.capability());
    let motd = system_info.motd_wait(ring, WAIT_FOREVER)...;
    for line in motd.lines() {
        terminal.write_line_wait(ring, line, ...)?;
    }
    Ok(())
}
```

`init` is no longer responsible for printing MOTD — its
`write_motd_to_terminal` helper is removed.

### Why Phase 1 Stayed Manifest-Driven

Moving `SystemInfo` distribution into `AuthorityBroker.shellBundle`
made architectural sense, but it required the broker to hold or be able
to mint informational caps and changed the shell bundle shape. Phase 1
therefore isolated the rename, the unification of the print site, and
the schema interface as separately reviewable prerequisites.

## Phase 2: Broker-Minted SystemInfo and Service Endpoints

`AuthorityBroker.shellBundle` returns a `RestrictedLauncher`, a copied
`UserSession`, `SystemInfo`, and any allowed profile-scoped service endpoint
caps per call:

```capnp
interface AuthorityBroker {
    shellBundle @0 (sessionCapId :UInt32, profile :Text)
        -> (launcherIndex      :UInt16,
            sessionIndex       :UInt16,
            systemInfoIndex    :UInt16,
            serviceEndpoints   :List(BundleEndpoint));
}

struct BundleEndpoint {
    name        @0 :Text;        # e.g. "chat", "adventure"
    capIndex    @1 :UInt16;
}
```

The broker mints:

1. `SystemInfo` (always) — replaces the manifest grant.
2. Service-endpoint caps the requested profile is allowed to reach
   (`chat` and `adventure` for operator profiles, none for guest or
   anonymous).

`RestrictedShellLauncher`'s required shell grants collapsed to
`creds`, `sessions`, `audit`, and `broker`; `system_info` and service
endpoint authority now arrive through the broker bundle, keeping the
kernel launcher minimal.

### Phase 2 implementation notes

- Phase 2 landed in three sub-tiers (A: `SystemInfo`; B: `adventure`; C:
  `chat`). The broker holds a kernel-side `Arc<Endpoint>` for chat — the
  `KernelCapSource::ChatEndpoint` lazy singleton constructed by
  `BootCapFactory` — and `Arc::clone`s it into every operator bundle.
  `adventure` is fresh per operator bundle.
- The shell prefers manifest-granted (`CapSet`) caps over bundle service
  endpoints when both have the same name. The focused chat manifest now gives
  init the kernel singleton `chat_endpoint` to forward to `chat-server` and
  relies on the broker-issued `chat` endpoint for the normal shell path instead
  of a shell-local chat-server export, matching the Telnet and default shell
  bundle model. Normal shell `@chat badge 200` syntax is now rejected by the
  parser before it can reach the delegated-client relabel check; lower-level
  smoke paths retain relabel fixtures for kernel/process-spawn enforcement.
- `RestrictedShellLauncher::REQUIRED_SHELL_GRANTS` no longer requires
  `system_info`; the broker is now the single source for that cap.

## Cross-References

- [shell-proposal.md](shell-proposal.md) — banner ownership and
  help-topic discovery were implicit open questions; this proposal
  resolves "where does the banner live" (Phase 1) and "where does the
  post-login authority live" (Phase 2).
- [networking-proposal.md](networking-proposal.md) — Telnet
  gateway/shell interaction; SystemInfo is now part of the broker
  bundle consumed by the shell after launch.
- [boot-to-shell-proposal.md](boot-to-shell-proposal.md) — login
  flow runs after the shell has acquired its initial anonymous bundle
  and printed MOTD from broker-minted SystemInfo.
- [userspace-authority-broker-proposal.md](userspace-authority-broker-proposal.md) —
  Phase 2 makes the broker the single source of post-login authority,
  including informational caps.

## Non-Goals

- This proposal does not introduce persistent storage for system
  information. MOTD comes from the boot manifest; future fields will
  come from manifest, CloudMetadata, or a userspace documentation
  service when those exist.
- This proposal does not add a separate pre-authentication issue/banner
  channel. MOTD is printed after initial shell-bundle acquisition; a true
  pre-auth warning banner would need its own reviewed distribution path.
- This proposal does not unify hostname into Phase 1. Hostname has no
  current use site and would create churn without a payoff; add it
  when the first consumer appears.

## Open Questions

- Pre-authentication warning banner: MOTD now comes from the initial
  broker-issued shell bundle. If capOS later needs a banner before
  `SessionManager`/`AuthorityBroker` interaction, it should be a distinct
  issue-style surface rather than a regression to ad-hoc manifest grants.
- Hostname source: manifest field, CloudMetadata-derived, or
  storage-backed mutable state. Park until first consumer.
- Help-topic discovery: tied to schema reflection and the
  `SchemaRegistry` open question in shell-proposal.md. Likely lives
  in a userspace documentation service, not in the kernel cap.
