You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Runtime connections are centralized through T3Config.resolve() in src/runtime/layer.ts, so environment selection belongs there.
Public package exports include ./config and ./auth; exported types change with the config shape.
Proposed decisions
Environment names
Environment names are non-empty strings containing only [A-Za-z0-9._-].
Selection precedence
Resolve the base stored environment from --environment, then T3CLI_ENV, then config default.
If both T3CODE_URL and T3CODE_TOKEN are set, they override the selected environment connection.
If exactly one of T3CODE_URL or T3CODE_TOKEN is set, fail with a config error instead of mixing env and stored credentials.
If both env vars participate, resolved config source is env; otherwise config.
If selected environment does not exist, fail before trying to connect.
Project/thread scope
Per environment. Projects and thread IDs belong to the selected server.
Default names
auth local default name: local
auth pair --url <url> default name: hostname slug from paired server URL
--name <name> overrides default name for both commands
Pair/local write behavior
successful auth pair / auth local writes the named environment
written environment becomes default only when it is the first stored environment
otherwise default changes only via auth use <name>
duplicate name follows replacement rules below
Duplicate names
If --name already exists:
Context
Behavior
--replace
overwrite that environment and make it default
interactive human TTY
prompt Environment '<name>' already exists. Replace? [y/N], default no
non-interactive, no --replace
fail with message to pass --replace
Use existing Effect CLI prompt style from src/cli/confirm.ts.
Credential storage
Config file mode is 0600. Do not use group-readable or group-writable modes like 0640 / 0660.
V2 config stores environment metadata in plaintext and stores tokens only as encrypted blobs. No raw token is written to v2 config.
Encryption:
cipher: AES-256-GCM
key: random local master key
AAD: schema version + environment name + url + local flag
Key source:
OS keyring master key by default.
Automatic fallback to local key file at ~/.config/t3cli/key or $XDG_CONFIG_HOME/t3cli/key with mode 0600 when keyring is unavailable.
Do not add user-facing backend/status reporting for which key source was used.
Resolved questions
Resolved: auth pair / auth local makes the written environment default only when creating the first environment. After that, default changes only via auth use <name>.
Resolved: T3CODE_URL and T3CODE_TOKEN must be set together. Setting only one fails instead of mixing env and stored credentials.
Resolved: when deleting the default environment, clear default even if other environments remain. User must run auth use <name> or pass --environment.
Resolved: environment names are strict slugs: non-empty, only [A-Za-z0-9._-].
Resolved: auth list --format json includes stored fields only: name, url, local, default, and active. It does not contact servers or store extra token metadata.
Current v1 flat config is read as v2 with one environment:
env name: local when local: true, otherwise hostname slug from url, otherwise default
default: migrated env name
empty/missing config remains unauthenticated
All auth/config mutations write v2. Do not write flat v1.
Possible last-env removal shape:
{ "version": 2, "environments": {} }
CLI surface
t3cli --environment <name> ... # global runtime selection
t3cli auth pair --url <url> [--name <name>] [--replace] [--local] [--format json]
t3cli auth local [--name <name>] [--replace] [existing local flags] [--format json]
t3cli auth list [--format json]
t3cli auth use <name> [--format json]
t3cli auth unpair [--name <name>] [--yes] [--format json]
t3cli auth status [--format json]
auth status reports active environment name when config-backed, plus current url, local, source, role, and expiry.
auth list never prints tokens. JSON includes only stored fields: name, url, local, default, and active.
auth use <name> only changes default; it does not contact the server.
auth unpair
Scope for this issue: local credential removal only.
Server self-revoke is not supported by t3code for the current session (current_session_revoke_not_allowed), so no revoke flow belongs in this issue. Document that auth unpair removes local CLI credentials and any remote token can remain valid until natural expiry.
Behavior:
t3cli auth unpair # remove default environment
t3cli auth unpair --name work # remove specific environment
t3cli auth unpair --yes # skip confirmation; required in non-interactive mode
No token values are printed.
Use --yes rather than --force, matching existing destructive-confirm CLI convention.
Implementation checklist
Change config types/schema in src/config/service.ts and src/config/layer.ts to parse v1 flat + v2 named encrypted-token config.
Add environment selection to T3Config.resolve().
Add root --environment <name> and T3CLI_ENV support.
Extend auth write path in src/auth/layer.ts / src/auth/type.ts for named writes, encrypted token writes, and replacement behavior.
Add --name + --replace to auth pair and auth local.
Summary
Add multiple named auth environments to one
t3cliinstall, so one CLI config can hold credentials for local, staging, and remote t3code servers.Current
masterstate checked: packaget3code-cli@0.11.0at1d46c60.Current state
src/cli/auth.tsand currently expose onlyauth pair,auth local, andauth status.src/config/service.ts+src/config/layer.ts.{ url?, token?, local? }at~/.config/t3cli/config.jsonor$XDG_CONFIG_HOME/t3cli/config.json.T3Config.resolve()already appliesT3CODE_URL/T3CODE_TOKENabove stored config.T3Config.resolve()insrc/runtime/layer.ts, so environment selection belongs there../configand./auth; exported types change with the config shape.Proposed decisions
Environment names
Environment names are non-empty strings containing only
[A-Za-z0-9._-].Selection precedence
--environment, thenT3CLI_ENV, then configdefault.T3CODE_URLandT3CODE_TOKENare set, they override the selected environment connection.T3CODE_URLorT3CODE_TOKENis set, fail with a config error instead of mixing env and stored credentials.sourceisenv; otherwiseconfig.If selected environment does not exist, fail before trying to connect.
Project/thread scope
Per environment. Projects and thread IDs belong to the selected server.
Default names
auth localdefault name:localauth pair --url <url>default name: hostname slug from paired server URL--name <name>overrides default name for both commandsPair/local write behavior
auth pair/auth localwrites the named environmentdefaultonly when it is the first stored environmentauth use <name>Duplicate names
If
--namealready exists:--replaceEnvironment '<name>' already exists. Replace? [y/N], default no--replace--replaceUse existing Effect CLI prompt style from
src/cli/confirm.ts.Credential storage
Config file mode is
0600. Do not use group-readable or group-writable modes like0640/0660.V2 config stores environment metadata in plaintext and stores tokens only as encrypted blobs. No raw token is written to v2 config.
Encryption:
Key source:
~/.config/t3cli/keyor$XDG_CONFIG_HOME/t3cli/keywith mode0600when keyring is unavailable.Do not add user-facing backend/status reporting for which key source was used.
Resolved questions
auth pair/auth localmakes the written environment default only when creating the first environment. After that, default changes only viaauth use <name>.T3CODE_URLandT3CODE_TOKENmust be set together. Setting only one fails instead of mixing env and stored credentials.auth unpairrequires confirmation. Human TTY prompts; non-interactive mode requires--yes.defaulteven if other environments remain. User must runauth use <name>or pass--environment.[A-Za-z0-9._-].auth list --format jsonincludes stored fields only:name,url,local,default, andactive. It does not contact servers or store extra token metadata.Config schema v2
{ "version": 2, "default": "home", "environments": { "home": { "url": "https://example.com", "local": false, "token": { "kind": "encrypted", "alg": "aes-256-gcm", "key": "default", "nonce": "...", "ciphertext": "...", "tag": "..." } }, "local": { "url": "http://127.0.0.1:3000", "local": true, "token": { "kind": "encrypted", "alg": "aes-256-gcm", "key": "default", "nonce": "...", "ciphertext": "...", "tag": "..." } } } }Current v1 flat config is read as v2 with one environment:
localwhenlocal: true, otherwise hostname slug fromurl, otherwisedefaultdefault: migrated env nameAll auth/config mutations write v2. Do not write flat v1.
Possible last-env removal shape:
{ "version": 2, "environments": {} }CLI surface
auth statusreports active environment name when config-backed, plus currenturl,local,source, role, and expiry.auth listnever prints tokens. JSON includes only stored fields:name,url,local,default, andactive.auth use <name>only changesdefault; it does not contact the server.auth unpairScope for this issue: local credential removal only.
Server self-revoke is not supported by t3code for the current session (
current_session_revoke_not_allowed), so no revoke flow belongs in this issue. Document thatauth unpairremoves local CLI credentials and any remote token can remain valid until natural expiry.Behavior:
No token values are printed.
Use
--yesrather than--force, matching existing destructive-confirm CLI convention.Implementation checklist
src/config/service.tsandsrc/config/layer.tsto parse v1 flat + v2 named encrypted-token config.T3Config.resolve().--environment <name>andT3CLI_ENVsupport.src/auth/layer.ts/src/auth/type.tsfor named writes, encrypted token writes, and replacement behavior.--name+--replacetoauth pairandauth local.auth list,auth use, andauth unpair.0600key-file fallback.skills/t3code-cli/reference/setup.md+skills/t3code-cli/reference/commands.md.Acceptance criteria
auth use, then run an existing command against the selected server.--environment <name>overrides config default for one invocation.T3CLI_ENV=<name>works when--environmentis omitted.T3CODE_URL/T3CODE_TOKENoverride selected config only when both are set; setting exactly one fails.--replaceor confirmed prompt.auth listhides tokens.0600.auth unpairremoves local credentials, clearsdefaultwhen deleting the default environment, and documents token expiry behavior.Out of scope
Related
complete environmentsshould use this config once implemented.