Skip to content

feat(auth): migrate API keys to a SQLite credential registry - #430

Draft
cursor[bot] wants to merge 4 commits into
mainfrom
cursor/bc-cd9ac3b0-5638-46dc-b431-427f9eb36cee-6ed3
Draft

feat(auth): migrate API keys to a SQLite credential registry#430
cursor[bot] wants to merge 4 commits into
mainfrom
cursor/bc-cd9ac3b0-5638-46dc-b431-427f9eb36cee-6ed3

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown

What

Closes the request-time os.getenv("CODEC_CARVER_API_KEYS") deviation tracked in #329 and #373.

  • New stdlib credential_registry.py stores SHA-256 verifiers in api_credentials (3NF, two-word table names) with credential_events and runtime_policies.
  • saas_web bootstraps from env once at startup, then verifies X-API-Key against the registry. Later env changes are ignored.
  • Public binds (0.0.0.0) fail closed without keys. Empty-registry local use requires explicit loopback development mode.
  • Rotation keeps the previous key valid until revoke. 401 responses use Cache-Control: no-store and never echo secrets.
  • Doctoring: docs/doctoring/api-credential-registry.md (APA 7: NIST SP 800-63B-4, OWASP API2:2023, RFC 2104, NIST SP 800-218).

Why

Org rule: env is transport into a KV, never the runtime source. Comparing raw env strings on every request also first-match short-circuited and could raise on hostile headers.

Validation

  • python3 -m unittest tests.test_credential_registry tests.test_saas_web.TestApiKeyAuth tests.test_job_store -q — 51 passed
  • python3 -m unittest tests.test_saas_web -q — 98 passed with credential_registry.py and saas_web.py at 100% coverage
  • python3 -m interrogate credential_registry.py saas_web.py — 100%
  • python3 -m py_compile ... credential_registry.py — OK

Notes

Open in Web View Automation 

cursoragent and others added 3 commits August 16, 2026 16:07
Store SHA-256 verifiers in api_credentials so request-time auth no
longer reads CODEC_CARVER_API_KEYS. Bootstrap stays idempotent; rotate
keeps the previous key valid until revoke.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Named startup copies transport keys once. Middleware verifies X-API-Key
against stored digests, ignores later env changes, and returns 401
without echoing secrets.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add APA 7 doctoring, ERD, and the operator next-action for public
binds, rotation, and closing issues #329 and #373.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor
cursor Bot requested a review from seonghobae August 16, 2026 16:08

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

This is the #373-complete credential landing on this head (5a80b18). It adds the fail-closed public-bind policy, credential_events audit rows, and public listings that omit key_digest.

Do this next:

  1. Keep #430 as the named credential head for issues #329/#373. Do not also merge #429 onto main — the two registries will collide (lifecycle_status vs lifecycle_state, lifespan vs on_event, verify return type).
  2. After required checks finish, port two #429 ideas onto this head if they are still missing: FastAPI lifespan instead of deprecated on_event, and verify returning credential_id so usage_metering can stop storing plaintext.
  3. GET /health belongs to #427, not this PR. Do not copy environment scripts here.
  4. Do not self-approve. seonghobae is requested.

Local evidence on this head: 51 focused tests, 98 test_saas_web, 100% coverage on credential_registry.py + saas_web.py, interrogate 100%.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Port the two #429 ideas onto this head: verify yields the stable
credential_id for usage metering, and FastAPI lifespan runs the named
bootstrap instead of a deprecated startup event.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fe1023c on this head ports the two #429 ideas without taking #429's registry: verify_api_key returns credential_id, and FastAPI lifespan runs the named bootstrap.

Keep #430 as the named credential landing for #329/#373. Do not merge #429lifecycle_status vs lifecycle_state, on_event vs lifespan, and the verify return type will collide.

GET /health still belongs to #427. Do not copy environment scripts here.

The buyer gap this head left open is billing: usage still stored plaintext keys. The stacked follow-up on cursor/bc-9615934f-b2f0-472b-83ac-65cd089b0a4e-b5a7 rebinds that to usage_periods(credential_id, billing_period) and 429s a spent monthly plan. Merge this PR first, then that follow-up.

Do not self-approve. seonghobae is requested.

Local evidence on fe1023c plus the follow-up commits: 133 tests across credential, usage, and saas_web; 100% coverage on those three modules; interrogate 100%.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: api API, protocol, event, or external contract area: data Database, schema, migration, ETL, or lineage priority: medium Normal-priority or P2 work status: draft Draft pull request type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant