SOLWYN

Authentication

How the Solwyn CLI stores credentials, resolves them, what a key's scope allows, and which credential each command requires

Credentials live in your operating system's keychain — never in config files, never in argv, never echoed from credential input. The CLI reads and writes them through the keyring library and hands the secret to an Authorization header only at the request boundary. Nothing else touches it.

Where credentials are stored

Every stored secret sits under the keychain service name solwyn-cli, as three separate entries:

EntryHolds
api_keyA project API key
access_tokenA JWT access token
refresh_tokenA JWT refresh token

Your platform's native secret store backs the keychain:

PlatformBackend
macOSKeychain Services
LinuxSecretService (GNOME Keyring, KDE Wallet)
WindowsWindows Credential Manager

When no keychain backend is available, the CLI treats the keychain as empty rather than failing — so a container with SOLWYN_API_KEY set works, and a container without one reports that authentication is required.

The CLI never accepts a supplied key or password as a command-line argument, and never echoes credential input. Newly created project keys are deliberately revealed once or stored securely. Project keys are read from a hidden prompt or from stdin; passwords are read from a hidden prompt only. A credential in your shell history is a credential you did not get from Solwyn.

Two kinds of credential

Solwyn recognizes two credential types, and they authorize different things.

A JWT pair — an access token plus a refresh token — represents you, the logged-in user, with your account role: owner, admin, or member. You obtain it with solwyn login, which prompts for your email and password on an interactive terminal. When an eligible request receives a definite 401 rejection, the CLI refreshes the pair once and retries once; you do not re-login when an access token expires. Mutations that must not be replayed — keys rotate, keys revoke, projects create, and a batch budget check — skip that retry and fail immediately on a 401 instead.

A project key authorizes one project. It has the shape sk_proj_ followed by 64 lowercase hexadecimal characters — validated against the pattern ^sk_proj_[a-f0-9]{64}$ before it is ever stored. You provide it with solwyn login --with-key (a hidden prompt on a terminal, or piped over stdin), or through the SOLWYN_API_KEY environment variable.

Key scopes

Every project key carries a scope: full or read_only. A full key can do everything a project key may do. A read_only key satisfies project-key reads — status, costs, budget status, runs list, notifications show — but is refused by anything that writes: budget check, budget confirm, record, notifications set. The refusal is a 403 with the code read_only_key, exit 3, and a hint that names the fix:

Rotate to a full-scope key with solwyn keys rotate --scope full.

You choose a scope when a key is minted — projects create --scope for a project's first key, keys rotate --scope for a replacement — and keys show reports the current one. Give a read-only key to a dashboard or a monitoring job; give a full key to the SDK and to any agent that will run the budget guardrail loop.

Resolution precedence

When a command needs a credential, the CLI resolves one in a fixed order. The first source that yields a value wins:

SOLWYN_API_KEY environment variable — if set, its value is used as a project key. A malformed value fails immediately with exit 3; it does not fall through to the keychain.

Stored project key — the keychain's api_key entry.

Stored JWT pair — the keychain's access_token and refresh_token entries. A pair with one entry missing is reported as incomplete and does not select another identity.

If none resolve, the command reports authentication_required with the hint Set SOLWYN_API_KEY or run solwyn login. and exits 3. An environment key always shadows a stored key, and a stored key always shadows a stored JWT pair — set SOLWYN_API_KEY and you act as that project regardless of what the keychain holds.

What each command requires

Some commands act on your whole account and need your user login; some act on a single project and need that project's key; some accept either. Every command's --help states its requirement on the Auth: line.

CredentialCommands
Project key onlybudget check, budget confirm, record; writes need full scope
JWT readsoverview, audit, projects list, keys show, channels list/show, account alert-email show, account phone show, alerts account show/preview, alerts activity/pending, alerts … --account, costs --account, attest --account, team members list, plan change --dry-run, and opt-in --with-last-fired reads
Owner/admin JWTprojects create/rename/delete (project.write), keys rotate/revoke (key.write), budget set/restore-defaults, budget rule and threshold writes, runs stop (budget.write), webhook writes (project.write), tags retire, team invites list/send/resend/revoke
Owner JWTBilling writes including checkout/portal creation, team members set-role, account rename, account alert-email set/clear, channel mutations/test/delete, account-alert mutations
JWT with target-specific roleteam members remove and team leave: owner removes admin/member, admin removes member, any role removes itself; last-owner protection applies
Either credentialstatus, whoami, forecast, budget status/uncounted, ordinary budget rules list, rule preview, project costs/analytics, providers, runs list/show, tags list/values, project alert history/export, project attest, notifications show, projects show, plan, plan list
Conditionalnotifications set accepts full-scope key or owner/admin JWT; Slack/PagerDuty test accepts JWT or full-scope key; email test requires JWT; SMS test checks eligibility without sending
Optionaldoctor; init accepts existing credentials or interactive bootstrap
No credentialLocal login, logout, config, agent-context, mcp-config; public invite show performs a credential-free preview read

