diff --git a/docs/marketing/devrel/demos/org-scoped-api-keys-demo.md b/docs/marketing/devrel/demos/org-scoped-api-keys-demo.md new file mode 100644 index 0000000..fd30a31 --- /dev/null +++ b/docs/marketing/devrel/demos/org-scoped-api-keys-demo.md @@ -0,0 +1,121 @@ +# Org-Scoped API Keys: Enterprise Key Management for Multi-Agent Teams + +**Published:** April 20, 2026 | **Author:** Molecule AI Marketing + +--- + +When your engineering team scales from two agents to twenty, the last thing you want is a single `ADMIN_TOKEN` hardcoded in your environment. It's a single point of failure, impossible to rotate without downtime, and impossible to audit. Today's launch changes that. + +We're rolling out **org-scoped API keys** — named, revocable, audit-trail-enabled tokens that live at the organization level and can reach any workspace in your org without breaking the security model. + +## What Are Org-Scoped API Keys? + +Org-scoped API keys are long-lived credentials minted at the organization level via the Canvas UI or the `POST /org/tokens` endpoint. Each key has: + +- A **display name** you choose at creation time (e.g., `ci-deploy-bot`, `devops-rev-proxy`) +- A **sha256 hash** stored server-side — the plaintext is shown once and never again +- A **prefix** (first 8 characters) visible in listings so you can identify keys without exposing secrets +- A **created-by** field that tracks provenance in the audit trail +- **Immediate revocation** — drop a key and it stops being accepted on the very next request + +The keys work across all workspaces in your org — not just admin-surface endpoints, but also per-workspace sub-routes like `/workspaces/:id/channels` and `/workspaces/:id/tokens`. + +## Why Enterprise Teams Need Org-Level Key Management + +### The `ADMIN_TOKEN` problem + +A single env-var token works for prototypes. For production multi-agent systems it creates three compounding risks: + +1. **Rotation requires downtime.** You can't rotate a token used by ten agents simultaneously. You rotate, or you don't — and both choices are bad. +2. **No attribution.** When something calls your API, you have no idea which agent or integration is responsible. +3. **No compartmentalization.** One compromised token compromises everything. + +### What org-scoped keys give you + +| Capability | `ADMIN_TOKEN` | Org-Scoped Keys | +|---|---|---| +| Rotate without downtime | ❌ | ✅ (one key revokes, another takes over) | +| Identify caller per request | ❌ | ✅ (audit prefix in every log line) | +| Revoke a single integration | ❌ | ✅ (per-key revocation) | +| Assign to workspace subroutes | ❌ | ✅ | +| Audit trail with attribution | Partial | ✅ (`created_by` + prefix in logs) | + +## Audit Trail and Rate-Limit Controls + +Every request authenticated with an org API key carries the key's prefix in the audit log, making it straightforward to trace calls back to a specific integration. When combined with the `created_by` field stored at mint time, you get full provenance: *which admin created this key, when, and what it's been calling.* + +Rate-limit controls for org tokens are planned as a near-term follow-on (see roadmap, P3). For now, the token hierarchy is: + +- **Lazy bootstrap** (Tier 0) — only active when there are zero org tokens and no `ADMIN_TOKEN` at all +- **WorkOS session** (Tier 1) — verified user sessions +- **Org API tokens** (Tier 2a) — new org-scoped keys (primary path for service integrations) +- **`ADMIN_TOKEN` env var** (Tier 2b) — break-glass for operators, CLI tooling +- **Workspace tokens** (Tier 3) — deprecated per-workspace tokens + +## How to Get Started + +### Mint a key via API + +```bash +curl -X POST https://your-deployment.molecule.ai/org/tokens \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "name": "ci-deploy-bot", + "description": "GitHub Actions deploy pipeline" + }' +``` + +Response (plaintext shown once — store it securely): + +```json +{ + "id": "tok_01HXYZ...", + "name": "ci-deploy-bot", + "display_prefix": "mole_a1b2", + "created_at": "2026-04-20T14:00:00Z", + "created_by": "admin@example.com" +} +``` + +### List and revoke keys + +```bash +# List all active keys (prefix-only, no plaintext) +curl https://your-deployment.molecule.ai/org/tokens \ + -H "Authorization: Bearer " + +# Revoke a key immediately +curl -X DELETE https://your-deployment.molecule.ai/org/tokens/tok_01HXYZ... \ + -H "Authorization: Bearer " +``` + +### Use in a workspace sub-route + +```bash +# Token hits workspace sub-route via org auth +curl https://your-deployment.molecule.ai/workspaces/ws_abc123/channels \ + -H "Authorization: Bearer mole_a1b2c3d4..." +``` + +## Competitive Note: Hermes v0.10.0 Tool Gateway + +Hermes v0.10.0 ships bundled tool primitives (web search, image generation, TTS, browser automation) as platform-level features for paid Portal subscribers. This positions Hermes as "batteries included" for single-user AI. However, Hermes has no multi-agent or A2A support — its tool gateway operates in a single-user context. + +Molecule's org-scoped API keys reinforce a different value proposition: **enterprise-grade identity and access management for multi-agent teams.** The skills architecture offers greater composability than Hermes' bundled approach, and org tokens now give teams the access-control primitives needed to deploy that composability safely in production. + +Hermes' bundled tools are a valid competitive concern at the feature-surface level. Org-scoped API keys address a deeper need — the same teams comparing bundled-vs-skills architectures need first-class auth and audit before they're comfortable shipping to production. This launch moves Molecule closer to that confidence threshold. + +--- + +## TTS Announcement Clip (45 seconds) + +*[Suggested script for TTS / social audio: read at ~150 words/min]* + +> **Clip script:** +> +> "Molecule AI is shipping org-scoped API keys — enterprise-grade credentials for multi-agent teams. Mint named, revocable tokens from the Canvas UI or REST API. No more single ADMIN_TOKEN across your whole deployment. Each key gets a display prefix for audit attribution, a created-by trace, and immediate revocation. Tokens work across every workspace in your org — including sub-routes, not just admin endpoints. Rotate keys without downtime. Identify which integration called what, every time. Get started in under five minutes: POST to slash-org-slash-tokens, store the plaintext once, and you're live. Org-scoped API keys are available now on all production deployments. Head to the docs or open Canvas to mint your first key." + +--- + +*Attach comments to issue #1114 for review. Labels: `area:content-marketer`, `marketing`, `ready-for-review`.* \ No newline at end of file diff --git a/org-api-keys/README.md b/org-api-keys/README.md new file mode 100644 index 0000000..0f9e7b2 --- /dev/null +++ b/org-api-keys/README.md @@ -0,0 +1,194 @@ +# Org-Scoped API Keys — Working Demo + +> **PR:** #1105 — `feat(auth): org-scoped API keys` +> **What it ships:** `workspace-server/internal/handlers/org_tokens.go` — named, revocable org-admin tokens minted from canvas UI or CLI +> **Acceptance criteria:** working demo + repo link + 1-min screencast or README walkthrough + +--- + +## What This Demo Shows + +An org admin can mint a named, revocable API key from the CLI (or canvas). The key is a full-admin bearer token for the tenant platform — it authorizes every admin-gated endpoint on the tenant (all workspaces, org settings, bundles, secrets). The demo shows minting, using, and revoking the key. + +**Key facts:** +- 256-bit entropy, base64url encoded. Prefix shown in UI for identification. +- Plaintext returned exactly once at mint time — never stored. +- `org-token:` format in audit logs and API responses. +- Revocation is immediate and idempotent. + +**Routes:** +| Method | Path | What | +|---|---|---| +| `GET` | `/org/tokens` | List live (non-revoked) tokens | +| `POST` | `/org/tokens` | Mint a new token — plaintext returned once | +| `DELETE` | `/org/tokens/:id` | Revoke a token | + +--- + +## Prerequisites + +- Molecule AI platform running (`go run ./cmd/server` from `workspace-server/`) +- Canvas open at `http://localhost:3000` +- Admin session cookie OR an existing org-scoped token +- `curl` and `jq` on the caller machine + +--- + +## Working Demo Script + +### 1. Mint a named org token + +```bash +PLATFORM="http://localhost:8080" +COOKIE="session=$(curl -s -c - -X POST "$PLATFORM/cp/auth/login" \ + -d 'email=admin@example.com&password=...' | grep session | awk '{print $7}')" + +# Mint a named org token +curl -s -X POST "$PLATFORM/org/tokens" \ + -H "Cookie: $COOKIE" \ + -H "Content-Type: application/json" \ + -d '{"name": "ci-pipeline-key"}' | jq +``` + +Response (200): +```json +{ + "id": "otok_xxxxxxxxxxxx", + "prefix": "mL9kXp2W", + "name": "ci-pipeline-key", + "auth_token": "org-token:mL9kXp2WQrZvT8sBmN3cD4eF6gH0iJ1kL9pM3nO5qR7tU0vW1xY2zA3bC4dE5fG", + "warning": "copy this token now; it will not be shown again" +} +``` + +**Save the `auth_token` value — it cannot be retrieved again.** + +--- + +### 2. Use the org token to list workspaces + +The token is a full-admin bearer. Pass it in the `Authorization` header: + +```bash +ORG_TOKEN="org-token:mL9kXp2WQrZvT8sBmN3cD4eF6gH0iJ1kL9pM3nO5qR7tU0vW1xY2zA3bC4dE5fG" + +curl -s "$PLATFORM/org/tokens" \ + -H "Authorization: Bearer $ORG_TOKEN" | jq +``` + +Response — lists all live org tokens: +```json +{ + "tokens": [ + { + "id": "otok_xxxxxxxxxxxx", + "prefix": "mL9kXp2W", + "name": "ci-pipeline-key", + "created_by": "admin@example.com", + "created_at": "2026-04-21T00:00:00Z", + "revoked_at": null + } + ], + "count": 1 +} +``` + +Use the token to call any admin-gated endpoint: + +```bash +# List all workspaces in the org +curl -s "$PLATFORM/workspaces" \ + -H "Authorization: Bearer $ORG_TOKEN" | jq '.workspaces[].id' + +# List all org bundles +curl -s "$PLATFORM/bundles" \ + -H "Authorization: Bearer $ORG_TOKEN" | jq '.' + +# Read workspace secrets +curl -s "$PLATFORM/workspaces/ws-123/secrets/values" \ + -H "Authorization: Bearer $ORG_TOKEN" | jq +``` + +--- + +### 3. Revoke the token and confirm 401 + +```bash +# Revoke via DELETE +curl -s -X DELETE "$PLATFORM/org/tokens/otok_xxxxxxxxxxxx" \ + -H "Authorization: Bearer $ORG_TOKEN" \ + -H "Content-Type: application/json" \ + -w "\nHTTP %{http_code}\n" +``` + +Response: +``` +HTTP 200 +``` + +Revocation is immediate. Confirm the token no longer works: + +```bash +curl -s "$PLATFORM/org/tokens" \ + -H "Authorization: Bearer $ORG_TOKEN" \ + -w "\nHTTP %{http_code}\n" +``` + +Response: +``` +HTTP 401 +{"error":"invalid or revoked org api token"} +``` + +The token is dead. Revocation is permanent — the row is soft-deleted (revoked_at timestamp set). The same plaintext can never authenticate again. + +--- + +### 4. Verify the token shows as revoked in the list + +Revoke requires the session cookie (not the org token — org tokens can't revoke other org tokens): + +```bash +curl -s "$PLATFORM/org/tokens" \ + -H "Cookie: $COOKIE" | jq '.tokens[0].revoked_at' +``` + +Response: +```json +"2026-04-21T00:05:00Z" +``` + +Audit trail: `created_by` on the original mint + `revoked_at` timestamp provide full provenance tracking. + +--- + +## Screencast Outline (1 min) + +**0:00–0:10** Terminal: mint a named org token via `POST /org/tokens`. Response shows `auth_token` field — highlight the "copy now" warning. + +**0:10–0:25** Terminal: use the token to call `GET /workspaces`. Output shows workspace IDs. Call out `Authorization: Bearer org-token:...` header. + +**0:25–0:40** Terminal: revoke the token via `DELETE /org/tokens/:id`. Output: `HTTP 200`. Immediately try to call `/org/tokens` again — `HTTP 401` shown in terminal. + +**0:40–0:55** Canvas: switch to Org Settings → API Keys tab. The revoked token shows `revoked_at` timestamp. New token listed alongside any others. + +**0:55–1:00** Narration: *"Org-scoped API keys: mint named tokens, use them anywhere, revoke them instantly. Full admin access, one line of curl. No session cookies, no browser. org-token keys, from Molecule AI."* + +--- + +## TTS Narration Script (30s) + +> Org-scoped API keys let you mint a full-admin bearer token for the Molecule AI platform — named, revocable, auditable. Mint one from the CLI or canvas, use it to call any admin endpoint, revoke it when you're done. The token is 256 bits of entropy, shown exactly once at mint time, and the revoke is immediate. No session cookies, no browser. Just a bearer token that works until you revoke it. Org API keys, from Molecule AI. + +--- + +## Code Reference + +| File | What | +|---|---| +| `workspace-server/internal/handlers/org_tokens.go` | OrgTokenHandler: List, Create, Revoke | +| `workspace-server/internal/orgtoken/tokens.go` | `Issue()`, `Revoke()`, `List()` — token lifecycle | +| `workspace-server/migrations/035_org_api_tokens.up.sql` | Schema: `org_api_tokens` table | +| `canvas/src/components/settings/OrgTokensTab.tsx` | Canvas UI: org token management tab | + +**Source:** `workspace-server/internal/handlers/org_tokens.go` (PR #1105) diff --git a/org-api-keys/narration.mp3 b/org-api-keys/narration.mp3 new file mode 100644 index 0000000..b268ce1 Binary files /dev/null and b/org-api-keys/narration.mp3 differ diff --git a/org-api-keys/storyboard.md b/org-api-keys/storyboard.md new file mode 100644 index 0000000..421ff80 --- /dev/null +++ b/org-api-keys/storyboard.md @@ -0,0 +1,172 @@ +# Screencast Storyboard — Org-Scoped API Keys + +> **PR:** #1105 | **Feature:** `org_tokens.go` | **Duration:** 60 seconds +> **Format:** Terminal-led, clean dark theme + +--- + +## Pre-roll (0:00–0:04) + +**Canvas — full screen** +Org settings panel open. Org Settings → API Keys tab visible with one existing token listed. + +Narration (0:00–0:04): +> "Every org has admin operations — listing workspaces, managing secrets, deploying bundles. Org-scoped API keys let you do all of it, from the CLI, without a browser." + +**Camera:** Static Canvas frame. 3-second hold. + +--- + +## Moment 1 — Mint the token (0:04–0:16) + +**Cut to:** Terminal window, dark theme. + +Prompt: `admin@platform:~$` + +```bash +PLATFORM="https://acme.moleculesai.app" + +curl -s -X POST "$PLATFORM/org/tokens" \ + -H "Cookie: session=admin@example.com..." \ + -H "Content-Type: application/json" \ + -d '{"name": "ci-pipeline-key"}' | jq +``` + +**Terminal output:** + +```json +{ + "id": "otok_a1b2c3d4e5f6", + "prefix": "mL9kXp2W", + "name": "ci-pipeline-key", + "auth_token": "org-token:mL9kXp2WQrZvT8sBmN3cD4eF6gH0iJ1kL9pM3nO5qR7tU0vW1xY2zA3bC4dE5fG", + "warning": "copy this token now; it will not be shown again" +} +``` + +**Camera:** Type-in animation. Highlight `auth_token` value and `warning` field — amber ring, 1s hold. + +Narration (0:05–0:13): +> "One POST, a name, and the token is minted. 256 bits of entropy. The plaintext is shown exactly once — copy it now." + +**Callout text (bottom-left):** +`One-time display. Never stored.` + +--- + +## Moment 2 — Use the token (0:16–0:30) + +**Terminal continues:** + +```bash +ORG_TOKEN="org-token:mL9kXp2WQrZvT8sBmN3cD4eF6gH0iJ1kL9pM3nO5qR7tU0vW1xY2zA3bC4dE5fG" + +# List all workspaces +curl -s "$PLATFORM/workspaces" \ + -H "Authorization: Bearer $ORG_TOKEN" | jq '.count' +``` + +**Terminal output:** + +```json +{"count": 7} +``` + +**Terminal continues:** + +```bash +# List all org tokens (as audit check) +curl -s "$PLATFORM/org/tokens" \ + -H "Authorization: Bearer $ORG_TOKEN" | jq '.tokens[] | "\(.name) — \(.created_by)"' +``` + +**Terminal output:** + +``` +"ci-pipeline-key — admin@example.com" +``` + +**Camera:** Run the two curl commands. Show JSON output. Hold on the org token listing with `created_by` attribution. + +Narration (0:16–0:26): +> "Use it anywhere. Authorization header, full admin access. Every workspace, every bundle, every secret. Audit trail shows who minted it." + +--- + +## Moment 3 — Revoke and confirm 401 (0:30–0:50) + +**Terminal:** + +```bash +# Revoke the token +curl -s -X DELETE "$PLATFORM/org/tokens/otok_a1b2c3d4e5f6" \ + -H "Authorization: Bearer $ORG_TOKEN" \ + -w "\nHTTP %{http_code}\n" +``` + +**Terminal output:** + +``` +HTTP 200 +``` + +**Terminal immediately:** + +```bash +# Confirm it's dead +curl -s "$PLATFORM/workspaces" \ + -H "Authorization: Bearer $ORG_TOKEN" \ + -w "\nHTTP %{http_code}\n" +``` + +**Terminal output:** + +``` +{"error":"invalid or revoked org api token"} +HTTP 401 +``` + +**Camera:** Full revoke sequence. Hold on `HTTP 401` in red. + +Narration (0:32–0:44): +> "Revoke it. One DELETE. The token dies immediately — the 401 confirms it. The same plaintext will never work again." + +--- + +## Moment 4 — Canvas audit trail (0:50–0:56) + +**Cut to:** Canvas — Org Settings → API Keys tab. + +The revoked token now shows `revoked_at: 2026-04-21T00:04:30Z`. The `ci-pipeline-key` token is listed alongside any others with `created_by: admin@example.com`. + +Narration (0:50–0:54): +> "The canvas shows the full audit trail. Who minted it, when, when it was revoked. Named tokens, full admin scope, instant revocation." + +--- + +## Close (0:56–1:00) + +**Terminal clean frame.** + +Narration (0:56–0:58): +> "Org API keys — mint, use, revoke. No session cookies. No browser. Full admin access from the CLI." + +**End card:** + +``` +Org-Scoped API Keys +workspace-server/internal/handlers/org_tokens.go — molecule-core#1105 +``` + +**Fade to black.** + +--- + +## Production Notes + +- **Terminal theme:** Dark, SF Mono / JetBrains Mono 14pt, same as other demos. +- **HTTP status:** Use `curl -w "\nHTTP %{http_code}\n"` in all terminal demos to show status codes inline. +- **Callout style:** Amber ring `#E8A000`, 1s fade-in/out. +- **401 highlight:** Show the HTTP status in red (`\u001b[31m` ANSI if supported, or just text highlight). +- **Canvas cutaway:** Pre-record the Org Settings → API Keys tab with a live token in the list. +- **VO pacing:** Read against the timeline — the 0:32–0:44 revoke sequence is the climax; VO should land on "401" for emphasis.