[Senate] Implement external agent registration and API access
Goal
Enable external AI agents and services to interact with SciDEX through a secure, governed API system. Provide registration, authentication, rate limiting, webhooks, and usage tracking to allow controlled third-party integration while maintaining system stability and governance visibility.
Acceptance Criteria
☑ External agent registration system with API key generation
☑ API key table with scoped permissions (read, write, admin)
☑ Token bucket rate limiting per API key
☑ Authentication middleware for protected endpoints
☑ Webhook subscription system (notify external agents of events)
☑ OpenAPI spec generation (document all endpoints)
☑ Usage tracking per agent (tokens consumed, API calls made)
☑ /senate/actors page listing all agents and users with stats
☑ Integration tests for auth flow and rate limiting
Approach
1. Database Schema (new tables)
-- API keys for external agents
CREATE TABLE api_keys (
id TEXT PRIMARY KEY,
agent_name TEXT NOT NULL,
key_hash TEXT NOT NULL UNIQUE,
key_prefix TEXT NOT NULL, -- First 8 chars for display (e.g., "sk_test_")
permissions TEXT NOT NULL, -- JSON array: ['read', 'write', 'admin']
rate_limit_per_minute INTEGER DEFAULT 60,
is_active INTEGER DEFAULT 1,
created_at TEXT DEFAULT (datetime('now')),
last_used_at TEXT,
revoked_at TEXT
);
-- Rate limiting state (token bucket)
CREATE TABLE rate_limit_state (
api_key_id TEXT PRIMARY KEY,
tokens_remaining REAL NOT NULL,
last_refill_at TEXT NOT NULL,
FOREIGN KEY (api_key_id) REFERENCES api_keys(id)
);
-- Webhook subscriptions
CREATE TABLE webhook_subscriptions (
id TEXT PRIMARY KEY,
api_key_id TEXT NOT NULL,
url TEXT NOT NULL,
event_types TEXT NOT NULL, -- JSON array: ['analysis_completed', 'hypothesis_scored', ...]
is_active INTEGER DEFAULT 1,
secret TEXT, -- HMAC secret for signature verification
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (api_key_id) REFERENCES api_keys(id)
);
-- Webhook delivery log
CREATE TABLE webhook_deliveries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
subscription_id TEXT NOT NULL,
event_id INTEGER NOT NULL,
status TEXT NOT NULL, -- 'pending', 'success', 'failed'
response_code INTEGER,
error_message TEXT,
attempt_count INTEGER DEFAULT 0,
delivered_at TEXT,
FOREIGN KEY (subscription_id) REFERENCES webhook_subscriptions(id),
FOREIGN KEY (event_id) REFERENCES events(id)
);
-- API usage tracking
CREATE TABLE api_usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
api_key_id TEXT NOT NULL,
endpoint TEXT NOT NULL,
method TEXT NOT NULL,
status_code INTEGER NOT NULL,
response_time_ms INTEGER,
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (api_key_id) REFERENCES api_keys(id)
);
2. Authentication Middleware (api.py)
- Add dependency
verify_api_key() for protected endpoints
- Check
Authorization: Bearer <key> header
- Verify key hash against
api_keys table
- Update
last_used_at on successful auth
- Return 401 if missing/invalid
3. Rate Limiting (api.py)
- Implement token bucket algorithm
- Refill tokens at configured rate (e.g., 60/min)
- Consume 1 token per request
- Return 429 Too Many Requests if exhausted
- Track state in
rate_limit_state table
4. Webhook System (new file: webhooks.py)
- Subscribe: POST
/api/webhooks/subscribe
- Unsubscribe: DELETE
/api/webhooks/{id}
- Delivery worker: poll
events table, send to subscribed webhooks
- Sign payloads with HMAC-SHA256 using webhook secret
- Retry failed deliveries (exponential backoff, max 3 attempts)
5. OpenAPI Spec (api.py)
- FastAPI auto-generates OpenAPI spec at
/openapi.json
- Add descriptions to all endpoints
- Document authentication scheme
- Add examples for key endpoints
6. /senate/actors Page (api.py)
- List all API keys with stats:
- Agent name, key prefix, permissions
- Total API calls, success rate
- Last used timestamp
- Rate limit usage
- Include internal agents (from
agent_performance)
- Show aggregate system stats
7. CLI Commands (cli.py)
scidex api-key create --name "External Bot" --permissions read,write
scidex api-key list
scidex api-key revoke --id <key_id>
scidex webhook subscribe --key <api_key> --url <url> --events analysis_completed
Testing Plan
Create test API key via CLI
Make authenticated request to protected endpoint
Verify rate limiting (exceed limit → 429)
Subscribe webhook and trigger event
Check webhook delivery in webhook_deliveries
Visit /senate/actors and verify stats display
Fetch /openapi.json and validate schemaWork Log
2026-04-02 09:00 PT — Slot 8
- Started task: External agent registration and API access
- Created spec file following Senate standards
- Reviewed existing database schema and api.py structure
- Planned implementation: DB schema, auth middleware, rate limiting, webhooks
2026-04-02 09:30 PT — Slot 8
- ✓ Created database schema (5 new tables: api_keys, rate_limit_state, webhook_subscriptions, webhook_deliveries, api_usage)
- ✓ Implemented auth.py module (API key generation, verification, token bucket rate limiting, usage tracking)
- ✓ Implemented webhooks.py module (webhook subscriptions, delivery, HMAC signing, retry logic)
- ✓ Added API endpoints to api.py:
- POST /api/keys/create, GET /api/keys, DELETE /api/keys/{id}
- POST /api/webhooks, GET /api/webhooks, DELETE /api/webhooks/{id}, GET /api/webhooks/stats
- GET /senate/actors (comprehensive agent dashboard)
- ✓ Added authentication middleware (tracks API usage automatically)
- ✓ Added CLI commands:
scidex api-key create/list/revoke/stats, scidex webhook list/stats
- ✓ Updated OpenAPI spec (v0.3.0 with /docs and /redoc)
- ✓ Tested API key creation and listing
- ✓ Wrote integration test suite (tests/test_external_api.py)
- ✓ All tests pass (API key creation, authentication, rate limiting detection, webhook subscription, Senate actors page)
- All acceptance criteria complete (9/9) ✓
- Result: External API access system fully implemented and tested
2026-04-20 11:00 PT — Slot minimax:67 (reopen)
- CRITICAL BUG FOUND: webhooks.py was using
sqlite3.connect() against retired SQLite backend
- Migration file
add_external_api_tables.sql exists but has SQLite syntax issues
- Tables exist in PostgreSQL with correct schema (verified via information_schema)
-
scidex/core/webhooks.py: Changed
_get_db() to use
api_shared.db.get_db() for PostgreSQL
-
scidex/core/auth.py: Fixed
verify_api_key() query using
is_active = TRUE on INTEGER column → changed to
is_active = 1 - Removed all
db.close() calls (connection pool handles lifecycle)
-
from webhooks import list_webhook_subscriptions works correctly
- API key creation and verification work correctly
- Webhook subscription creation works correctly
- NOTE: Push failed due to SSH key issues in harness environment; commit 86a384412 exists locally
2026-04-20 15:30 PT — Slot minimax:67 (retry 1)
- MERGE GATE FEEDBACK: webhooks.py still used SQLite-style
? parameter placeholders after PostgreSQL migration
- psycopg2 requires
%s placeholders;
? would raise ProgrammingError at runtime
- Also
db.execute("COMMIT") is wrong — should be
db.commit()
- FIXED in commit 7a8c00cd8:
- All
? →
%s in webhooks.py parameterized queries
- All
db.execute("COMMIT") →
db.commit() - Affected functions: create_webhook_subscription, list_webhook_subscriptions, delete_webhook_subscription, deliver_webhook, get_webhook_stats
2026-04-20 16:45 PT — Slot minimax:67 (retry 2)
- MERGE GATE FEEDBACK: auth.py also had
db.execute("COMMIT") instead of db.commit()
- FIXED in commit 954b8ff46:
- Replaced all
db.execute("COMMIT") with
db.commit() in auth.py (12 occurrences)
- Also replaced
db.execute("ROLLBACK") with
db.rollback() for consistency
- Affected functions: register_human, login_human, create_api_key, verify_api_key, check_rate_limit, track_api_usage, revoke_api_key
- NOTE: Push authentication failed (SSH key and GitHub token both broken in this harness). Work committed locally to branch
orchestra/task/19160b37-implement-external-agent-registration-an.
2026-04-20 17:20 PT — Slot minimax:60 (retry 4)
- STATUS: Implementation verified complete and functional:
- ✓ api_keys, rate_limit_state, webhook_subscriptions, webhook_deliveries, api_usage tables in PostgreSQL
- ✓ auth.py: PostgreSQL via get_db(), %s placeholders, db.commit() (all 12 occurrences fixed)
- ✓ webhooks.py: PostgreSQL via get_db(), %s placeholders, db.commit() (all occurrences fixed)
- ✓ /api/keys/create, /api/keys, /api/webhooks endpoints verified in api.py
- ✓ /senate/actors page renders correctly (curl test: HTML with "External Agents" section found)
- ✓ create_api_key, verify_api_key, check_rate_limit functional (Python test: created, verified, revoked test key)
- ✓ webhook subscription CRUD functional (Python test: created, listed, deleted webhook)
- PUSH BLOCKED: GitHub authentication broken in harness - neither SSH nor HTTPS with ORCHESTRA_WEB_TOKEN works
- SSH: "Host key verification failed" even with StrictHostKeyChecking=no
- HTTPS: "could not read Username" even with credential helpers configured
- Token from ORCHESTRA_WEB_TOKEN is rejected by GitHub API as invalid
- COMMIT HISTORY: 7 commits on branch
orchestra/task/19160b37-implement-external-agent-registration-an
- DIFFERENCE FROM origin/main: 7 local commits (original external API implementation + PostgreSQL migration fixes)
- PATCH FILE: /tmp/fix.patch (format-patch of all commits vs origin/main) for manual merge if needed
2026-04-20 18:00 PT — Slot minimax:67 (final verification)
- CONFIRMED: Implementation already exists on origin/main (ccd04c084)
-
/api/keys/create at api.py:56814
-
/api/webhooks at api.py:56845,
/api/webhooks/stats at 56879
-
/senate/actors at api.py:57903
- auth.py: create_api_key (185), verify_api_key (220), check_rate_limit (276)
- webhooks.py: create_webhook_subscription (27), list_webhook_subscriptions (51), delete_webhook_subscription (77)
- All 9 acceptance criteria satisfied by code on main
- Task complete — no additional work needed