Skip to content

feat(auth): scope-split LLM bearer — /llm/* accepts a second token that never opens writes - #63

Merged
MSD21091969 merged 2 commits into
masterfrom
feat/t263-llm-token-split
Jul 22, 2026
Merged

feat(auth): scope-split LLM bearer — /llm/* accepts a second token that never opens writes#63
MSD21091969 merged 2 commits into
masterfrom
feat/t263-llm-token-split

Conversation

@MSD21091969

Copy link
Copy Markdown
Collaborator

Implements Sam''s t263 design call for the gemini lane: the pilot must reach the kernel''s /llm/* proxy (now bearer-gated by #60) without holding the fleet write token in a browser context.

Change: new --llm-token-file / MOOS_LLM_TOKEN — a second bearer accepted ONLY on /llm/*. The write token still works everywhere it did. llmGate matrix: both-empty = open (backward compatible) · write-token-only = exactly today''s behavior · llm-token-only = /llm/* closed by explicit intent · both = either passes on /llm/*, mutating routes accept the write token ONLY. CORS wildcard stripped regardless, matching writeGate.

Blast radius: a pilot-side leak exposes Gemini egress, never HG writes — the exact containment #60 was built for.

Verified: go build / go vet / full go test ./... green (10 new tests incl. the boundary case: llm token → 401 on POST /rewrites). No new deps; stdlib only.

Deploy plan after merge (Zappa runs, Sam-gated restart): rebuild Z440 kernel → mint secrets/moos-llm-token → launcher gains --enable-llm-proxy + conditional --llm-token-file → restart → pilot PR flips gemini enabled:true + sends the LLM bearer from chrome.storage.

Sovereign-runtime change — Sam''s review + merge.

🤖 Generated with Claude Code

…at never opens writes (t263)

New --llm-token-file / MOOS_LLM_TOKEN: a second bearer accepted ONLY on the
/llm/* egress routes. Rationale: the pilot (a browser-resident client) needs
to reach the Gemini kernel-proxy now that #60 gates egress, but storing the
fleet WRITE token in a browser context would widen the exact blast radius the
bearer was built to contain. With the split, a pilot-side leak exposes Gemini
egress only - never HG writes.

llmGate matrix: both-empty = open (backward compatible); write-token-only =
current behavior; llm-token-only = /llm/* closed by explicit intent; both =
either passes on /llm/*, write routes accept the write token ONLY. CORS
wildcard stripped on /llm/* regardless, matching writeGate. 10 new tests
incl. the boundary case (llm token 401s on POST /rewrites).

authored-by: agent:claude-cowork.hp-z440 / session:sam.z440-cowork-workspace / t263-auth-deploy
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 22, 2026 15:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a scope-split bearer token for the /llm/* proxy so browser-resident clients (pilot/gemini lane) can access LLM egress without holding the fleet write token, reducing blast radius while keeping backward-compatible “open when unset” behavior.

Changes:

  • Introduces Server.llmToken and an /llm/*-specific llmGate that accepts either the write token or the LLM token (and still strips permissive CORS headers).
  • Adds --llm-token-file / MOOS_LLM_TOKEN wiring in cmd/moos and sets the LLM token on the transport server.
  • Expands auth tests to cover the scope-split matrix and the “LLM token must not open writes” boundary.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
internal/transport/server.go Adds llmToken, SetLLMToken, and llmGate; switches /llm/... route from writeGate to llmGate.
internal/transport/auth_test.go Adds tests covering /llm/* token acceptance matrix and rejects LLM token for writeGate.
cmd/moos/main.go Adds --llm-token-file / MOOS_LLM_TOKEN resolution and configures/logs the scope-split LLM bearer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/transport/server.go Outdated
Comment on lines +83 to +87
// SetLLMToken records the scope-split bearer accepted only on /llm/* routes
// (in addition to the write token, which always works there). An empty string
// (the default) means /llm/* is gated by the write token alone. Call once
// before Handler() is attached, like SetAuthToken.
func (s *Server) SetLLMToken(v string) { s.llmToken = v }
Comment thread cmd/moos/main.go
Comment on lines +158 to +161
if llmToken != "" {
tSrv.SetLLMToken(llmToken)
log.Printf("auth: scope-split LLM bearer configured — /llm/* accepts it OR the write token; mutating routes accept the write token only")
}
- SetAuthToken/SetLLMToken comments now spell out the llmGate matrix
  (either-token on /llm/*, both-empty = open, llm token never opens writes)
- the scope-split startup log line is now posture-conditional: proxy-off =
  token inert; no write token = mutating routes remain unauthenticated

authored-by: agent:claude-cowork.hp-z440 / session:sam.z440-cowork-workspace / t263-gemini-lane
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MSD21091969

Copy link
Copy Markdown
Collaborator Author

Both Copilot comments addressed in f9fbe2b — the SetAuthToken/SetLLMToken doc comments now spell out the llmGate matrix exactly (either-token on /llm/*, both-empty = open mode, llm token never opens writes), and the scope-split startup log line is posture-conditional: proxy-off logs the token as inert (no route registered); no-write-token logs that mutating routes remain unauthenticated. Suite re-run green. Ready for merge.

authored-by: agent:claude-cowork.hp-z440 / session:sam.z440-cowork-workspace / t263-gemini-lane

@MSD21091969
MSD21091969 merged commit 26fbf0f into master Jul 22, 2026
2 checks passed
@MSD21091969
MSD21091969 deleted the feat/t263-llm-token-split branch July 22, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants