docs/11-auth-and-accounts.md

11 — Auth and Accounts

A Picasso account is needed only for managed inference, memory sync, cloud sessions and the account dashboard at https://picassohq.com/account. Bring-your-own-key runs work without signing in. The CLI signs in with a device code. The CLI and Picasso for Mac share one sign-in on a Mac: a single Keychain item, so signing in or out on one signs the other in or out too.

Account model

GET /v1/me returns the signed-in account:

{ "account": { "id": "<id>", "email": "<email>", "plan": "free" } }

The launch release ships solo free-tier accounts. The organization, team, SSO and SCIM shapes in earlier drafts are design only. The API has an org-scoped GET /v1/auth/sso/providers?organization_id=… route that answers 503 sso_provider_not_configured when no SSO adapter is configured, and no client uses it at launch.

Device-code login

$ picasso auth login
Picasso Terms version <v> (effective <YYYY-MM-DD>): https://picassohq.com/legal/terms
In the free tier, new coding sessions are captured under these Terms.
Do you accept the Picasso Terms? [y/N] y
Picasso device login started.
Verification: https://picassohq.com/auth/device?code=1A2B-3C4D
Code: 1A2B-3C4D

Picasso device login complete.
Signed in as [email protected] (free). Token stored in the OS keychain.
Picasso Terms: version <v> accepted <YYYY-MM-DD>.

The default API is https://api.picassohq.com. --api-url <url> or PICASSO_API_URL overrides it.

The flow:

  1. The CLI reads the published Terms (GET /v1/account/terms). Before the device flow starts, they must be accepted, either at the [y/N] prompt on a terminal or with --accept-terms for scripts and CI. A non-interactive login without --accept-terms exits 2 with Signing in requires accepting the Picasso Terms (version <v>): https://picassohq.com/legal/terms. Re-run with --accept-terms to accept them. Answering no exits 1 with Sign-in cancelled: the Picasso Terms were not accepted.
  2. The CLI calls POST /v1/auth/device/authorize and prints the verification URL and code.
  3. The CLI polls POST /v1/auth/device/token at the server's interval, adding 5 s on slow_down. Both calls send x-picasso-client: cli/<version> and x-picasso-installation-id: <uuid>.
  4. The user opens the URL on any device, signs in, and enters the code.
  5. The API returns one bearer token. The CLI stores it (see below), then records the Terms acceptance (POST /v1/account/terms-acceptances, surface:"cli"). A failed acceptance record never blocks sign-in. It is reported, and nothing is captured until a later login records it.

Exit codes: 0 signed in, 1 network or API error, 3 denied (Device login was denied.) or expired (Device login expired before approval completed.).

Token lifetime. The token lasts 30 days. There is no refresh token and no silent refresh. Before each use the CLI checks expiresAt with a 60 s skew. An expired token is deleted and reported as signed out, and the user runs picasso auth login again.

Sign-out and status

CommandOutputExit
picasso auth statusSigned in as <email> · plan <plan> · <api origin> · expires <YYYY-MM-DD>0
Not signed in. Run \picasso auth login\. (no token)3
Signed out — run \picasso auth login\`` (expired, or the server answered 401)3
Your Picasso account is suspended. Contact [email protected]. (403 account_suspended)3
picasso auth status --json{signedIn:true, apiUrl, account{id,email,plan}, expiresAt, tokenStorage, installationId}, or `{signedIn:false, reason:"missing"\"expired"\"revoked"\"suspended", apiUrl}`0 / 3
picasso auth logoutSigned out of Picasso. This device's session was revoked.0
Not signed in.0
picasso: Signed out on this device. warning: the server session could not be revoked (<reason>); it expires <ISO>.1

auth status calls GET /v1/me. On a 401 it deletes the local token. On a 403 account_suspended it keeps the token, and managed and cloud calls stay blocked until an admin reinstates the account. A network failure exits 1.

auth logout calls POST /v1/auth/logout with the bearer token, then deletes the stored token. A 401 from logout counts as revoked. The token is deleted even when the revoke fails, and the command then exits 1 so scripts know the server session stays valid until it expires. Because the Keychain item is shared, logging out of the CLI also signs out Picasso for Mac for that API origin. When PICASSO_API_TOKEN is set, logout prints PICASSO_API_TOKEN is set in the environment; unset it to sign out. and changes nothing.

The same 401 and 403 mapping applies to every managed and cloud call (managed runs, memory sync, cloud session upload). A 401 deletes the local token and fails with picasso: Signed out — run \picasso auth login\` (exit 3). A 403 account_suspended keeps the token and fails with picasso: Your Picasso account is suspended. Contact [email protected]. (exit 3). Signed-in users can see and revoke their own devices' sessions at https://picassohq.com/account (GET /v1/account/sessions`).

Credential storage

Picasso account token. It is a generic password in the macOS login Keychain:

The CLI and the Mac app read and write the same item (@picasso/shared-home readAccountSession/writeAccountSession). The secret is a JSON record:

{ "schemaVersion": 1, "accessToken": "<bearer>", "tokenType": "Bearer", "expiresAt": 1767225600000,
  "accountId": "<id>", "email": "<email>", "serverSessionId": "<id>", "signedInBy": "cli",
  "installationId": "<uuid>", "apiOrigin": "https://api.picassohq.com", "updatedAt": "<ISO>" }

Installation id. ~/.picasso/installation-id holds one UUIDv4 (mode 0600) per machine, created on first API use and sent as x-picasso-installation-id. Admins see it, with the surface (cli/mac) and last-seen time, on each account session.

Other credentials. Provider API keys (BYO mode) and MCP secrets are stored per provider route by the engine's credential store and are covered in 07-providers.md and 05-mcp.md. Subscription passthrough is not available in this release (17-subscription-passthrough.md). The memory sync key has its own Keychain item, com.picassohq.memory-key (see 15-memory.md and picasso memory key).

Client config and minimum version

At startup (except update, auth, help, --help and --version), the CLI reads GET /v1/client-config, cached in ~/.picasso/client-config.json for ttl_seconds (300 s by default). A network failure fails open. PICASSO_CLIENT_CONFIG=off skips the fetch. The CLI exits 1 with picasso: Picasso <cur> is no longer supported (minimum <min>). Run: npm i -g @picassohq/cli@latest in three cases:

Before the deadline, a forced update prints a warning instead.

Session-level identity

Every cloud-synced session and memory record is scoped to the account that owns it. Local sessions stay on the machine. Cloud session upload is opt-in (picasso settings set sync.cloudSessions on; see 06-sessions.md).

Account deletion

The CLI has no account-deletion command in this release. Deletion is a privacy request handled through the admin compliance flow. It erases the account's data in one transaction and returns a receipt. To remove the local sign-in, run picasso auth logout. picasso uninstall --purge removes local data, and only *records* a keychain purge request for platform uninstallers: it does not delete Keychain items itself.

Acceptance criteria

Dependencies