-
Keep provider and credentialSource separate.
-
Uniqueness is by asset (e.g., auth-file:<name>).
-
codex api key records must prefer a persisted stable local id over a derived config fingerprint. Editing apiKey / baseUrl / prefix must not change the record id used by frontend selection, modal state, disable state, or rotation ordering.
-
User-created or copied codex api key records may share the same normalized config identity (apiKey + baseUrl + prefix). Treat each persisted local-id as a separate editable account asset; store files should be keyed by local-id so duplicates do not overwrite each other.
-
Legacy sidecar mirror data without local-id may still be deduplicated against a stored local record with the same normalized config identity to avoid reintroducing the historical double-card bug.
-
Account-card copy/import must copy a structured account payload, not just a display name. The payload should round-trip all supported asset classes: auth-file, codex-api-key, and openai-compatible.
-
Account-card clipboard writes must tolerate browser preview and Wails permission differences. Prefer a shared clipboard helper with DOM copy, Web Clipboard, Wails runtime, and app-local fallback paths instead of calling navigator.clipboard.writeText directly from card components.
-
Copied account imports create a new editable asset even when the underlying config is identical. When the imported title/provider/file name already exists, append user-facing numeric suffixes from #2 onward; do not use -copy style suffixes. For auth-file imports, preserve .json after the numbered title, such as codex-auth #2.json.
-
Account import file intake may accept common archives such as .zip, .tar, .tar.gz, .tgz, .json.gz, and .gzip. Use a mature compression library such as fflate for compressed layers; keep any TAR handling limited to archive entry enumeration. Archive handling is only a candidate-expansion step: scan internal .json entries, ignore directories / non-JSON files / __MACOSX, preserve archive-qualified names such as accounts.zip:nested/auth.json, and keep final session/auth normalization in the shared backend upload/import path.
-
Do not fetch accounts until sidecar is ready.
-
Reload from Wails after create/delete instead of hand-merging state.
-
Account inventory reads must not implicitly fan out into all-account quota, usage, and rate-limit reads. Load the safe inventory first, then schedule runtime supplements only for visible, selected, active-route, live-session, or explicitly refreshed accounts.
-
Automatic account runtime supplement reads must pass through one coordinator keyed by stable account id plus resource. Concurrent overlapping batches may refresh only missing account/resource pairs and must wait for existing in-flight work instead of duplicating it. When no visible/selected target is reported, do not fall back to refreshing the entire small account pool.
-
Version-like runtime metadata such as rate-limit strategies should use an App-lifetime singleflight loader and a separate failure domain from frequently refreshed per-account statuses. A strategy read failure must not erase an otherwise valid status response.
-
Account Runtime Authority V2 uses accounts-v2.sqlite as the only GetTokens runtime credential source. Legacy accounts-v1.sqlite, auth-dir/*.json, config API keys, FileTokenStore.List(), codex-api-key, and openai-compatibility are migration inputs only and must never become fallback runtime accounts.
-
OAuth refresh commit order is strict: upstream refresh result -> credential generation/token revision CAS in accounts-v2.sqlite -> in-memory auth/scheduler/hook update. A CAS conflict must leave memory, route guard, quota/rate-limit and hooks unchanged.
-
One active account compiles to exactly one runtime auth. Legacy openai-compatible assets containing multiple API key entries must be split into separate account assets during migration; runtime synthesis must not fan one account out into multiple auth IDs.
-
Every runtime auth enumeration entry point, including generic recursive token filestores, must prune migration-backups directories before reading JSON. Filtering only account-store synthesis is insufficient: a generic filestore can otherwise register backup OAuth files as orphan auths, refresh their consumed tokens, and project the resulting identity-level auth-error onto a current SQLite account with the same provider account_id.
-
The main account-store SQLite file is sidecar-owned. App/Wails must not directly sql.Open the primary accounts-v2.sqlite for account first-paint, details, diagnostics, or repair decisions. App-side fast reads must go through a sidecar management snapshot endpoint, or through a versioned sidecar-exported read-only projection that is explicitly limited to display data and never participates in runtime state decisions.
-
SQLite is the account truth source, but hot routing still depends on runtime AuthManager and ModelRegistry indexes. Account-store apply/refresh must rebuild those indexes even in embedded/no-watcher service mode; a card being applied in SQLite is not sufficient evidence that requests can route through it.
-
Account-store SQLite I/O failures such as SQLITE_IOERR_SHORT_READ / disk I/O error (522) are sidecar runtime failures, not frontend state issues. Fix them inside CLIProxyAPI account-store boundaries: upgrade/verify the SQLite driver, reset/reopen the cached store for recoverable read errors, retry read endpoints at most once, and return structured management errors such as code=account_store_io_error with recoverable=true. Account-card stale banners may summarize this as a user-readable cached-quota warning, but diagnostics surfaces must retain the raw error summary/tooltip for troubleshooting.
-
Account-store hot refresh must distinguish "read failed" from "valid empty account store". A configured-but-unreadable SQLite store must not be interpreted as an authoritative zero-account snapshot, because delete/update-triggered no-watcher refresh would otherwise prune every existing account-store runtime auth and make Codex relay return auth_unavailable until restart. Preserve existing runtime auths on read failure, surface the structured account-store error, and only prune when ListAccounts succeeds with an actual empty active set.
-
Account deletion remains soft-delete on the request path: set account_cards.deleted_at_unix_ms first, cancel matching runtime jobs, and reapply the account store once. Physical deletion belongs to a sidecar-owned idle cleanup worker that hard-deletes old soft-deleted account_cards in bounded batches, relies on SQLite ON DELETE CASCADE for credential/runtime rows, uses a retention window so recent mistakes remain recoverable, and must never run inside frontend loops.
-
Account deletion's immediate runtime effect must be targeted by account_key: after SQLite soft-delete succeeds, remove/disable only matching account-store runtime auths, unregister only their models, prune only their live sessions / websocket pins, and bump the pool epoch. Full account-store reconcile remains as a later consistency check, not the only way for a single delete to take effect and not a reason to mutate unrelated accounts.
-
Removing an account-store runtime auth must also clear every route-guard source owned by that auth before retaining its disabled tombstone. Healthy credential replacement / OAuth reauthorization must clear transient guards through the same full-auth lookup. Route guard blocks may be owned by auth id or account key and indexed by file name plus provider account identity; clearing only auth.ID is insufficient for quota/runtime blocks that carry only AccountKey. Use one route-guard API that expands the complete auth lookup keys instead of reconstructing identity rules in service code.
-
codex api key lives in local storage under ~/.config/gettokens-data/codex-api-keys/, not in auth-dir.
-
AccountsPage is route-only; heavy assembly lives under frontend/src/features/accounts/.
-
Feature-internal layering for accounts is:
components/
hooks/
model/
tests/
-
Do not re-introduce helpers.ts-style catch-all files. Split by responsibility such as config snippets, selectors, presentation, quota formatting, and actions.
-
Account list filters must not be collapsed into a single enum once source and availability semantics diverge. For account list and Codex account order filters, use AND-style requirement fields instead of a single availability enum or *Only compatibility fields:
source
requiresRequestable
requiresBlocked
requiresDisabled
hasBalance
hasLongestQuota
requiresError
-
Keep disabled and error/unavailable filters separate. A manually disabled account is route-excluded by user intent, while an error/unavailable account needs diagnostic attention.
-
Account-list status filters and status grouping must consume the same operational auth/quota failure evidence as account cards. 可请求 / requestable means active/configured/routeable and not blocked by terminal quota/runtime evidence such as blocked=true with sources[].source=auth-error or real stale + degradedReason; those accounts belong in 异常 / error instead. 已禁用 remains an explicit user/system disabled bucket, separate from operational error.
-
Account-card top-level operational state must consume runtime stale/degradedReason and sidecar route-guard source evidence, not only fresh usage/quota success. For OAuth/auth-file quota refresh failures with a real degraded reason, or route runtime blocked=true with sources[].source=auth-error, show the card as status_error_display / danger instead of falling back to "waiting check" or "available" whether the reason is terminal upstream failure (401/402/403, unauthorized, token_expired, invalidated, deactivated_workspace, etc.) or a generic refresh error (management api-call failed, refresh_failed, etc.). This auth/quota failure evidence must be evaluated before runtimeStatus=registered_routeable; being registered only proves the auth existed in runtime, not that the current OAuth credential is still usable. The card visual tint must follow the operational tone as well: danger maps to the critical/danger tint and must not fall back to routeable/positive coloring. Keep only explicit placeholder/unknown reasons such as "Quota runtime status has not been observed yet." in the waiting-check path. Codex account-list requestability remains a separate eligibility chain.
-
Sidecar management account read models must also project active route guard evidence. GET /v0/management/accounts and GET /v0/management/accounts/:account_key should overlay active AccountRouteGuardStore blocks as display/runtime evidence such as runtime_routeability_status=degraded, runtime_failure_class=<source>, and runtime_routeability_reason=<reason>, while leaving SQLite account_runtime_apply_state unchanged. Do not persist a transient auth-error, quota-empty, rate-limit, or upstream block by rewriting account-store routeability from a read path.
-
Persist account-list filter preferences separately from ephemeral UI state. Persist filters; do not persist search drafts, modal open state, or bulk-selection state unless a later requirement explicitly needs that.
-
Account plan is runtime data, not a fixed frontend enum. Account list grouping, badges, and plan filters must aggregate plan keys from AccountRecord.planType and quota planType; new values such as team, enterprise, billing, or provider-specific plan keys must form their own groups/options instead of falling into unknown merely because the frontend has not hard-coded them. Old all-selected plan filter state should not hide newly discovered plan keys.
-
Browser preview fixtures for account-list filters must include representative samples for every runtime-derived facet being shipped. If a rollout adds dynamic request-status filters such as HTTP 401/402/... or splits resource filters into quota/balance/usage facets, update frontend/src/features/accounts/previewData.ts so preview mode exposes at least one matching account per new facet; otherwise headless browser acceptance can prove only layout, not that the new filter group actually renders or narrows results.
-
When Wails dev App cards and external browser cards appear inconsistent, classify the evidence before changing card logic: first confirm whether the page has Wails bindings and which sidecar/profile/port it is reading, then compare per-window UI view-state and localStorage cache, then inspect the same account id's React runtime state versus cached quota state. Do not unify WebView/Chrome localStorage or mutate account-card semantics until same-account backend truth is proven inconsistent. A DEV-only diagnostics surface is the preferred first slice for this class of issue.
-
Vendor preset cURL templates that depend on platform cookies should use a neutral placeholder such as {{platformCookie}}. If platformCookie is stored for an account, keep it scoped to management quota/billing curl execution and never use it as runtime auth, route guard identity, or upstream model/chat credential.
-
Management-only credentials such as modelFetchApiKey/modelFetchBaseUrl must stay out of runtime account synthesis, route guard, usage attribution, and APIKeyEntriesJSON; they are only for management actions such as provider model-list fetching.
-
Relay/Codex model catalog refreshes must use modelFetchApiKey/modelFetchBaseUrl for OpenAI-compatible /models fetching when present, with per-field fallback to runtime apiKey/baseUrl only when the management credential field is empty. Token-plan runtime credentials such as tp-* are not valid evidence that /models cannot be populated.
-
Account-list bulk selection actions should render as one sticky workbench toolbar, not as a nested card or dashed sub-section. High-frequency actions can stay inline; secondary actions should collapse into a menu only when measured available width is insufficient, using a small pure layout predicate plus browser scroll checks that prove no top blank band leaks account cards through the sticky area.
-
When simplifying an account detail surface, reduce repeated information before shrinking individual controls. Prefer these orderings:
- remove duplicated summary fields that already appear in a dedicated section below
- reuse shared primitives such as
QuotaBars, compact stat strips, and embedded-label inputs instead of inventing detail-only variants
- keep deep-link restoration and detail hash behavior intact while changing layout
- lock the new density with focused tests that assert structure, module ordering, and route restoration
-
For account detail surfaces, keep top-of-page runtime summaries limited to live operational signals. Quota, balance, raw auth content, and export/route controls should live in their own sections rather than repeating in the runtime strip.
-
Account detail mutations that change visible account fields must update the local account-list cache by stable account.id immediately after the backend save succeeds, then use ListAccounts only as a final refresh. Do not update only selectedAccount or rely on reload as the sole display source; if ListAccounts fails or is swallowed, reopening detail from a stale card must still show the saved values.
-
Detail modals backed by frame hash state must update their local detail state synchronously when opening or closing before relying on hashchange. If close only clears selectedAccount while the old detail value is still in local state, the hydration effect can reopen the modal and make the close button require two clicks.