Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7bb5882
feat(config): read auth settings like the legacy CLI
pjcdawkins Oct 1, 2026
2867d32
feat(auth): add a Go credential store
pjcdawkins Oct 1, 2026
cd85366
feat(auth): refresh tokens in Go under a lock
pjcdawkins Oct 1, 2026
cd88186
feat: move the auth commands to Go
pjcdawkins Oct 1, 2026
de9dc09
feat(legacy): get tokens and auth state from Go
pjcdawkins Oct 1, 2026
0cd3ea5
fix: match the legacy CLI's api-token-login and --max-age errors
pjcdawkins Oct 1, 2026
57b6cb0
test: cover migration, refresh concurrency, crashes and step-up
pjcdawkins Oct 1, 2026
9722662
chore: fix lint findings
pjcdawkins Oct 1, 2026
4cf36a7
docs: describe the Go auth stack
pjcdawkins Oct 1, 2026
afb58b9
test: skip the permission check on Windows
pjcdawkins Oct 1, 2026
8fb68f5
fix: list only a native command's own and visible options in help
pjcdawkins Oct 1, 2026
a5767fa
fix(auth): address review findings
pjcdawkins Oct 1, 2026
525f5b5
fix(auth): address PR review comments
pjcdawkins Oct 1, 2026
abc7f78
perf(auth): skip the legacy export when it is not needed
pjcdawkins Oct 1, 2026
407a084
refactor(auth): shrink the legacy login hook and drop the logout hook
pjcdawkins Oct 1, 2026
74173cf
fix(legacy): replace the placeholder token when a client needs a login
pjcdawkins Oct 1, 2026
8e91ce6
test: cover login prompts, login and logout effects, 401s and session…
pjcdawkins Oct 1, 2026
05b1dcf
fix(config): resolve the home and writable dirs like the legacy CLI
pjcdawkins Oct 1, 2026
dab68d7
fix(config): choose the writable dir by permissions, like the legacy CLI
pjcdawkins Oct 1, 2026
84eb0cf
test(config): cover a file in place of the writable dir
pjcdawkins Oct 1, 2026
964a140
test(config): set the temp dir on Windows too
pjcdawkins Oct 1, 2026
5d4130d
fix(legacy): stop abbreviations from running the internal auth commands
pjcdawkins Oct 1, 2026
cb81ed4
fix(legacy): keep the internal auth commands hidden once loaded
pjcdawkins Oct 1, 2026
ebca841
fix(auth): do not retry a refresh request after it was sent
pjcdawkins Oct 2, 2026
b1275c3
fix(config): read API token and lock settings from the base config
pjcdawkins Oct 2, 2026
6dee32a
fix(auth): forget the API token session too during keychain recovery
pjcdawkins Oct 2, 2026
a739acf
test(integration): expect no retry after a refresh request is sent
pjcdawkins Oct 2, 2026
cafbbd9
feat(auth): let the system choose the local login server's port
pjcdawkins Oct 2, 2026
c6c2d51
fix(auth): pass a rejected access token via stdin, not argv
pjcdawkins Oct 2, 2026
1cdc070
fix(auth): wait for keychain changes instead of timing out
pjcdawkins Oct 2, 2026
77a2ef1
fix(auth): delete session files when a refresh logs the user out
pjcdawkins Oct 2, 2026
d8c7639
fix: let --verbose and --debug override --quiet for errors
pjcdawkins Oct 2, 2026
fbd8139
fix(auth): keep session files whose keychain secrets were not deleted
pjcdawkins Oct 2, 2026
eafc7ba
test(integration): stub the macOS browser launcher too
pjcdawkins Oct 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ go test -v -run TestName ./path/to/package
### Hybrid CLI System

The CLI operates as a wrapper around a legacy PHP CLI:
- Go layer: Handles new commands (init, list, version, config:install, project:convert) and core infrastructure
- Go layer: Handles new commands (init, list, version, config:install, project:convert, the auth commands) and core infrastructure, including authentication
- PHP layer: Legacy commands are proxied through `internal/legacy/CLIWrapper`
- The PHP CLI (platform.phar) is embedded at build time via go:embed
- An index of legacy commands (commands.json, from `list --all --format=json`) is embedded too, so the Go layer can resolve abbreviations like `p:init` in the same way as Symfony Console
Expand All @@ -67,7 +67,7 @@ The CLI operates as a wrapper around a legacy PHP CLI:

**Commands**: `commands/`
- `root.go`: Root command that sets up the Cobra CLI and delegates to legacy CLI when needed
- Native Go commands: init, list, version, config:install, project:convert, completion
- Native Go commands: init, list, version, config:install, project:convert, completion, and auth:browser-login (login), auth:api-token-login, auth:logout (logout), auth:token
- Unrecognized commands are passed to the legacy PHP CLI

**Configuration**: `internal/config/`
Expand All @@ -90,8 +90,10 @@ The CLI operates as a wrapper around a legacy PHP CLI:
- Handles authentication, organizations, and resource management

**Authentication**: `internal/auth/`
- JWT handling and OAuth2 flow
- Custom transport for API authentication
- Go is the only component that stores or refreshes credentials. `auth.Manager` resolves tokens (API tokens, `api.access_token`, stored sessions) and refreshes them under a per-session flock (`<writable dir>/auth/<id>.lock`), re-reading the store under the lock because refresh tokens rotate
- `internal/auth/store`: one entry per session ID, in the system keychain (go-keyring) or in `<writable dir>/auth/<id>.json`
- Auth settings are read by `config.Auth()` with the legacy CLI's precedence: embedded config, the user's `config.yaml`, env vars
- The legacy CLI gets tokens and auth state by running the hidden `auth:internal token|status` command (via `<PREFIX>WRAPPER_EXECUTABLE`), and Go runs the hidden PHP commands `auth:post-login` (SSH certificates and config) and `auth:export-sessions` (a one-time migration of the legacy storage, recorded in `auth/.migrated`)

**Project Initialization**: `internal/init/`
- AI-powered project configuration generation
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ Environment variables include:
This skips confirmation questions.
- `UPSUN_CLI_SESSION_ID`: switch user session (default `default`). See also
`upsun session:switch`.
- `UPSUN_CLI_API_DISABLE_CREDENTIAL_HELPERS=1`: store credentials in files under
`~/.upsun-cli/auth/` instead of the system keychain. Files are also used when
no keychain is available.
- `UPSUN_CLI_AUTO_LOAD_SSH_CERT=0`: disable automatically loading an SSH
certificate when running login or SSH commands.
- `UPSUN_CLI_SHELL_CONFIG_FILE`: the shell config file that `self:install`
Expand Down
Loading
Loading