migration(0004): backfill workspace_token kind for cloud-agent-box keys

Closed
KH
6mo ago
+21-0

Summary

The pre-#42 /v1/tokens/workspace route in @relayauth/server@0.2.9 stored rows in api_keys with kind='api_key' and workspace_id=NULL even when the caller requested a workspace token. The key value carries the relay_ws_ prefix and is accepted by the apiKeyAuth middleware, but resolveWorkspaceToken (used by /v1/tokens/path) rejects them because kind != 'workspace_token' — surfacing as 401 workspace_token_required in the cloud-agent box-warm flow.

The route fix that correctly sets kind/workspace_id at mint time is already in main via #42 (feat(tokens): implement path-scoped relayfile tokens). This migration cleans up rows that were stored incorrectly under the old route so they resolve as workspace tokens once the new code is deployed.

Scope

UPDATE api_keys
SET kind = 'workspace_token',
    workspace_id = SUBSTR(name, LENGTH('cloud-agent-box:') + 1)
WHERE name LIKE 'cloud-agent-box:%'
  AND kind = 'api_key'
  AND revoked_at IS NULL;

Only rows matching the documented cloud-agent-box:<workspaceId> shape (cloud-web-worker mint pattern, see cloud:packages/web/lib/relay-workspaces.ts:mintRelayAuthWorkspaceToken). Idempotent — the kind='api_key' AND revoked_at IS NULL guards make re-runs harmless.

How this surfaces

Pear's cloud-agent attach flow (cloud-web-worker → relayauth /v1/tokens/workspace → relayauth /v1/tokens/path) was failing with workspace_token_required. Manual D1 patches in prod confirmed: once these rows are corrected (and the new route code is deployed), the box-warm chain completes and Pear attaches.

Release / deploy chain

  1. ✅ This migration merged into relayauth main.
  2. Publish a new @relayauth/server release (e.g. 0.3.0) including this migration + the #42 route fix.
  3. Bump @relayauth/server in AgentWorkforce/cloud (follow-up PR).
  4. Cloud deploy → runs new migrations against relayauth D1 in each stage + deploys the worker with the kind-correct route. New mints land with kind='workspace_token' directly; this migration only catches the leftover bad rows.

🤖 Generated with Claude Code