# Signed-in coding agents

Claude Code and Codex can run on a subscription instead of an API key — but
their sign-in lives in a file on the machine you signed in on. A fresh sandbox
has no such file, so every one of them asks you to sign in again.

seekrit keeps that sign-in for you. Sign in **once**, from any machine, and
every sandbox you start through `seekrit run` is already signed in — on any
provider, because the only thing the sandbox needs is the `seekrit` CLI and a
token:

```bash
# once, on your laptop
seekrit agent-login codex --app agents --env ava

# in every sandbox, from then on
seekrit run --agent-login codex -- codex exec "Add a /health endpoint"
```

The login is encrypted on your machine and decrypted inside the sandbox, like
every other seekrit secret: the seekrit API stores ciphertext it cannot read.

No terminal handy? Sign in from the [dashboard](#from-the-dashboard) instead.

## Pick the credential

| You run | Store | What the sandbox runs | Refreshes? |
| --- | --- | --- | --- |
| Claude Code, many sandboxes at once | A long-lived token — `seekrit agent-login claude` | `seekrit run -- claude -p "…"` | No — lasts a year |
| Claude Code, needing the full account sign-in | Its login file — `seekrit agent-login claude --from …` | `seekrit run --agent-login claude -- claude …` | Yes, saved back |
| Codex on a ChatGPT plan | Its login file — `seekrit agent-login codex` | `seekrit run --agent-login codex -- codex …` | Yes, saved back |
| Either, on API billing | `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` as plain secrets | `seekrit run -- …` | No |

The column that matters is the last one. A token that never refreshes is just a
secret. A login that **refreshes** is harder, and is what the rest of this page
is about.

## Claude Code: the long-lived token

`claude setup-token` issues a token that lasts a year and never refreshes, made
for exactly this. `seekrit agent-login claude` runs it, asks you to paste the
token it prints, and stores it as `CLAUDE_CODE_OAUTH_TOKEN` — the variable Claude
Code reads:

```bash
seekrit agent-login claude --app agents --env ava
```

From then on it is an ordinary secret: any `seekrit run` in that environment
injects it, and nothing is written back.

```bash
seekrit run -- claude -p "Add a /health endpoint" --dangerously-skip-permissions
```

The token covers model calls. Features tied to a full account sign-in may need
the login file instead (next section). Already have a token? Pipe it in:
`printenv CLAUDE_CODE_OAUTH_TOKEN | seekrit agent-login claude --token-stdin`.

## Codex: sign in once

```bash
seekrit agent-login codex --app agents --env ava
# no browser on this machine? add --device-auth
```

This runs `codex login` in a throwaway home directory, so the login seekrit
stores is a **new** sign-in that belongs to seekrit alone, not a copy of your
laptop's. It is stored as `CODEX_LOGIN`.

To store a login you already have — say you signed in inside a sandbox — pass
the file:

```bash
seekrit agent-login codex --from ~/.codex/auth.json --move
```

> **Warning:** Without `--move`, the file and the copy in seekrit share one refresh token, and whichever refreshes first signs the other out. Keep exactly one of them.

The same `--from` works for Claude Code's full login,
`~/.claude/.credentials.json` on Linux, stored as `CLAUDE_LOGIN`.

## From the dashboard

Every environment page has a **Coding agent sign-ins** card that does the same
without a terminal.

**Sign in Codex** runs the same device sign-in as `codex login --device-auth`,
in the browser tab: the dashboard shows a one-time code, you enter it at
`auth.openai.com/codex/device`, and the tab waits for you to approve. OpenAI
sends the sign-in straight back to the tab — its auth endpoints accept requests
from other sites — where it is turned into the same `auth.json` Codex writes,
encrypted with the environment's key, and stored as `CODEX_LOGIN`. seekrit's
servers never see it. Your keyring is unlocked *before* you are sent to OpenAI,
so an approved sign-in is never lost to a cancelled unlock.

> **Warning:** Enter a code only because you started the sign-in yourself, in this dashboard. If a website or another person hands you a code, close the dialog: that is how device-code phishing works.

**Add Claude Code token** takes the token `claude setup-token` prints. Claude
Code has no device sign-in, and Anthropic's token endpoint does not accept
requests from other sites, so the dashboard cannot complete a Claude sign-in by
itself — doing it on seekrit's servers would mean those servers saw your token.
Run `claude setup-token` on any machine with Claude Code, paste the result, and
the tab encrypts it as `CLAUDE_CODE_OAUTH_TOKEN`. The same dialog takes a full
`.credentials.json` (stored as `CLAUDE_LOGIN`) when you need the whole account
sign-in.

## Run it in a sandbox

Give the sandbox the `seekrit` CLI and a [service token](/docs/guides/service-tokens)
bound to the environment that holds the login, then launch the agent through
`seekrit run --agent-login`:

```bash
npm i -g @seekrit/cli @openai/codex
export SEEKRIT_TOKEN=skt_…
seekrit run --agent-login codex -- codex exec "Add a /health endpoint"
```

That works the same on E2B, Modal, Daytona, Vercel Sandbox, Fly, a CI runner or
a container — anything that can run a command. `--agent-login claude` and
`--agent-login codex` can both be given, and `--agent-login codex=OTHER_NAME`
reads a login stored under another name.

## What `--agent-login` does

Before the agent starts, seekrit writes the login file where the agent looks
for it (`$CODEX_HOME/auth.json`, or `$CLAUDE_CONFIG_DIR/.credentials.json`;
`~/.codex` and `~/.claude` by default). When the agent exits, the file is
removed. A run never overwrites a login that seekrit did not put there.

In between, it keeps the login alive. Both agents sign in with an OAuth
**refresh token that changes every time it is used**. If a sandbox refreshed and
threw the new token away with its disk, the next sandbox would present the old
one, the provider would reject it as reused, and you would be signed out. So:

- **Every refresh is saved back.** The file is watched, and the moment the agent
  refreshes, the new login is encrypted and stored — conditioned on the version
  this sandbox read, so two sandboxes can never silently overwrite each other.
- **Running sandboxes pick up each other's refreshes.** Near refresh time a
  sandbox checks for a newer copy every few seconds and writes it to disk. Both
  agents re-read their login before refreshing, so they adopt it instead of
  spending a token a sibling already used.
- **One sandbox refreshes; the rest wait.** A sandbox that starts when the login
  is due to refresh marks it as being refreshed first. Others starting at the
  same moment wait for that refresh — up to two minutes — rather than each
  refreshing the same token.

The marker lives inside the encrypted value, so the API sees nothing but
ciphertext and version numbers. seekrit never calls Anthropic's or OpenAI's
token endpoints — the agent refreshes itself, exactly as on your laptop, and
seekrit only moves the file.

If a refresh cannot be saved — seekrit unreachable when the agent exits — the
run says so, exits non-zero, and leaves the login on disk. The next
`seekrit run --agent-login` on that machine saves it.

## Check on a login

```bash
seekrit agent-login status --app agents --env ava
```

```text
NAME                     AGENT   KIND              VERSION  NEXT REFRESH
CLAUDE_CODE_OAUTH_TOKEN  claude  long-lived token  v1       never
CODEX_LOGIN              codex   login file        v14      2026-11-19T08:12:44.000Z
```

Signed out anyway — the provider revoked it, or a sign-in was lost? Run
`seekrit agent-login codex` again; it replaces the stored login, and the next
sandbox uses the new one.

## Where to keep logins

- **One environment per person.** A subscription login is a personal
  credential. Spreading one person's sign-in across a team's agents is account
  sharing as far as Anthropic and OpenAI are concerned, and usage limits are per
  account anyway. Keep each person's logins in an environment only they and
  their sandboxes can read.
- **Not a protected environment.** A refresh has to be saved the moment it
  happens, so an environment with [approvals](/docs/guides/approvals) is
  refused: the refresh would wait for a reviewer while the old token is already
  spent.
- **Little else beside them.** The sandbox's token can read and write everything
  in its environment, and the agent can read the token. An environment holding
  the login and the agent's own keys — not production — keeps that small.

> **Note:** The agent holds its own login: it is the credential the agent signs in with, so it has to be on the sandbox's disk while the agent runs, and code the agent runs can read it. For credentials the agent *uses* rather than *is* — API keys for the services it calls — keep them outside the sandbox with [`seekrit-proxy`](/docs/guides/agent-proxy).

## Limits

- **Two sandboxes already running at refresh time can still collide.** If both
  refresh in the same few seconds, before either sees the other's copy, one of
  them may be signed out. For a large parallel fleet, prefer a credential with no
  refresh to race: the Claude Code long-lived token, or an API key.
- **Claude Code on macOS** keeps its login in the Keychain, not in
  `.credentials.json`, so `--agent-login claude` takes effect on Linux —
  sandboxes, containers and CI. The long-lived token works everywhere.
- **`seekrit run` only.** The `seekrit-run` launcher and the SDKs inject
  variables; they do not manage login files.
- **The dashboard's Codex sign-in borrows Codex's public sign-in client.** It
  sends exactly the requests `codex login --device-auth` does. If OpenAI ever
  stops accepting them from a browser, `seekrit agent-login codex
  --device-auth` still works, because it runs the real `codex`.

## See also

- [Agent sandboxes](/docs/guides/sandboxes) — getting the agent's *other* secrets in, or keeping them out
- [`seekrit run`](/docs/guides/run) — the launcher this builds on
- [CLI reference](/docs/reference/cli#agent-logins-agent-login) — every flag
- [Service tokens](/docs/guides/service-tokens) — the credential a sandbox resolves with
