Skip to content

feat(rls): Phase 1 #12 — AgentCard RLS + callsite fix#140

Open
webdevcom01-cell wants to merge 5 commits into
mainfrom
feat/rls-phase1-agentcard
Open

feat(rls): Phase 1 #12 — AgentCard RLS + callsite fix#140
webdevcom01-cell wants to merge 5 commits into
mainfrom
feat/rls-phase1-agentcard

Conversation

@webdevcom01-cell
Copy link
Copy Markdown
Owner

@webdevcom01-cell webdevcom01-cell commented May 24, 2026

Summary

  • Migration `20260606000000_rls_phase1_agentcard`: `ENABLE ROW LEVEL SECURITY` + `FORCE` on `AgentCard`
    • SELECT: `isPublic = true` (cross-org A2A discovery) OR `EXISTS (SELECT 1 FROM "Agent" WHERE id = agentId AND organizationId = current_org_id)` (own-org private cards)
    • INSERT/UPDATE/DELETE: EXISTS subquery only — cannot modify another org's cards
    • No `organizationId` column added — `agentId` is `UNIQUE` so the EXISTS is a single-row PK lookup (O(1))
  • `card-generator.ts`: `upsertAgentCard` wraps `agentCard.upsert` in `withAdminBypass` — fire-and-forget write, no org context; relies on DATABASE_URL BYPASSRLS (tech-debt chore(deps): bump actions/upload-artifact from 4.6.2 to 7.0.0 #6)
  • ADR-0001 (`docs/adr/0001-agentcard-rls-classification.md`): documents TENANT_INDIRECT classification decision vs Option A (add `organizationId` column)
  • Runbook updated (`docs/rls-phase-1-cutover-runbook.md` §2.3, §3.3, §A.4): `AgentCard` reclassified from `TENANT_DIRECT` → `TENANT_INDIRECT (EXISTS via Agent)`

RLS tests — ⚠️ PLACEHOLDER

`prisma/migrations/20260606000000_rls_phase1_agentcard/test.sql` contains 4 manual SQL scenarios for staging:

  1. Org A SELECT own AgentCard → 1 row (EXISTS = true)
  2. Org A SELECT Org B's `isPublic=true` card → 1 row (cross-org public read)
  3. Org A UPDATE Org B's card (even when `isPublic=true`) → UPDATE 0 (blocked)
  4. Org A INSERT AgentCard with `agentId` from Org B → ERROR 42501 (WITH CHECK fail)

These are not automated. No RLS integration test harness exists yet — the existing vitest suite mocks Prisma and cannot test actual SQL policies. A `it.skip()` TODO block in `src/lib/db/tests/rls-middleware.test.ts` tracks when a real-DB harness is wired. The `test.sql` file is the placeholder until then.

Test plan

  • `pnpm precheck` — TypeScript ✅ vitest 4117 passed ✅ ESLint ✅
  • E2E: pre-existing auth failures expected (continue-on-error)
  • Manual staging: execute `test.sql` as `admin_user` against staging DB, verify all 4 expected outcomes
  • Smoke: `GET /api/agents/discover` returns `agentCard.skills` for public agents
  • Smoke: agent PATCH/PUT still updates card (upsertAgentCard via withAdminBypass)

- Migration 20260606000000: enable RLS + FORCE on AgentCard; SELECT
  policy allows own-org cards (EXISTS subquery against Agent) plus
  cross-org isPublic=true reads; INSERT/UPDATE/DELETE require EXISTS
  match; no organizationId column (agentId UNIQUE -> single-row PK lookup)
- card-generator: upsertAgentCard wraps agentCard.upsert in
  withAdminBypass (fire-and-forget write, no org context available)
@webdevcom01-cell webdevcom01-cell added the e2e Run E2E tests on this PR label May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e Run E2E tests on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant