Skip to content

feat(llc): LLC API route group + agent-facing API keys (GH#8218) - #8468

Merged
mrveiss merged 4 commits into
Dev_new_guifrom
issue-8218
May 23, 2026
Merged

mrveiss merged 4 commits into
Dev_new_guifrom
issue-8218

Conversation

@mrveiss

@mrveiss mrveiss commented May 23, 2026 •

Copy link
Copy Markdown
Owner

Summary

Implements GH#8218 — LLC API route group registration and agent-facing API keys.

  • Alembic migration 20260523_026: llc_agent_api_keys table with SHA-256 key_hash, agent_id, company_id, and three indexes for O(1) lookup
  • LLCApiKey ORM model (llc/models/api_key.py): per-agent bearer key record; raw token shown once at creation, never stored
  • LLCAgentAuthMiddleware (llc/middleware/agent_auth.py): scoped to /api/llc/agent/* only — zero overhead for all other routes; validates SHA-256 bearer token, injects request.state.agent_id/company_id, updates last_used_at best-effort
  • ApiKeyService (llc/services/api_key.py): issue_key / revoke_key / validate_key; key format llc_ + secrets.token_urlsafe(48)
  • 7 Phase-1 agent route stubs (llc/api/agent_api.py): /work-items/next, /work-items/{id}/status, /cost-events, /comments, /products, /heartbeat/report, /context/{item_id}
  • API key management routes (llc/api/api_keys.py): POST/DELETE/GET /api/llc/agents/{id}/api-keys
  • ApiKeyNotFound exception added to llc/exceptions.py
  • configure_llc_agent_auth() added to initialization/middleware.py, called after configure_audit()
  • 7 unit tests for ApiKeyService — all mocked, no DB required

Architectural 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 — the startswith check short-circuits immediately for all other routes. The two-session pattern in the middleware (validate + best-effort last_used_at update) 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

  • New table llc_agent_api_keys (migration 029): stores agent bearer key hashes with revocation support
  • New middleware LLCAgentAuthMiddleware: intercepts /api/llc/agent/*, validates SHA-256 bearer token, injects agent context
  • New service ApiKeyService: issue/revoke/validate operations with proper ownership checks
  • New routes under /api/llc/agents/{id}/api-keys: create, revoke, list keys
  • New agent routes (Phase 1 stubs): 7 endpoints under /api/llc/agent/ ready for implementation in subsequent phases
  • Middleware registration in initialization/middleware.py: configure_llc_agent_auth() added after audit middleware

Files changed (11)

File Action
migrations/versions/20260523_026_llc_agent_api_keys.py new
llc/models/api_key.py new
llc/middleware/__init__.py new
llc/middleware/agent_auth.py new
llc/services/api_key.py new
llc/api/agent_api.py new
llc/api/api_keys.py new
llc/tests/test_api_keys.py new
llc/api/__init__.py updated
llc/exceptions.py updated
initialization/middleware.py updated

Verification

  • Migration 029 chains from 028 (secrets migration, merged via PR feat(llc): company-scoped secrets — versioned, encrypted, RBAC (GH#8217) #8467) ✅
  • get_async_session_factory() exists in user_management/database.py ✅
  • LLCAgentAuthMiddleware scoped to /api/llc/agent/* prefix only ✅
  • 7 unit tests for ApiKeyService covering issue, revoke, validate, hash operations ✅
  • Import smoke: from llc.api import router; from llc.middleware.agent_auth import LLCAgentAuthMiddleware; from llc.services.api_key import ApiKeyService ✅
  • Middleware wrapped in try/except ImportError — safe on environments without LLC ✅

Model Used

claude-sonnet-4-6 (CodeReviewer agent, MVA-877)

Test plan

  • python -m pytest llc/tests/test_api_keys.py -v — 7 tests pass
  • python -c "from llc.api import router; from llc.middleware.agent_auth import LLCAgentAuthMiddleware; from llc.services.api_key import ApiKeyService; print('imports ok')" — clean import
  • GET /api/llc/health returns {"status": "ok", "module": "llc"}

Parent: MVA-811
Child of: MVA-875

🤖 Generated with Claude Code

mrveiss and others added 2 commits May 23, 2026 14:25
- 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>
@github-actions

github-actions Bot commented May 23, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ SSOT Configuration Compliance: Violations Found

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_url

TypeScript:

import config from '@/config/ssot-config'
// Use: config.vm.main, config.port.backend, config.backendUrl

📖 See SSOT_CONFIG_GUIDE.md for documentation.

mrveiss and others added 2 commits May 23, 2026 14:31
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>
@mrveiss
mrveiss merged commit f374ada into Dev_new_gui May 23, 2026
7 of 14 checks passed
@mrveiss
mrveiss deleted the issue-8218 branch May 23, 2026 11:35
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.

1 participant