feat(runner): stage managed Codex credentials (#12398)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Codex can authenticate through an API key or an `auth.json` document. > - Forwarding managed credentials in process arguments, durable session options, or general environment state would leak them across the runner boundary. > - The ACPX sandbox therefore needs a private Codex home, bounded staging, crash-safe cleanup intent, and cross-process ownership that fails closed without relying on writable marker files. > - This pull request establishes that credential boundary before later PRs connect the Codex ACP process. ## Linked Issues or Issue Description **Agent or provider** The qualified Codex ACPX profile. **Why this adapter is useful** Managed installations may already have a Codex login document, while service deployments may provide an API key or inline JSON. The runner needs all three modes without copying ambient home state, accepting ambiguous sources, persisting secret values in ordinary runner state, following links, or allowing concurrent processes to mutate the same credential home. **How the agent is invoked** A later runtime-host pull request acquires this credential lease before opening Codex and closes it during shutdown and failed-start paths. API keys remain launch-only environment values. JSON credentials are atomically staged as `auth.json` inside the isolated Codex home. This pull request does not spawn Codex, add a dependency, register an adapter, or change execution selection. **Additional context** This is the lowest unmerged PR in the runner series and targets `master` directly. It deliberately requires one explicit credential source and never reads the Paperclip server user's ambient home by default. ## What Changed - Add one-use managed Codex credential leases for API-key, inline-JSON, and explicit managed-file modes. - Reject missing and ambiguous source combinations. - Require absolute external managed-file paths, private ownership and permissions on POSIX, bounded documents, no-follow opens, and stable file identity during reads. - Stage JSON through a deterministic private no-follow staging path, scrub stale staging state on admission, sync the file, rename atomically, and sync the parent directory. - Persist a bounded cleanup intent and remove staged or provider-generated authentication files without following links. - Fence each canonical credential home with a markerless 2-of-3 exclusive loopback quorum derived from the current UID and canonical home. One unrelated occupied candidate is tolerated; two occupied candidates fail closed before credential mutation. - Bound stuck parent filesystem operations and cleanup helpers process-wide so an unresponsive filesystem cannot create unbounded work. - Zero temporary credential buffers and make cleanup retryable without allowing a stale cleanup generation to affect a successor. - Add focused tests for credential modes, permissions, malformed and ambiguous sources, symlink rejection, deterministic staging, quorum contention and crash release, stuck-operation bounds, and idempotent cleanup. ## Verification - Prettier and `git diff --check` passed for the exact pushed head. - Greptile reviewed the exact head at 5/5 with no blocking finding; independent static audits found no remaining correctness, security, test-contract, or resource-bound blocker. - GitHub Actions passed on the exact head `29e75afb7b8529b9d070070e0c1462e795d115bb`, including Build, Typecheck, the complete test matrix, e2e, verify, and security checks. One unrelated hosted-runner `npm ENOENT` shard passed on its GitHub rerun. - The diff contains 2 files and does not change `pnpm-lock.yaml`, workflows, dependencies, public package exports, server selection, or UI behavior. ## Risks The primary risks are credential disclosure, concurrent ownership, and cleanup that outlives its authority. Secret values never enter returned identity or durable runner data. Source and destination operations are bounded, no-follow, privately permissioned, and crash-durable. The markerless kernel quorum prevents concurrent home mutation without trusting a replaceable marker, while process-global operation/helper caps prevent unresponsive filesystem work from growing without bound. Cleanup is generation-scoped so a stale retry cannot remove a successor's credential. ## Model Used OpenAI Codex with GPT-5 and repository tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked an existing public item or described the issue in this PR - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal task identifier - [ ] I have run the affected tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have documented the credential and cleanup boundary - [x] All applicable GitHub Actions are green - [x] Greptile is 5/5 with every actionable comment resolved - [x] I will address all review findings before requesting merge
This commit is contained in:
parent
001428a2d6
commit
dba1a2d4f5