Phase 6 — BYO Subscription via Per-User HOME
Goal
Support OAuth-based subscriptions (Claude Pro/Max, ChatGPT Plus where applicable) without reimplementing each provider's auth flow. The mechanism is: allocate a per-user HOME directory, run the CLI's own login flow with that HOME, and reuse the resulting on-disk auth state on subsequent invocations.
Preconditions
Phases 1–3 shipped (Tier-1 from phase 5 is helpful but not strictly required — per-user HOMEs work equally in Tier 0).
Deliverables
auth/byo_subscription.pyimplementing per-user HOME allocation and lookup.- Storage layout:
<plugin_storage>/external_agent/homes/<user_id>/<backend>/. - A login-proxy mechanism that lets a user run the CLI's interactive login with the per-user HOME applied. The exact UX of this is left to you — see Implementation steps 3 below for the two viable options.
- Settings UI surface: "Connect
account" button per backend that supports this mode. Shows connected/disconnected state. - New
mode: byo_subscriptionvalue for the per-backend config (already named in §4.6 of the index). - Credential resolution priority extended: per-user subscription HOME (if backend mode is
byo_subscription) → per-user BYO key (modebyo_key) → technical-user (modetechnical_user) → error. - Tests for HOME allocation and injection; a manual smoke procedure documented in the handoff for the actual login flow (not automatable in CI without real browser-based provider auth).
New files
src/codumentor/plugins/external_agent/auth/byo_subscription.py
tests/test_external_agent_auth_byo_subscription.py
Touched:
plugin.py(wire the new resolution path)routes.py(add login-proxy routes — see below)ui/plugins/external_agent/settings.tsx(add the Connect button + connected state)adapters/claude_code.pyand any other adapter whose CLI supports OAuth login (set a flag or class attributesupports_byo_subscription: bool = True)
Implementation steps
- Per-user HOME allocation:
byo_subscription.get_home(user_id, backend) -> Pathreturns and lazily creates<plugin_storage>/external_agent/homes/<user_id>/<backend>/. Permissions:0o700, owned by the Codumentor process user. Add a.gitignoreif the storage dir is git-tracked anywhere.
- Injection at call time: when a backend's mode is
byo_subscription, passhome=<that path>toadapter.build_env. Adapters already accept ahomeargument per §4.1 of the index; ensure they setHOMEand any other env vars the CLI looks at (e.g.,XDG_CONFIG_HOME) to that path.
- Login-proxy UX — pick one and document:
Option A — Guided manual setup (recommended for first cut). The Connect button shows the user a one-line shell command to run on the same machine as Codumentor:
```
HOME=<plugin_storage>/external_agent/homes/<user_id>/<backend> claude /login
auth/probe` to confirm the HOME now contains a valid session. Simple, no terminal-proxy code, ships fast.
The user runs it, completes the browser-based login, and clicks "I've finished." Codumentor then runs an
Option B — In-app terminal. A WebSocket-backed PTY that runs the login command inside the proxied HOME and streams the interactive output into a terminal widget in the UI. More polished UX, but substantial implementation work and a new attack surface. Defer unless option A is unacceptable for the deployment context.
Default to A. If you choose B, surface to the orchestrator first.
- Resolution priority in the credential resolution path: check the configured backend mode first. If
byo_subscription, setcreds = None(no api_key) and passhome. If the on-disk HOME is empty / unconfigured, return a structured error (auth/probereturnsauthenticated: false).
- Settings UI: per-backend row gains: - "Account connected" (or "Not connected") status, computed from a backend probe with the per-user HOME applied. - "Connect" button → opens a dialog with the command (option A) or terminal (option B). - "Disconnect" button → deletes the per-user HOME directory.
- Tests: cover HOME allocation, env injection (assert the spawned subprocess sees the correct
HOME), and the resolution priority chain. The actual login is not testable in CI; document a manual smoke procedure.
Acceptance criteria
make testpasses.- Manual smoke: set Claude Code backend mode to
byo_subscription, click Connect, run the login command, verify a subsequentclaude_code(query="hello")works and (if visible from Anthropic's side) is billed against the user's Claude Pro/Max subscription, not against any API key. - Disconnecting properly clears the HOME and subsequent calls fail with
auth_required.
Out of scope
- Provider-specific OAuth flows in Codumentor. We never see the OAuth tokens — the CLI handles them. This is intentional.
- Cross-machine HOMEs. Users connect on the machine where Codumentor runs. If Codumentor is on a server, the login command needs port-forwarding for the browser — document but do not solve.
- Refresh-token automation. The CLI handles its own refresh.
- Migrating between modes (e.g., switching a user from
byo_keytobyo_subscriptionand back). Allowed but no UI assistance.
Handoff
Fill out the template. In "Contracts established/changed":
- The exact filesystem layout for per-user HOMEs.
- Which adapters opted into
supports_byo_subscription. - The final UX choice (option A or B) and the exact route paths.
- The full credential resolution priority chain after this phase.
In "Surprises": call out any CLI that did not behave as expected when HOME was overridden (some CLIs hard-code paths under ~).