メインコンテンツへスキップ

CLI credential custody

Status: accepted account custody contract. CLI commands have not shipped. The native package contract records implemented platforms and verification limits. See the account infrastructure spec.

Sign in once, close the terminal, and keep using Grida. Two commands running together must not invalidate each other's session. Signing out must survive a late response from another process. These are separate requirements from where credentials are stored.

What established CLIs do

Reviewed on 2026-09-06 using official documentation and public source: GitHub CLI v2.100.0, Codex rust-v0.153.4, and Azure CLI/MSAL documentation and their dev branches. These are source observations, not runtime security audits.

CLIPersistent storageRefresh and logout
GitHub CLIOS credential store first; automatically falls back to plaintext when unavailable or failing. --insecure-storage explicitly chooses plaintext.auth refresh reruns authorization to adjust scopes; the inspected CLI persists an access token, not a rotating refresh-token pair. auth logout removes local authentication without server revocation.
CodexCLI auth defaults to a plaintext file. keyring requires OS storage; auto permits file fallback. Source also supports process-only ephemeral storage.Managed ChatGPT auth refreshes and saves replacement tokens. CLI logout attempts remote revocation, then removes local credentials even if revocation fails. API-key logout does not revoke the Platform key.
Azure CLIMicrosoft documents an encrypted MSAL file cache on Windows and plaintext on Linux/macOS. This describes that cache, not every broker-backed login.MSAL Extensions coordinates cache modifications with a file lock and reloads persisted state. This alone does not establish serialization of the complete network refresh.

Sources: GitHub CLI login, refresh implementation, authorization result, and logout; Codex storage modes, storage implementation, and revocation; Azure MSAL cache documentation and cache coordination.

There is no single industry default. File storage is a defensible portability choice; OS storage is a defensible protection default. Neither supplies the entire session lifecycle.

Codex illustrates the concurrency distinction: its inspected refresh path uses an in-process semaphore and rereads credentials before refreshing. Its official CI guidance requires one machine or serialized job stream per credential copy, with refreshed credentials preserved between runs. We should not infer cross-process safety from a refresh method or a keyring backend. Refresh source, CI guidance.

Grida experience

grida auth login uses the OS credential store by default. A separately selected file mode supports machines without a usable credential service. The choice prioritizes OS protection for ordinary login while keeping headless use explicit. Command composition exposes that choice in the CLI delivery.

An unavailable or locked store produces a storage error. It never silently creates a plaintext copy or reports the user as signed out. File mode remains plaintext, protected by user-only OS permissions; it is not described as encrypted. A host without an implemented protection adapter fails closed.

Account status and storage inspection expose only safe metadata, without showing tokens or contacting the issuer. grida auth logout clears this session locally and reports whether its remote revocation succeeded. Application-wide and account-wide revocation remain distinct actions.

Neither mode requires Desktop or a daemon. OS storage protects credentials at rest; it does not establish isolation from an authorized process running as the same OS user, including the user's chosen agent harness.

Persistence contract

Credentials belong to a trusted CLI profile: credential home, issuer, public client ID, and API origin. Persisted identity must match the verified account. Repository configuration cannot redirect credentials or select another store. Desktop cookies, provider keys and ChatGPT subscription OAuth remain separate.

The selected backend is stable for that profile. A read failure never causes an older file or keyring entry to become authoritative. Backend changes need an explicit migration that handles residual copies and incomplete cleanup.

One coordinator per profile serializes reread → refresh → durable save across processes. Every credential mutation participates, including identity verification, login commits, logout, and cleanup; locking individual storage reads and writes is insufficient. Login captures the durable revision before browser interaction and checks it when committing, without holding a long lock during consent. Logout leaves a durable invalidation marker so an earlier login cannot restore the session afterward.

File mode uses atomic replacement and private permissions. Keyring mode still needs process coordination and protected metadata. Accepted refresh rotation is saved before a later identity request can fail. A remote token rotation and a local write cannot form one atomic transaction: a crash between them may still require login. Recovery must report that outcome honestly.

Security ownership

GRIDA-SEC-010 owns registered native OAuth account authority, including consent, callback, token use, durable custody, and session-local revocation. The registry records the implemented controls and their platform limits.

GRIDA-SEC-011 owns the separate local Supabase OAuth provisioning boundary: disposable fixture authority, environment isolation, administrative registration, and scoped cleanup. It does not certify hosted deployment. Hosted provisioning will need its own enforced contract when introduced. The security registry defines both boundaries and their bound files. Existing Desktop and GG boundaries remain in force.