Repository navigation
feat(llc): LLC API route group + agent-facing API keys (GH#8218) - #8468
Merged
Merged
Conversation
- Alembic migration 20260523_026: llc_agent_api_keys table with indexes
- LLCApiKey SQLAlchemy ORM model (SHA-256 key_hash, per-agent bearer keys)
- LLCAgentAuthMiddleware: scoped to /api/llc/agent/*, O(1) hash lookup
- ApiKeyService: issue_key / revoke_key / validate_key
- 7 agent-facing route stubs (Phase 1): work-items, cost-events, comments,
products, heartbeat, context
- POST/DELETE/GET /api/llc/agents/{id}/api-keys management routes
- ApiKeyNotFound exception added to llc/exceptions.py
- configure_llc_agent_auth() registered in initialization/middleware.py
- 7 unit tests for ApiKeyService (all mocked, no DB required)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…_new_gui GH#8218 branch was cut before approvals (027) and secrets (028) merged. Migration renamed 026→029, chains off secrets (028). Conflict in llc/api/__init__.py resolved: includes both agent_api/api_keys (GH#8218) and approvals (Dev_new_gui) routers. PR #8468 must merge after PR #8467 (secrets, 028). Co-Authored-By: Paperclip <noreply@paperclip.ing>
Contributor
|
| Metric | Count |
|---|---|
| Total Violations | 7 |
| SSOT Violations (high priority) | 7 |
| Other Violations | 0 |
⚠️ 7 values have SSOT config equivalents!
These should be replaced with SSOT config imports:
Python:
from src.config.ssot_config import config
# Use: config.vm.main, config.port.backend, config.backend_urlTypeScript:
import config from '@/config/ssot-config'
// Use: config.vm.main, config.port.backend, config.backendUrl📖 See SSOT_CONFIG_GUIDE.md for documentation.
Brings in GH#8217 secrets module: llc/api/secrets.py, models/secret.py, services/secret.py, migration 028. Resolves llc/api/__init__.py to include all routers (activity, approvals, secrets, budget, companies, goals, work_items, api_keys, agent). Migration 029 down_revision=028 is correct. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…auth, agent_api) Co-Authored-By: Paperclip <noreply@paperclip.ing>
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements GH#8218 — LLC API route group registration and agent-facing API keys.
20260523_026:llc_agent_api_keystable with SHA-256key_hash,agent_id,company_id, and three indexes for O(1) lookupllc/models/api_key.py): per-agent bearer key record; raw token shown once at creation, never storedllc/middleware/agent_auth.py): scoped to/api/llc/agent/*only — zero overhead for all other routes; validates SHA-256 bearer token, injectsrequest.state.agent_id/company_id, updateslast_used_atbest-effortllc/services/api_key.py):issue_key/revoke_key/validate_key; key formatllc_+secrets.token_urlsafe(48)llc/api/agent_api.py):/work-items/next,/work-items/{id}/status,/cost-events,/comments,/products,/heartbeat/report,/context/{item_id}llc/api/api_keys.py):POST/DELETE/GET /api/llc/agents/{id}/api-keysApiKeyNotFoundexception added tollc/exceptions.pyconfigure_llc_agent_auth()added toinitialization/middleware.py, called afterconfigure_audit()ApiKeyService— all mocked, no DB requiredArchitectural decision: SHA-256 (stdlib
hashlib) not bcrypt — high-entropy machine tokens don't need key-stretching; SHA-256 enables O(1) DB lookup; industry standard (GitHub, Stripe).Thinking Path
Machine-to-machine API keys differ from password auth: tokens from
secrets.token_urlsafe(48)have 288 bits of entropy, making brute force infeasible regardless of hashing algorithm. SHA-256 is correct here (GitHub PATs, Stripe API keys, Heroku API keys all use SHA-256). bcrypt/scrypt would add unnecessary latency (~100ms per request) with no security gain.Middleware scoping to
/api/llc/agent/*keeps the hot path clean — thestartswithcheck short-circuits immediately for all other routes. The two-session pattern in the middleware (validate + best-effortlast_used_atupdate) avoids holding a connection open across the request lifecycle.Migration 029 chains from 028 (LLC secrets, merged via PR #8467) as required by the merge order constraint.
What Changed
llc_agent_api_keys(migration 029): stores agent bearer key hashes with revocation supportLLCAgentAuthMiddleware: intercepts/api/llc/agent/*, validates SHA-256 bearer token, injects agent contextApiKeyService: issue/revoke/validate operations with proper ownership checks/api/llc/agents/{id}/api-keys: create, revoke, list keys/api/llc/agent/ready for implementation in subsequent phasesinitialization/middleware.py:configure_llc_agent_auth()added after audit middlewareFiles changed (11)
migrations/versions/20260523_026_llc_agent_api_keys.pyllc/models/api_key.pyllc/middleware/__init__.pyllc/middleware/agent_auth.pyllc/services/api_key.pyllc/api/agent_api.pyllc/api/api_keys.pyllc/tests/test_api_keys.pyllc/api/__init__.pyllc/exceptions.pyinitialization/middleware.pyVerification
get_async_session_factory()exists inuser_management/database.py✅LLCAgentAuthMiddlewarescoped to/api/llc/agent/*prefix only ✅ApiKeyServicecovering issue, revoke, validate, hash operations ✅from llc.api import router; from llc.middleware.agent_auth import LLCAgentAuthMiddleware; from llc.services.api_key import ApiKeyService✅Model Used
claude-sonnet-4-6 (CodeReviewer agent, MVA-877)
Test plan
python -m pytest llc/tests/test_api_keys.py -v— 7 tests passpython -c "from llc.api import router; from llc.middleware.agent_auth import LLCAgentAuthMiddleware; from llc.services.api_key import ApiKeyService; print('imports ok')"— clean importGET /api/llc/healthreturns{"status": "ok", "module": "llc"}Parent: MVA-811
Child of: MVA-875
🤖 Generated with Claude Code