Parent: #80 (M-phase epic)
Goal
Formalize Murmur's machine-plane auth so it supports multiple publishers cleanly. Today there's a single shared bearer (MURMUR_TOKEN) that does triple duty across registration, agent-shim auth, and webhook delivery. This blocks multi-tenant onboarding and conflates three different security concerns.
Behavior
Publisher namespace as first-class entity
Each publisher gets { id, slug, display_name, created_at }. Per-publisher state isolation across:
- Pipeline registry
- Skill registry (M5)
- Run history
- Webhook URL config
- HITL review queue (M3)
Four machine-plane secrets, rotatable independently
| Secret |
Direction |
Purpose |
publisher_admin_token |
Publisher → Murmur |
Pipeline + skill registration; publisher config; rotate other tokens |
publisher_runner_token |
Publisher → Murmur |
Triggering runs (e.g., from a public form) |
webhook_signing_secret |
Murmur → Publisher (HMAC) |
Murmur signs webhook payloads; publisher verifies |
subcommand_bearer |
Agent → Publisher shim (Murmur stores, injects on subcommand calls) |
Publisher-controlled bearer the publisher's shim verifies |
The agent's MCP session token is per-claim and orthogonal to these (already exists).
API surface
POST /publishers # create publisher (bootstrap; gated)
GET /publishers/me # current publisher (token-scoped)
PATCH /publishers/me # update display_name / webhook URL / subcommand_bearer
POST /publishers/me/tokens/{kind}/rotate
DELETE /publishers/me/tokens/{kind}/{id}
GET /publishers/me/audit # admin-action audit log
Existing endpoints (POST /pipelines, POST /skills, POST /pipelines/{id}/runs, webhook delivery) become publisher-scoped via token claims — no explicit publisher_id in request bodies.
Webhook signature
Murmur signs every webhook delivery with webhook_signing_secret over (timestamp, body). Publishers verify via X-Murmur-Signature: t=<unix>,v1=<hex> header. Replay protection: publishers reject deltas > 5 min.
Backward compat for the existing demo publisher
Existing MURMUR_TOKEN continues to work as the runner token for the existing demo publisher record (jobseek). Migration: when M1 ships, the existing MURMUR_TOKEN is grandfathered in as the new four-token set's runner_token; admin/webhook secrets get generated; the publisher gradually migrates env vars + secrets across the four token roles. No flag day; no public-facing downtime.
Folds in
Verification
- Two publisher records can co-exist; each has its own pipeline + skill namespace; cross-publisher reads return 404
- Each token kind rotates without invalidating the others
- Webhook signature validates against a sample publisher's verifier
- Audit log records every machine-plane admin action with
(publisher_id, token_kind, timestamp, action, actor_user_id_if_human)
- Demo publisher migration documented and tested with a no-downtime run
Definition of done
Dependencies / blocks
- Blocks M2 (human auth needs publisher context)
- Blocks M3 (HITL needs publisher scope)
- Blocks M5 (skill registry needs publisher scope)
- Blocks colophon-group/jobseek's full transition
Parent: #80 (M-phase epic)
Goal
Formalize Murmur's machine-plane auth so it supports multiple publishers cleanly. Today there's a single shared bearer (
MURMUR_TOKEN) that does triple duty across registration, agent-shim auth, and webhook delivery. This blocks multi-tenant onboarding and conflates three different security concerns.Behavior
Publisher namespace as first-class entity
Each publisher gets
{ id, slug, display_name, created_at }. Per-publisher state isolation across:Four machine-plane secrets, rotatable independently
publisher_admin_tokenpublisher_runner_tokenwebhook_signing_secretsubcommand_bearerThe agent's MCP session token is per-claim and orthogonal to these (already exists).
API surface
Existing endpoints (
POST /pipelines,POST /skills,POST /pipelines/{id}/runs, webhook delivery) become publisher-scoped via token claims — no explicitpublisher_idin request bodies.Webhook signature
Murmur signs every webhook delivery with
webhook_signing_secretover(timestamp, body). Publishers verify viaX-Murmur-Signature: t=<unix>,v1=<hex>header. Replay protection: publishers reject deltas > 5 min.Backward compat for the existing demo publisher
Existing
MURMUR_TOKENcontinues to work as the runner token for the existing demo publisher record (jobseek). Migration: when M1 ships, the existingMURMUR_TOKENis grandfathered in as the new four-token set's runner_token; admin/webhook secrets get generated; the publisher gradually migrates env vars + secrets across the four token roles. No flag day; no public-facing downtime.Folds in
pull_taskauth) — extends with the publisher namespace contextVerification
(publisher_id, token_kind, timestamp, action, actor_user_id_if_human)Definition of done
docs/auth.mdDependencies / blocks