account recover requires JWT but no active membership. invite accept uses selected JWT, while explicit --new-user requires a terminal and no selected credential; it never silently falls back to signup. See invitation workflows. No CLI command transfers ownership or implements an email-link verification flow.

solwyn mcp resolves a credential per tool call, not at startup. notifications set accepts either credential but needs a full-scope key to write; notifications test email requires a login because it sends to your account address.

Use the wrong type and the command fails with exit 3 before any request is dispatched. A key-only command run under a JWT reports project_key_required with the hint to set SOLWYN_API_KEY or store a key with solwyn login --with-key. A login-only command run under a project key reports authentication_required (or jwt_required) with a message naming the operation and a hint that depends on the key's source:

Key came fromHint
SOLWYN_API_KEYUnset SOLWYN_API_KEY, run solwyn login, then retry keys show.
the keychainRun solwyn logout, then solwyn login, and retry keys show.

Authorization failures exit 3

Since 0.2.0, an HTTP 403 maps to exit 3, the same as a 401. Four cases carry a specific hint:

SituationCodeHint
A read-only key attempted a writeread_only_keyRotate to a full-scope key with solwyn keys rotate --scope full.
A project key was used against a different project(varies)Select the project associated with this key, or configure a key for the selected project.
A project key was used where a login is required(varies)Run solwyn login to authenticate with your user account.
Your role cannot perform the actionowner_required, admin_required, role_required, permission_requiredYour role cannot perform this action; use solwyn whoami to inspect it.

A 402 — a plan-tier or quota limit — is not an authorization failure. It exits 1, carries the API's message, and links to the dashboard's billing page.

Revoked membership and capacity

A JWT response with account_membership_revoked means login alone cannot restore membership. Use account recover to idempotently create a personal account, or return the active one. If a key masks JWT, unset SOLWYN_API_KEY and deliberately clear stored credentials with logout before logging in. Recovery stays CLI-only.

Transient auth_capacity_exceeded is retried once with the shared Retry-After wait capped at 10 seconds; continued overload exits 1, not bad-credential exit 3. Other unknown write outcomes do not imply no effect. See reliability rules.

Logging in

Terminal
$ solwyn login
Email: alice@example.com
Password: ********

Stores a JWT pair in the keychain. Email and password login requires an interactive terminal — run it in a non-interactive context and it exits 3 with a hint to use SOLWYN_API_KEY or a project key instead.

Terminal
$ solwyn login --with-key
Project key: ****************

On a terminal, the key is read from a hidden prompt. With no terminal, it is read from stdin — pipe it in:

Terminal
$ echo "$SOLWYN_PROJECT_KEY" | solwyn login --with-key

Either way the key is validated against ^sk_proj_[a-f0-9]{64}$ and stored in the keychain's api_key entry. A malformed key is rejected before storage.

Logging out

Terminal
$ solwyn logout

Clears all three keychain entries — api_key, access_token, and refresh_token. It is idempotent: an entry already absent counts as cleared. Environment variables are not touched; if SOLWYN_API_KEY is set, it still resolves after a logout.

CI, containers, and agents

In pipelines and containers there is usually no keychain backend. Provide a project key through SOLWYN_API_KEY instead — it sits at the top of the resolution order, so it works with no keychain present:

GitHub Actions
jobs:
  check-budget:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install solwyn-cli
      - run: solwyn --project ${{ vars.SOLWYN_PROJECT_ID }} budget status
        env:
          SOLWYN_API_KEY: ${{ secrets.SOLWYN_API_KEY }}

Because budget check, budget confirm, and record require a project key, SOLWYN_API_KEY is exactly the credential automated budget guardrails run on — and it must be a full-scope key. A read-only key passes every read in a pipeline and then fails the first write with exit 3. See Scripting for the machine contract and Agents for wiring guardrails into agent loops.

Checking your state

Terminal
$ solwyn whoami

Reports which credential resolved, its source (keyring or environment), and — under a JWT pair — your identity, email_verified, account id, role, seat usage, and projects. It is the fastest way to confirm which project a given shell will act as, and which role a mutation will run under. doctor goes further and probes the API with the resolved credential.

Next steps

  • Configuration — set a default project and API URL in config.toml
  • init — one-command onboarding that stores a credential and verifies it end-to-end
  • keys — inspect a key's scope, rotate it, or revoke it
  • Privacy — the wire contract the SDK and CLI share

On this page