Skip to content

feat(codex): give each managed account a self-contained CODEX_HOME; retire shared mirror (PR-E) - #9179

Closed
brennanb2025 wants to merge 2 commits into
brennanb2025/codex-ma-a-config-hygienefrom
brennanb2025/codex-ma-e-per-account-home
Closed

brennanb2025 wants to merge 2 commits into
brennanb2025/codex-ma-a-config-hygienefrom
brennanb2025/codex-ma-e-per-account-home

Conversation

@brennanb2025

Copy link
Copy Markdown
Contributor

What & why

Today real-home routing fires only for the system-default (host === null) account. Every managed account still injects the single shared runtime home (codex-runtime-home/home) as CODEX_HOME and hot-swaps auth.json into it on select. Two consequences:

  • GAP-1: the shared mirror can't be deleted in Phase 2 while any managed account exists.
  • GAP-5: two managed accounts share one auth.json, so concurrent panes for different managed accounts race that file (token refresh from one clobbers the other).

This PR makes each codex-accounts/<id>/home a complete, directly-injected CODEX_HOME when the real-home flag is ON. The account's home is both the launch home and the credential store, so codex reads/refreshes auth.json in place — no hot-swap, no read-back reconciliation, no shared file to race.

Changes

  • codex/codex-home-paths.ts — syncSystemCodexResourcesIntoManagedHome(managedHomePath?) links the system resources (skills/hooks/plugins/plugin-state/profile-v2/themes/prompts/AGENTS.md) into any managed home, reusing the existing ownership-marker + owned-fallback-copy discipline. It only ever reads ~/.codex and writes symlinks/owned copies into the target home — never symlinks into or mutates ~/.codex.
  • codex-accounts/runtime-home-service.ts — prepareForCodexLaunch / prepareForRateLimitFetch / syncForCurrentSelection route a host managed account to its own home and skip the shared-home snapshot/hot-swap/read-back machinery. Resources + config are materialized into the per-account home at launch. A vanished auth.json drops the selection and falls back to the system default. getHostCodexHomePathsForSessionDiscovery scans every per-account home so account-scoped rollouts still surface in the AI Vault.
  • codex/hook-service.ts + codex/hook-trust-promotion.ts — install / getStatus / refreshRuntimeUserHooks (and the promotion/provenance helpers) accept a runtimeHomePath, so hooks.json + config.toml + RPC-granted trust land in the per-account home. Default stays the shared mirror (flag-OFF/WSL/SSH unchanged).
  • main/index.ts — the launch prep passes the resolved per-account home into the hook install.
  • codex-accounts/service.ts — config mirroring into a self-contained host home uses the trust-preserving merge (syncSystemConfigIntoManagedCodexHome) instead of the sanitize-overwrite, so hook/project trust codex granted in that home survives account switches (no re-approval / app-server re-grant churn). WSL homes and the flag-OFF path keep the existing sanitize path.
  • ai-vault/codex-session-root-dedup.ts — codex-accounts/<id>/home ranks as canonical managed (tier 1) alongside the shared runtime home, so hardlinked aliases collapse onto the per-account home over WSL/remote/custom roots (real home null still wins).

Routing matrix

# Selection Flag Launch CODEX_HOME auth
1 system-default OFF shared runtime mirror snapshot/restore (unchanged)
2 system-default ON null → real ~/.codex native (unchanged, PR-B)
3 host managed ON codex-accounts/<id>/home per-account, in place
4 host managed, 2 concurrent ON two distinct per-account homes no shared auth.json → no race
5 host managed OFF shared runtime mirror + hot-swap unchanged opt-out

Rows 3–5 are new/pinned by this PR; unblocks Phase-2 mirror deletion (no lane injects the shared home when the flag is ON).

Guardrails

  • ~/.codex is never mutated: resources link/copy from it into the per-account home; auth stays per-account; the only write-back to ~/.codex is the pre-existing whitelisted config-settings promotion (model/approvals), identical to what the shared home already does.
  • Ownership-marker discipline reused for all new linking; removal (rmSync) of a per-account home removes symlinks as links, never following into ~/.codex.
  • Flag-OFF and the system-default real-home (null) lane are byte-identical to today.
  • [Bug]: Orca's private CODEX_HOME causes a Codex auth-revocation war with the user's ~/.codex #5370 preserved: the nested-Orca CODEX_HOME === ORCA_CODEX_HOME daemon strip is value-agnostic and still fires for per-account home values.
  • Config is global across accounts, auth is per-account — consistent with the documented "swap credentials, not config defaults" intent.

Test evidence (sandboxed temp homes only)

npx vitest run --config config/vitest.config.ts (affected suites) + pnpm run typecheck:node — all green. New/updated coverage:

  • two managed accounts get distinct homes without racing one auth.json (each home keeps its own auth; shared auth.json never written).
  • per-account home materializes its own resources + config on launch; ~/.codex untouched.
  • hook-service installs managed hooks + trust into the per-account home, not the shared mirror or ~/.codex.
  • dedup ranks per-account homes and still collapses hardlinked aliases (real home still canonical).
  • explicit flag-OFF managed regression (shared home + hot-swap).
  • missing-auth active account drops selection → real home.
  • nested-Orca per-account CODEX_HOME strip ([Bug]: Orca's private CODEX_HOME causes a Codex auth-revocation war with the user's ~/.codex #5370).
  • codex + codex-accounts suites: 622 passed.

Notes

Do not merge — draft for review.

…etire shared mirror (PR-E)

With the real-home flag ON, a host managed account now launches directly
against its own codex-accounts/<id>/home instead of the shared runtime
mirror + auth.json hot-swap:

- codex-home-paths: syncSystemCodexResourcesIntoManagedHome links system
  resources into any managed home (ownership-marker discipline; never
  symlinks into / mutates ~/.codex).
- runtime-home-service: prepareForCodexLaunch / prepareForRateLimitFetch /
  syncForCurrentSelection route the per-account home directly and skip the
  shared-home hot-swap + token read-back; each home keeps its own auth in
  place (fixes GAP-5 concurrent auth race). Session discovery scans every
  per-account home.
- hook-service / hook-trust-promotion: install/getStatus/refresh accept a
  runtimeHomePath so hooks + RPC-granted trust land in the per-account home.
- service: config mirror into a self-contained home uses the trust-
  preserving merge so granted hook/project trust survives account switches.
- codex-session-root-dedup: rank codex-accounts/<id>/home as canonical
  managed alongside the shared runtime home.

Flag-OFF and the system-default real-home (null) lane are unchanged; the
nested-Orca CODEX_HOME===ORCA_CODEX_HOME daemon strip (#5370) is preserved.
Sandboxed tests only; ~/.codex is never mutated.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing as superseded: #9501 (real-home routing + self-contained multi-account homes) explicitly lists this draft as superseded in its description, and that consolidation merged and shipped in v1.4.149 stable.

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.

1 participant