Skip to content

feat(web): hashed API credential registry for request-time auth - #429

Closed
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/bc-a4b41c77-dfeb-4e01-9a8a-8337fa71a8d4-a854
Closed

feat(web): hashed API credential registry for request-time auth#429
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/bc-a4b41c77-dfeb-4e01-9a8a-8337fa71a8d4-a854

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown

What

Request-time SaaS authentication reads a stdlib SQLite registry (credential_registry.py, table api_credentials) instead of os.getenv("CODEC_CARVER_API_KEYS").

  • Bootstrap transport: pass an explicit mapping into bootstrap_from_mapping at process start (FastAPI lifespan). The env var is not read inside request handlers.
  • Stored material is a SHA-256 digest of the UTF-8 key, plus lifecycle (active / rotated / revoked) and optional expiry. Callers pass now.
  • Verification compares every active digest with hmac.compare_digest so first-match timing is not a signal and mixed str/bytes cannot raise TypeError on hostile Unicode.
  • GET /health returns {"status":"ok","service":"codec-carver"} and stays auth-exempt so probes can confirm the process is up.
  • Decision record: docs/doctoring/api-credential-registry.md (APA 7: NIST SP 800-63B, RFC 2104, OWASP API Security Top 10 2023).

Why

Issues #329 and #373. AGENTS.md forbids request-time env secrets. This is the next buyer-visible gap after the Cloud Agent environment work.

Validation

  • python3 -m unittest tests.test_credential_registry tests.test_saas_web -v — 108 passed
  • python3 -m coverage report --include=credential_registry.py,saas_web.py — 100%
  • python3 -m interrogate credential_registry.py saas_web.py — 100%
  • python3 -m py_compile credential_registry.py saas_web.py — OK

Notes

Open in Web View Automation 

Request-time auth now reads api_credentials only. CODEC_CARVER_API_KEYS
is bootstrap transport into SHA-256 digests compared with
hmac.compare_digest across the full active set. GET /health stays
auth-exempt for probes. Closes the #329/#373 request-time env gap
without mixing Cloud Agent environment work from #426/#427.

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.

This is the credential-registry landing vehicle for #329/#373. Head d3f0280 moves request-time auth off os.getenv("CODEC_CARVER_API_KEYS") onto hashed api_credentials.

Next actions:

  • Keep #427 as the Cloud Agent environment head. Do not merge #426 at 8a60d4a.
  • After #427 is the named env tip, rebase this PR onto it. Both add GET /health and ARCHITECTURE.md.
  • Do not merge HMAC-only #376/#421 in parallel; this PR covers the Unicode compare_digest path.
  • Do not mix drop-zone (#428) or jobsconversion_jobs here.
  • Do not self-approve. Wait for required checks on this head.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

@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.

Useful first slice (hashed api_credentials, lifespan bootstrap, auth-exempt GET /health), but do not land this in parallel with #430.

#430 already covers the #373 gaps this body deferred: fail-closed 0.0.0.0 without keys, credential_events audit, and public listings that omit digests. Merging both will conflict on the registry API.

Do this next:

  1. Prefer #430 (5a80b18) as the credential landing vehicle. Close or supersede this draft after that head is named.
  2. Keep verifycredential_id as the idea to port onto #430 so usage metering can drop plaintext api_key.
  3. Do not mix drop-zone (#428) or Cloud Agent env (#426/#427) into this branch.
  4. Do not self-approve.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
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>

Copy link
Copy Markdown
Contributor

Closing as superseded by #430. The current #430 lineage explicitly ports the remaining #429 ideas—FastAPI lifespan bootstrap and verify returning the stable credential_id—onto the stronger credential-registry design. #430 also adds the missing production boundaries: public-bind fail-closed behavior, explicit loopback development mode, credential lifecycle/event tables, runtime policies, rotation/revoke semantics, and no-store 401s. No unique product behavior remains here, and the two SQLite registry implementations must not compete for credential_registry.py / saas_web.py. No checks or reviews from this closed PR transfer to #430.

@seonghobae seonghobae closed this Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants