Deploy credentials¶
deploy writes a saved credential into a target provider home. The target is
always an explicit --home argument, and any existing credential is
snapshotted before it is replaced.
Target home resolution¶
--home names the provider home directory, not the credential file:
| Provider | Pass | lairen writes |
|---|---|---|
codex |
--home ./project/.codex |
./project/.codex/auth.json |
kimi |
--home ./project/.kimi-code |
./project/.kimi-code/credentials/<name>.json |
claude |
--home ./project/.claude |
.credentials.json, plus oauthAccount in .claude.json |
commandcode |
--home ./project/.commandcode |
auth.json |
cursor |
--home ~/.config/Cursor/User/globalStorage |
state.vscdb |
opencode |
--home ./project/opencode |
auth.json |
Tilde expansion and relative paths are supported. Missing parent directories
are created during the write. For cursor, the target state.vscdb must
already exist; lairen does not create a Cursor database from scratch.
For Kimi, lairen always writes credentials/kimi-code.json in the target home,
regardless of the source file's name. scan can discover environment-scoped
files such as kimi-code-env-*.json; deploying one normalizes it to the
standard name.
Resolution of REF¶
REF follows the rules in Identities and refs:
lairen deploy codex/alice@example.com --home ./project/.codex
lairen deploy work --home ./project/.codex
lairen deploy 'alice@example.com' --home ./project/.codex
work is an alias, alice@example.com is a bare id or label. Aliases resolve
before ids and labels; the full provider/id form is always exact and is the
only unambiguous choice when short handles collide.
What deploy does¶
- Resolves the account in the registry.
- Loads the credential blob from the store.
- Reads any existing target credential.
- Copies the existing target into a snapshot directory unless
--no-backupis set. - Composes the new file contents. Most providers replace the file; a few merge into an existing structure (see below).
- Writes atomically with mode
0600. - Records
last_deployed_atandlast_deployed_homein the registry. - Appends a
deployentry to the deployment log, whichlairen statususes for lease reporting.
Output:
deployed codex/alice@example.com -> /home/alice/project/.codex/auth.json
snapshot: /home/alice/.local/share/lairen/state/snapshots/codex-20261026T120000000000Z/auth.json
The snapshot line appears only when a target file existed and backups were enabled.
Sync before you redeploy¶
deploy copies the stored credential; it cannot see tokens that a target's
agent refreshed after the last registration. Before deploying an account to a
new home, pull the freshest known copy back into the store:
sync reads known deployed homes and the source home, compares refresh and
access expiry, and updates only the store. It never writes a home. A home whose
credential now belongs to a different identity is reported and must be rebound
explicitly with lairen update. Use deploy --refresh as a one-step
alternative; it syncs known homes and the target before writing. See
sync.
Overwrite protection¶
Before replacing an existing target, deploy compares its live credential with
the stored copy for that account. If the target is newer — the home ran and
refreshed after the last registration — deploy refuses:
refusing to overwrite /home/me/project/.codex/auth.json: it holds a newer
credential for codex/alice@example.com than the registry copy.
Run `lairen sync codex/alice@example.com --home /home/me/project/.codex` first.
Pass --force to overwrite anyway.
Sync the account, then deploy again, or use deploy --refresh for the same
account. --force overwrites without capturing the target tokens; the normal
pre-deploy snapshot still preserves the file. A target whose credential is
unregistered produces a warning rather than a refusal.
Replace versus merge¶
| Provider | Behavior |
|---|---|
codex |
Replaces auth.json with the saved blob. |
kimi |
Replaces the credential file with the saved blob. |
claude |
Replaces .credentials.json, then merges oauthAccount fields into .claude.json. |
commandcode |
Writes {"apiKey": "<key>"} to auth.json. |
cursor |
Updates Cursor's ItemTable keys in place; other rows are preserved. |
opencode |
Preserves other auth.json slots and replaces only opencode-go. |
Merging providers still create a snapshot of the file they modify.
Snapshots and rollback¶
Snapshots live under the state directory:
To roll back a Codex deployment:
cp ~/.local/share/lairen/state/snapshots/codex-20261026T120000000000Z/auth.json \
~/project/.codex/auth.json
chmod 600 ~/project/.codex/auth.json
Snapshots are never pruned automatically. Delete old timestamp directories when they are no longer useful.
Use --no-backup when the target is disposable or when a script already
manages backups:
Swapping identities between projects¶
Given two project-local Codex homes:
# Capture the login each project currently holds
lairen register codex --home ~/code/project-a/.codex
lairen register codex --home ~/code/project-b/.codex
# Inspect the ids
lairen list
# Put A's account into B and B's account into A
lairen deploy codex/alice@example.com --home ~/code/project-b/.codex
lairen deploy codex/bob@example.com --home ~/code/project-a/.codex
Each deployment snapshots the file it replaces, so both previous project credentials remain recoverable. Both accounts remain in the registry.
If one project has no login yet, register only the source, then deploy into the empty target. lairen creates the target home and credential file.
Verify a deployment¶
doctor reports whether each provider home now contains a login. show
reports the account's metadata and the stored blob's expiry, not the contents
of the target file.
Before you deploy¶
If the account has been used in another home, run lairen sync REF or pass
--refresh first. Otherwise you deploy the stored copy, which may predate the
tokens that home is actually using.
Stop any running agent that uses the same saved session, and read the OAuth rotation rule. Deployment is a copy, not a move; two active homes sharing one refresh token can invalidate each other.
For Cursor, close the application before deploying so the SQLite database is not locked.