Summary
Evolve sume login from today’s browser/device approval → long-lived API key flow toward a true OAuth-style credential model (access token + refresh token, CLI-managed refresh), aligned with peers like Railway CLI—while keeping API keys as a first-class path for CI/agents.
Why This Matters
- Interactive human CLI login currently stores a durable API key in
~/.sume-com/config.json. That is simple and matches Stripe/OpenAI-style developer APIs, but it is a weaker secret-on-disk model than short-lived access + refresh.
- Remote MCP Connect on
sume-com is moving toward OAuth (DCR, AS, Clerk IdP). CLI auth and MCP auth do not have to be identical, but diverging forever (keys-only CLI vs OAuth MCP) increases user confusion (“I logged in on the web/MCP but CLI still wants a key”) and duplicate login UX.
- Peers show both patterns are valid; this issue makes an explicit product decision and implementation path for OAuth-direction login, with evidence—not a vague “add OAuth someday.”
Conversation Context
Decisions / constraints from product discussion (2026-07-15):
- Sume CLI today is correctly understood as: login UX mints an API key, not OAuth access/refresh for API calls. Nobody refreshes; key lives until revoke/logout.
- Peer survey:
- Railway CLI: real OAuth 2 (PKCE + device-code), stores
accessToken + refreshToken, CLI itself refreshes before authenticated commands; CI uses separate env tokens (RAILWAY_TOKEN / RAILWAY_API_TOKEN).
- Modal CLI: browser flow mints long-lived
token_id/token_secret in ~/.modal.toml — closer to Sume’s current shape (no OAuth refresh loop).
- Higgsfield CLI (
@higgsfield/cli): browser OAuth PKCE via Clerk; short-lived sessions; public docs emphasize re-login on expiry more than silent refresh.
- Product stance from discussion: for a developer API product, staying API-key-centric for agents/CI is still recommended; invest in full OAuth+refresh when interactive human CLI + short-lived secrets become important—or to align login UX with MCP OAuth.
- This issue is the CLI-side tracking item for that OAuth direction. Platform endpoints may live in
sumelabs/sume-com (web/API); call that out as a cross-repo dependency rather than inventing auth only inside the CLI binary.
Current Behavior
User-facing
sume login # opens app, device approval
sume login --no-browser # print verification URL
sume auth setup --api-key # manual key
export SUME_API_KEY=... # env override
On success, CLI stores apiKey (+ authMode, URLs) under ~/.sume-com/config.json (mode 0600).
Protocol today
POST {appBaseUrl}/api/cli/auth/device/start → device_code, user_code, verification_uri(_complete), interval, expires_in
- User approves in browser on app origin (docs:
app.sume.com; needs verification vs current www.sume.com branding)
POST {appBaseUrl}/api/cli/auth/device/poll → on approved, returns api_key.key
- All subsequent public API calls use
x-api-key: <key> (or Authorization: Bearer <key> if authMode: bearer) against https://api.sume.com/v1
Code / docs SoT (this repo)
src/lib/login-client.ts — device start/poll types; poll success shape is api_key, not OAuth tokens
src/lib/config.ts — apiKey / authMode only; no refresh fields
src/lib/api-client.ts — HTTP boundary; API-key header modes
src/commands/auth.ts / login command wiring
docs/architecture.md § Auth And Config
README.md § Configure Auth
test/cli.test.ts — “logs in with the browser device flow and stores the returned API key”
- Agent skill
agent/sume-auth/SKILL.md — documents key-centric login
What this is not today
- No
access_token / refresh_token stored for API calls
- No CLI-side token refresh loop
- No PKCE authorization-code exchange against an AS
token endpoint for CLI sessions
Desired Behavior
Product outcome
sume login (interactive) can complete an OAuth-style flow and store credentials that are not a long-lived raw developer API key by default (access + refresh, or equivalently short-lived access with refresh).
- CLI refreshes access tokens automatically before authenticated API calls when expired/near-expiry (Railway-like
ensure_valid_token), without prompting the user every hour.
- Automation remains first-class:
SUME_API_KEY / sume auth setup --api-key continue to work for CI/agents (document precedence clearly).
- Logout / revoke clears local OAuth creds and documents how to revoke server-side.
- Docs +
sume-auth skill updated so agents know which mode they are in (oauth vs api_key) without printing secrets.
Observable login success (OAuth mode)
sume auth status --json should report something like:
{
"authenticated": true,
"mode": "oauth",
"has_access_token": true,
"has_refresh_token": true,
"access_expires_at": "2026-07-15T12:00:00.000Z",
"api_base_url": "https://api.sume.com/v1"
}
(Exact field names can match existing status schema; do not print token values.)
API calls (OAuth mode)
Authorization: Bearer <access_token>
against the public API (or whatever audience/resource the platform AS defines for CLI). Needs verification with sume-com whether CLI tokens share MCP mcp:read audience or a dedicated cli / api scope.
Source Of Truth
Internal (sumelabs/cli)
src/lib/login-client.ts — replace or extend device poll response to OAuth tokens or add parallel OAuth PKCE client
src/lib/config.ts — persist accessToken, refreshToken, tokenExpiresAt, authMode: "oauth" | "x-api-key" | "bearer"
src/lib/api-client.ts — attach Bearer access token; call ensure-valid before requests
docs/architecture.md, README.md, agent/sume-auth/SKILL.md
test/cli.test.ts — login + refresh + precedence tests
Platform dependency (sumelabs/sume-com) — Needs verification / likely companion work
- Existing CLI device routes under app:
/api/cli/auth/device/* (mint API keys today)
- MCP/OAuth AS work on web/API (
packages/mcp-oauth, www authorize, possible future mcp.* DCR/token). CLI OAuth should reuse platform AS where possible rather than inventing a third auth island.
- Domain note: CLI docs still say
app.sume.com; production branding may be www.sume.com — implementers must verify live login origins.
External peer evidence
| Peer |
Pattern |
Refresh owner |
Automation |
| Railway CLI + login docs |
OAuth PKCE + device-code; ~/.railway/config.json access+refresh |
CLI |
Env project/account tokens |
| Modal CLI |
Browser mints long-lived token pair in ~/.modal.toml |
None (durable secret) |
MODAL_TOKEN_ID / MODAL_TOKEN_SECRET |
Higgsfield CLI (@higgsfield/cli) |
OAuth PKCE (Clerk) |
Weak / re-login per public docs |
Unclear |
| Sume CLI today |
Device/browser → API key |
None |
SUME_API_KEY |
Railway is the primary reference for desired interactive OAuth+refresh. Modal/Sume-today is the reference for why API keys must remain.
Proposed Credential / Config Schema
Local config (~/.sume-com/config.json) — OAuth mode
{
"authMode": "oauth",
"accessToken": "<redacted-in-docs>",
"refreshToken": "<redacted-in-docs>",
"tokenExpiresAt": "2026-07-15T12:00:00.000Z",
"baseUrl": "https://api.sume.com/v1",
"appBaseUrl": "https://www.sume.com"
}
Local config — API key mode (unchanged)
{
"authMode": "x-api-key",
"apiKey": "<redacted-in-docs>",
"baseUrl": "https://api.sume.com/v1"
}
Precedence (proposed)
SUME_API_KEY (forces API-key mode for that process)
- Explicit OAuth tokens in config when
authMode=oauth
- Config
apiKey
- Unauthenticated → doctor/login guidance
Validation Rules
- OAuth mode must never log access/refresh tokens (agent redaction paths included).
- Refresh failures that require user interaction should surface a clear
sume login next action (not infinite spin).
- Device-code path must remain for headless (
--no-browser), either as OAuth device grant or as “mint key” legacy—product choice below.
- Backward compatibility: existing
apiKey configs keep working without forced re-login on upgrade (migration path documented).
Implementation Notes
Decision gate (implementer must pick and document in PR)
Option A — Railway-like (preferred for “OAuth direction”)
Interactive sume login uses AS authorization-code + PKCE (browser) and/or OAuth device-code; store access+refresh; CLI refreshes; API keys remain for CI only.
Option B — Hybrid mint (closer to Modal)
Keep minting API keys via improved device flow, but add optional short-lived session tokens later. Does not fully satisfy “OAuth direction” alone—only accept if platform cannot ship CLI OAuth AS endpoints soon.
Option C — Thin wrapper over MCP user OAuth
Reuse the same AS/scopes as remote MCP tokens for CLI HTTP calls. Highest alignment with MCP Connect; requires confirmed API acceptance of those bearer tokens for /v1/*.
Default recommendation from conversation: Option A or C, not B, if this issue is explicitly “oauth 방향으로 변경”.
Likely files to modify (CLI)
src/lib/login-client.ts — OAuth start/exchange/refresh helpers
src/lib/config.ts — token fields + migration from key-only
src/lib/api-client.ts — ensureValidAccessToken() before requests
src/commands/auth.ts / login command — flags: --api-key-mode escape hatch?
docs/architecture.md, README.md, agent/sume-auth/**
test/cli.test.ts — mock AS token + refresh
Likely platform work (sume-com) — link companion issue if split
- CLI-oriented OAuth client (PKCE public client) + redirect/device support
- Token audience/scopes for public API calls
- Revocation endpoint behavior
- Deprecation policy for “device poll returns api_key” (keep for compat or sunset)
Flow (Option A target)
sume login → register/discover AS (or use configured authorize URL)
- Browser PKCE or device-code → user authenticates (Clerk behind platform AS)
- Token endpoint → access + refresh → write config
sume avatars list → ensure valid access → Authorization: Bearer … → api.sume.com/v1
- On 401/expiry → refresh → retry once → else ask re-login
Tests
- Login stores oauth fields, never prints secrets in
--json agent mode
- Refresh rotated before expiry; concurrent requests don’t stampede refresh (Needs verification of locking strategy)
SUME_API_KEY overrides oauth config
- Existing key-config users still authenticate after upgrade
- Headless
--no-browser path covered
Edge Cases And Risks
- Security: refresh token on disk is still high value—file mode
0600, redact in doctor/mcp logs, document revoke.
- Platform lag: CLI cannot fake OAuth if
sume-com still only mints API keys from device poll—ship platform endpoints first or behind feature flag.
- Domain drift:
app.sume.com vs www.sume.com login origins must match live AS.
- MCP confusion: users may expect one login for CLI + Cursor MCP; document whether tokens are shared or separate.
- Agents: long-lived keys are easier for agents; oauth refresh must work non-interactively after first login or agents will flap.
- Breaking change risk: forcing oauth-only login without key escape hatch breaks CI—non-goal to remove keys.
Non-Goals
- Removing
SUME_API_KEY / sume auth setup --api-key
- Replacing remote MCP OAuth / DCR work (that stays in
sume-com; coordinate only)
- Building a full desktop auth UI inside the CLI beyond opening the system browser
- Changing public API product auth away from API keys for SDK users
- Higgsfield-parity Clerk embedding inside the CLI binary
Acceptance Criteria
QA Plan
- Unit: mocked token endpoint + refresh in
pnpm test / existing cli test harness
- Manual interactive:
sume login against dev AS → sume auth status --json → sume doctor → one read command (sume health/me/catalog as applicable)
- Expiry simulation: force near-expiry / invalidate access → next command refreshes silently
- CI path:
SUME_API_KEY=… sume doctor --agent --json still passes without oauth config
- Negative: revoke refresh server-side → CLI prompts re-login cleanly
Suggested PR Scope
L if platform AS endpoints must be built in the same change set.
M if platform already exposes CLI OAuth token endpoints and this repo is client-only.
Suggested split:
sume-com: CLI OAuth AS support (PKCE/device + scopes + token audience) — companion mega-issue if not already covered
sumelabs/cli: config + api-client refresh + login UX + tests + docs
Ship behind a flag (sume login --oauth or env SUME_LOGIN_OAUTH=1) until stable, then make oauth the default interactive path.
Summary
Evolve
sume loginfrom today’s browser/device approval → long-lived API key flow toward a true OAuth-style credential model (access token + refresh token, CLI-managed refresh), aligned with peers like Railway CLI—while keeping API keys as a first-class path for CI/agents.Why This Matters
~/.sume-com/config.json. That is simple and matches Stripe/OpenAI-style developer APIs, but it is a weaker secret-on-disk model than short-lived access + refresh.sume-comis moving toward OAuth (DCR, AS, Clerk IdP). CLI auth and MCP auth do not have to be identical, but diverging forever (keys-only CLI vs OAuth MCP) increases user confusion (“I logged in on the web/MCP but CLI still wants a key”) and duplicate login UX.Conversation Context
Decisions / constraints from product discussion (2026-07-15):
accessToken+refreshToken, CLI itself refreshes before authenticated commands; CI uses separate env tokens (RAILWAY_TOKEN/RAILWAY_API_TOKEN).token_id/token_secretin~/.modal.toml— closer to Sume’s current shape (no OAuth refresh loop).@higgsfield/cli): browser OAuth PKCE via Clerk; short-lived sessions; public docs emphasize re-login on expiry more than silent refresh.sumelabs/sume-com(web/API); call that out as a cross-repo dependency rather than inventing auth only inside the CLI binary.Current Behavior
User-facing
On success, CLI stores
apiKey(+authMode, URLs) under~/.sume-com/config.json(mode0600).Protocol today
POST {appBaseUrl}/api/cli/auth/device/start→device_code,user_code,verification_uri(_complete),interval,expires_inapp.sume.com; needs verification vs currentwww.sume.combranding)POST {appBaseUrl}/api/cli/auth/device/poll→ onapproved, returnsapi_key.keyx-api-key: <key>(orAuthorization: Bearer <key>ifauthMode: bearer) againsthttps://api.sume.com/v1Code / docs SoT (this repo)
src/lib/login-client.ts— device start/poll types; poll success shape isapi_key, not OAuth tokenssrc/lib/config.ts—apiKey/authModeonly; no refresh fieldssrc/lib/api-client.ts— HTTP boundary; API-key header modessrc/commands/auth.ts/ login command wiringdocs/architecture.md§ Auth And ConfigREADME.md§ Configure Authtest/cli.test.ts— “logs in with the browser device flow and stores the returned API key”agent/sume-auth/SKILL.md— documents key-centric loginWhat this is not today
access_token/refresh_tokenstored for API callstokenendpoint for CLI sessionsDesired Behavior
Product outcome
sume login(interactive) can complete an OAuth-style flow and store credentials that are not a long-lived raw developer API key by default (access + refresh, or equivalently short-lived access with refresh).ensure_valid_token), without prompting the user every hour.SUME_API_KEY/sume auth setup --api-keycontinue to work for CI/agents (document precedence clearly).sume-authskill updated so agents know which mode they are in (oauthvsapi_key) without printing secrets.Observable login success (OAuth mode)
sume auth status --jsonshould report something like:{ "authenticated": true, "mode": "oauth", "has_access_token": true, "has_refresh_token": true, "access_expires_at": "2026-07-15T12:00:00.000Z", "api_base_url": "https://api.sume.com/v1" }(Exact field names can match existing status schema; do not print token values.)
API calls (OAuth mode)
Authorization: Bearer <access_token>against the public API (or whatever audience/
resourcethe platform AS defines for CLI). Needs verification withsume-comwhether CLI tokens share MCPmcp:readaudience or a dedicatedcli/apiscope.Source Of Truth
Internal (
sumelabs/cli)src/lib/login-client.ts— replace or extend device poll response to OAuth tokens or add parallel OAuth PKCE clientsrc/lib/config.ts— persistaccessToken,refreshToken,tokenExpiresAt,authMode: "oauth" | "x-api-key" | "bearer"src/lib/api-client.ts— attach Bearer access token; call ensure-valid before requestsdocs/architecture.md,README.md,agent/sume-auth/SKILL.mdtest/cli.test.ts— login + refresh + precedence testsPlatform dependency (
sumelabs/sume-com) — Needs verification / likely companion work/api/cli/auth/device/*(mint API keys today)packages/mcp-oauth,wwwauthorize, possible futuremcp.*DCR/token). CLI OAuth should reuse platform AS where possible rather than inventing a third auth island.app.sume.com; production branding may bewww.sume.com— implementers must verify live login origins.External peer evidence
~/.railway/config.jsonaccess+refresh~/.modal.tomlMODAL_TOKEN_ID/MODAL_TOKEN_SECRET@higgsfield/cli)SUME_API_KEYRailway is the primary reference for desired interactive OAuth+refresh. Modal/Sume-today is the reference for why API keys must remain.
Proposed Credential / Config Schema
Local config (
~/.sume-com/config.json) — OAuth mode{ "authMode": "oauth", "accessToken": "<redacted-in-docs>", "refreshToken": "<redacted-in-docs>", "tokenExpiresAt": "2026-07-15T12:00:00.000Z", "baseUrl": "https://api.sume.com/v1", "appBaseUrl": "https://www.sume.com" }Local config — API key mode (unchanged)
{ "authMode": "x-api-key", "apiKey": "<redacted-in-docs>", "baseUrl": "https://api.sume.com/v1" }Precedence (proposed)
SUME_API_KEY(forces API-key mode for that process)authMode=oauthapiKeyValidation Rules
sume loginnext action (not infinite spin).--no-browser), either as OAuth device grant or as “mint key” legacy—product choice below.apiKeyconfigs keep working without forced re-login on upgrade (migration path documented).Implementation Notes
Decision gate (implementer must pick and document in PR)
Option A — Railway-like (preferred for “OAuth direction”)
Interactive
sume loginuses AS authorization-code + PKCE (browser) and/or OAuth device-code; store access+refresh; CLI refreshes; API keys remain for CI only.Option B — Hybrid mint (closer to Modal)
Keep minting API keys via improved device flow, but add optional short-lived session tokens later. Does not fully satisfy “OAuth direction” alone—only accept if platform cannot ship CLI OAuth AS endpoints soon.
Option C — Thin wrapper over MCP user OAuth
Reuse the same AS/scopes as remote MCP tokens for CLI HTTP calls. Highest alignment with MCP Connect; requires confirmed API acceptance of those bearer tokens for
/v1/*.Default recommendation from conversation: Option A or C, not B, if this issue is explicitly “oauth 방향으로 변경”.
Likely files to modify (CLI)
src/lib/login-client.ts— OAuth start/exchange/refresh helperssrc/lib/config.ts— token fields + migration from key-onlysrc/lib/api-client.ts—ensureValidAccessToken()before requestssrc/commands/auth.ts/ login command — flags:--api-key-modeescape hatch?docs/architecture.md,README.md,agent/sume-auth/**test/cli.test.ts— mock AS token + refreshLikely platform work (sume-com) — link companion issue if split
Flow (Option A target)
sume login→ register/discover AS (or use configured authorize URL)sume avatars list→ ensure valid access →Authorization: Bearer …→api.sume.com/v1Tests
--jsonagent modeSUME_API_KEYoverrides oauth config--no-browserpath coveredEdge Cases And Risks
0600, redact in doctor/mcp logs, document revoke.sume-comstill only mints API keys from device poll—ship platform endpoints first or behind feature flag.app.sume.comvswww.sume.comlogin origins must match live AS.Non-Goals
SUME_API_KEY/sume auth setup --api-keysume-com; coordinate only)Acceptance Criteria
sume logincan authenticate via OAuth-direction flow and persist access (+ refresh if issued) without treating that credential as a dashboard-minted long-lived API key by defaultSUME_API_KEYand manual API-key setup still work and take documented precedence for automationsume auth status --json/sume doctor --agent --jsonreport mode safely (no token leakage)sume auth logoutclears oauth (+ key) local credssume-authskill updatedsume-comissue linkedQA Plan
pnpm test/ existing cli test harnesssume loginagainst dev AS →sume auth status --json→sume doctor→ one read command (sumehealth/me/catalog as applicable)SUME_API_KEY=… sume doctor --agent --jsonstill passes without oauth configSuggested PR Scope
L if platform AS endpoints must be built in the same change set.
M if platform already exposes CLI OAuth token endpoints and this repo is client-only.
Suggested split:
sume-com: CLI OAuth AS support (PKCE/device + scopes + token audience) — companion mega-issue if not already coveredsumelabs/cli: config + api-client refresh + login UX + tests + docsShip behind a flag (
sume login --oauthor envSUME_LOGIN_OAUTH=1) until stable, then make oauth the default interactive path.