Security and Privacy Considerations¶
- Date: 2026-08-02
- Status: Approved (2026-08-02, ADR-0009)
Hard rules¶
- Never read: tokens, prompts, request bodies,
server.env,api_keys, or artifact bodies. The statusline reads only metadata:model,provider,combo_name,status,timestamp,requested_model,combo_step_id(the metadata-only column allowlist fromArchitecture.md). - Never write: no writes to OmniRoute data, the transcript, or any configuration. The SQLite path (if ever enabled) uses a read-only
:rovolume mount only. - Never render: tokens, prompts, secrets, or sensitive request content. Output is bounded to the route/context segments (
01-display-requirements.md). - No network: the statusline makes no network calls. All reads are local files (transcript, mapping) or, optionally, a local Docker read.
Untrusted label source¶
OmniRoute is an untrusted label source (trust-boundary table in Architecture.md). Model/provider strings arrive via the API response and the transcript; they are display data only. The statusline never executes, evaluates, or interprets these strings as instructions, and never uses them in a shell context. This is the injection defense: a hostile or corrupted routed label can at most appear on screen, never act.
Statically-allowlisted read path¶
The statusline's reads must not depend on the auto/cheap classifier (circular dependency, Phase 2 finding 3). Verified behavior:
- Plain file reads (transcript tail, mapping file) do not require the classifier — this is the default path.
docker run(SQLite enrichment) may be gated by the classifier non-deterministically. This is a further reason the default provider path avoids Docker (04-caching-timeout-policy.md).
The implementation must prefer file reads and must not rely on any command that the classifier could block.
Local-only data¶
The mapping file (~/.claude/statusline-model-provider.json) contains only model→provider pairs — no credentials, no request content. It is user-owned local data. The design records generated_at and version fields so a stale or unexpected mapping is visible and recoverable, but the file is never treated as executable or authoritative beyond its role as a heuristic.
Failure and privacy¶
No failure mode exposes sensitive data (Architecture.md failure table). The error contract (02-input-output-contract.md) degrades to the requested label, model-only, or an empty line — never to raw transcript content, logs, or stack traces on screen.
Version drift and content safety¶
Transcript structure can change between Claude Code versions (observed relocation of model to message.model). The parser is defensive (ADR-0008): it reads only the named fields it needs, skips malformed lines, and never dumps raw JSON to the output. A schema change appears as a missing value, never as a break or a leak.
Review checklist¶
- [ ] No read of tokens, prompts,
server.env,api_keys, or artifact bodies. - [ ] No write to any source.
- [ ] Output contains only metadata (model, provider, branch, context).
- [ ] Default read path is classifier-independent (plain file reads).
- [ ] No network calls.
- [ ] Model/provider strings are rendered verbatim and never executed.