Root cause (F1094 / #1200): requireCallerOwnsOrg in org_plugin_allowlist.go
was reading org_api_tokens.created_by — a provenance label string ("session",
"admin-token", "org-token:<prefix>"), NEVER a UUID. The equality check
callerOrg != targetOrgID always failed → every org-token caller got 403
on /orgs/:id/plugins/allowlist routes.
Fix:
- Migration 036: adds org_id UUID column (nullable) to org_api_tokens
with partial index for fast lookups. Pre-migration tokens get org_id=NULL
→ deny by default (safer than cross-org access).
- orgtoken.Issue: takes new orgID param; stores in org_id column.
- orgtoken.OrgIDByTokenID: new helper reads org_id for a token ID.
Returns ("", nil) for NULL/unanchored tokens.
- requireCallerOwnsOrg: now calls OrgIDByTokenID instead of reading
created_by. Pre-migration tokens with org_id=NULL → callerOrg="" → deny.
- orgTokenActor (org_tokens.go): returns (createdBy, orgID) pair.
Tokens minted via another org token get org_id set at mint time.
Session/ADMIN_TOKEN callers get orgID="".
- orgtoken.Token struct: adds OrgID field for list display.
- orgtoken.List: selects org_id alongside other columns.
- Regression tests: happy path, unanchored denial, DB error denial.
Co-authored-by: Molecule AI Infra-Runtime-BE <infra-runtime-be@agents.moleculesai.app>
Co-authored-by: Molecule AI Dev Lead <dev-lead@agents.moleculesai.app>