@tideorg/mcp 1.4.2 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/GAP_REGISTER.md +18 -8
  3. package/PRIVACY.md +41 -0
  4. package/README.md +207 -197
  5. package/adapters/AGENTS.md +3 -2
  6. package/adapters/CLAUDE.md +1 -1
  7. package/canon/anti-patterns.md +55 -62
  8. package/canon/concepts.md +46 -34
  9. package/canon/custom-contracts.md +12 -8
  10. package/canon/feature-mapping.md +27 -75
  11. package/canon/framework-matrix.md +69 -22
  12. package/canon/hosting-options.md +111 -0
  13. package/canon/iga-change-requests-api.md +211 -0
  14. package/canon/invariants.md +33 -35
  15. package/canon/security-gap-mapping.md +506 -0
  16. package/canon/security-runtime-probes.md +177 -0
  17. package/canon/tidecloak-bootstrap.md +21 -16
  18. package/canon/tidecloak-endpoints.md +60 -98
  19. package/canon/troubleshooting.md +115 -53
  20. package/canon/version-policy.md +8 -12
  21. package/mcp-server/dist/server.js +221 -19
  22. package/package.json +48 -45
  23. package/playbooks/add-auth-nextjs-existing.md +33 -17
  24. package/playbooks/add-auth-nextjs-fresh.md +1 -1
  25. package/playbooks/add-rbac-nextjs.md +7 -2
  26. package/playbooks/bootstrap-realm-from-template.md +33 -18
  27. package/playbooks/deploy-tidecloak-docker.md +31 -28
  28. package/playbooks/diagnose-missing-roles-or-claims.md +2 -2
  29. package/playbooks/initialize-admin-and-link-account.md +22 -19
  30. package/playbooks/protect-api-nextjs.md +40 -1
  31. package/playbooks/protect-aspnet-core-asgard.md +313 -0
  32. package/playbooks/protect-routes-nextjs.md +24 -10
  33. package/playbooks/provision-tidecloak-skycloak.md +213 -0
  34. package/playbooks/setup-forseti-e2ee.md +32 -50
  35. package/playbooks/setup-iga-admin-panel.md +113 -171
  36. package/playbooks/verify-jwt-server-side.md +20 -1
  37. package/prompts/build-private-customer-portal.md +1 -1
  38. package/prompts/security-gap-analysis.md +55 -0
  39. package/reference-apps/encrypted-communication/anti-patterns.md +3 -3
  40. package/reference-apps/encrypted-communication/bootstrap-sequence.md +2 -2
  41. package/reference-apps/encrypted-communication/scenario.md +2 -2
  42. package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +8 -8
  43. package/reference-apps/iga-admin-governance/anti-patterns.md +18 -16
  44. package/reference-apps/iga-admin-governance/bootstrap-sequence.md +10 -5
  45. package/reference-apps/iga-admin-governance/role-policy-matrix.md +12 -13
  46. package/reference-apps/iga-admin-governance/scenario.md +15 -13
  47. package/reference-apps/organisation-password-manager/bootstrap-sequence.md +5 -6
  48. package/reference-apps/policy-governed-signing/bootstrap-sequence.md +9 -9
  49. package/skills/tide-diagnostics/SKILL.md +1 -1
  50. package/skills/tide-integration/SKILL.md +9 -18
  51. package/skills/tide-mcp-qa/SKILL.md +139 -0
  52. package/skills/tide-rbac-and-e2ee/SKILL.md +5 -5
  53. package/skills/tide-reviewer/SKILL.md +1 -1
  54. package/skills/tide-security-analyst/SKILL.md +182 -0
  55. package/skills/tide-setup/SKILL.md +1 -1
  56. package/canon/delegation.md +0 -195
  57. package/playbooks/setup-server-delegation.md +0 -142
@@ -14,43 +14,45 @@ Scenario-specific mistakes for IGA admin governance panels. General Tide anti-pa
14
14
 
15
15
  ---
16
16
 
17
- ## AP-G02: Using JSON for add-review or add-rejection
17
+ > Endpoints use the current `/iga/change-requests/...` surface (GAP-065). Full spec: `canon/iga-change-requests-api.md`.
18
18
 
19
- **Mistake**: Sending `application/json` to `/tideAdminResources/add-review` or `/tideAdminResources/add-rejection`.
19
+ ## AP-G02: Treating authorize as commit
20
20
 
21
- **Why it fails**: These endpoints require `multipart/form-data`. JSON submissions return 415 or are silently ignored.
21
+ **Mistake**: Calling only `POST /iga/change-requests/{id}/authorize` and assuming the change is applied.
22
22
 
23
- **Fix**: Use `FormData` for add-review and add-rejection. All other mutation endpoints (sign/batch, commit/batch, cancel/batch) use JSON.
23
+ **Why it fails**: `authorize` records an approval. The change does not take effect until `POST /iga/change-requests/{id}/commit` is called, and commit returns **412** until `readyToCommit` (threshold met, dependencies committed).
24
+
25
+ **Fix**: After `authorize` succeeds, poll for `readyToCommit === true`, then `commit`. During bootstrap, `bulk-authorize` then commit ready CRs in dependency passes.
24
26
 
25
27
  ---
26
28
 
27
- ## AP-G03: Treating sign as commit
29
+ ## AP-G03: Re-signing the same CR with the same admin
28
30
 
29
- **Mistake**: Calling only `sign/batch` and assuming the change is applied.
31
+ **Mistake**: Retrying `authorize` on a CR the same admin already signed.
30
32
 
31
- **Why it fails**: Signing records an approval. The change does not take effect until `commit/batch` is called after the quorum threshold is met.
33
+ **Why it fails**: Four-eyes enforcement returns **409 Conflict** one admin cannot count twice toward the threshold.
32
34
 
33
- **Fix**: After sign succeeds and approval count >= threshold, call `commit/batch`.
35
+ **Fix**: Have a *different* admin authorize. If you get 409, refresh the CR (`GET /iga/change-requests/{id}`) — it may already be signed or no longer PENDING.
34
36
 
35
37
  ---
36
38
 
37
- ## AP-G04: Caching change-set counts across mutations
39
+ ## AP-G04: Stale pending list across mutations
38
40
 
39
- **Mistake**: Fetching counts once and not refreshing after sign, commit, or cancel.
41
+ **Mistake**: Fetching the pending list once and not refreshing after authorize/commit/deny.
40
42
 
41
- **Why it fails**: Counts change after every mutation. Stale counts cause the UI to show incorrect badge numbers and confuse admins.
43
+ **Why it fails**: `status` and `readyToCommit` change after every action. Stale data drives wrong badge counts and lets the UI offer commit on a CR that is not ready (→ 412).
42
44
 
43
- **Fix**: Refresh counts after every mutation (sign, commit, cancel, add-review, add-rejection).
45
+ **Fix**: Re-fetch `GET /iga/change-requests?status=PENDING` after every mutation and recompute counts/badges from it.
44
46
 
45
47
  ---
46
48
 
47
- ## AP-G05: Ignoring enclave challenge in MultiAdmin mode
49
+ ## AP-G05: Skipping the enclave step in Tide MultiAdmin mode
48
50
 
49
- **Mistake**: Not handling the enclave approval popup when multiple admins exist. The sign response indicates `requiresApprovalPopup: true` but the app treats it as a normal success.
51
+ **Mistake**: Calling a bare `authorize` in Tide MultiAdmin mode and treating the response as done.
50
52
 
51
- **Why it fails**: In MultiAdmin mode, `sign/batch` returns an enclave challenge (base64 `changeSetDraftRequests`). The admin must sign this in the enclave popup and submit via `add-review`. Without this, the approval is not recorded.
53
+ **Why it fails**: In Tide MultiAdmin mode the approval is cryptographic a bare `authorize` does not record it. The admin must complete the two-phase enclave exchange: `GET /iga/change-requests/{id}/approval-model` sign the `requestModel` in the enclave → `POST /iga/change-requests/{id}/approval-model` with `{ requestModel }`.
52
54
 
53
- **Fix**: Check the sign response for `requiresApprovalPopup`. If true, present the enclave challenge to the admin and submit the result via `add-review` (FormData).
55
+ **Fix**: In MultiAdmin mode, drive the `approval-model` exchange and confirm `recorded: true` / an increased `authCount`. FirstAdmin/Tideless mode signs server-side on `authorize` — no enclave step.
54
56
 
55
57
  ---
56
58
 
@@ -50,12 +50,17 @@
50
50
  -d "isIGAEnabled=true"
51
51
  ```
52
52
 
53
- 6. Approve/commit initial client change request:
53
+ 6. Approve/commit initial change requests (current `/iga/change-requests/...` surface; see `canon/iga-change-requests-api.md`):
54
54
  ```bash
55
- # Get pending client changes
56
- CLIENTS=$(curl -s "http://localhost:8080/admin/realms/$REALM/tide-admin/change-set/clients/requests" \
57
- -H "Authorization: Bearer $TOKEN")
58
- # Extract changeSets array, then sign and commit
55
+ # Authorize all pending, then commit ready ones (FirstAdmin bootstrap signs server-side)
56
+ curl -s -X POST "http://localhost:8080/admin/realms/$REALM/iga/change-requests/bulk-authorize" \
57
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
58
+ -d '{"actionTypeIn":["CREATE","DELETE"],"limit":100}'
59
+ for id in $(curl -s "http://localhost:8080/admin/realms/$REALM/iga/change-requests?status=PENDING" \
60
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[] | select(.readyToCommit==true) | .id'); do
61
+ curl -s -X POST "http://localhost:8080/admin/realms/$REALM/iga/change-requests/$id/commit" \
62
+ -H "Authorization: Bearer $TOKEN"
63
+ done
59
64
  ```
60
65
 
61
66
  ---
@@ -49,19 +49,18 @@ The governance panel may optionally include policy management. These are NOT req
49
49
 
50
50
  ---
51
51
 
52
- ## Change-Set Endpoints (core governance)
52
+ ## Change-Request Endpoints (core governance)
53
+
54
+ Current `/iga/change-requests/...` surface (replaces legacy `/tide-admin/change-set/...`, GAP-065). **Full spec: `canon/iga-change-requests-api.md`.**
53
55
 
54
56
  | Operation | Endpoint | Method | Content-Type | Notes |
55
57
  |-----------|---------|--------|-------------|-------|
56
- | List counts | `/tide-admin/change-set/counts` | GET | — | Returns per-type counts |
57
- | List all requests | `/tide-admin/change-set/all/requests` | GET | — | Returns all pending changes grouped by type |
58
- | List by type | `/tide-admin/change-set/{type}/requests` | GET | | `{type}` = users, roles, clients, groups |
59
- | Sign (approve) | `/tide-admin/change-set/sign/batch` | POST | `application/json` | Body: `{ changeSets: [...] }` |
60
- | Commit | `/tide-admin/change-set/commit/batch` | POST | `application/json` | Body: `{ changeSets: [...] }` |
61
- | Cancel | `/tide-admin/change-set/cancel/batch` | POST | `application/json` | Body: `{ changeSets: [...] }` |
62
- | Add review (enclave) | `/tideAdminResources/add-review` | POST | **`multipart/form-data`** | Used after enclave challenge in MultiAdmin mode |
63
- | Add rejection | `/tideAdminResources/add-rejection` | POST | **`multipart/form-data`** | Content-type asymmetry: NOT JSON |
64
- | Activity | `/tide-admin/change-set/{id}/activity` | GET | | Returns approvals, rejections, comments |
65
- | Add comment | `/tide-admin/change-set/{id}/comments` | POST | `application/json` | |
66
- | Licensing requests | `/tideAdminResources/change-set/licensing/requests` | GET | — | Different path prefix from other types |
67
- | Ragnarok requests | `/ragnarok/change-set/offboarding/requests` | GET | — | Different path prefix |
58
+ | List | `/iga/change-requests?status=PENDING` | GET | — | Objects keyed by `id`; `status` = PENDING/APPROVED/DENIED/CANCELLED. Derive counts from the list. |
59
+ | Get one | `/iga/change-requests/{id}` | GET | — | Full CR (`entityType`, `readyToCommit`, `threshold`, `dependsOn`) |
60
+ | Authorize (approve) | `/iga/change-requests/{id}/authorize` | POST | `application/json` | Body `{}` optional. 409 = four-eyes re-sign |
61
+ | Bulk authorize | `/iga/change-requests/bulk-authorize` | POST | `application/json` | `{ "actionTypeIn": ["CREATE","DELETE"], "limit": 100 }`; 429 if a bulk run is active |
62
+ | Commit | `/iga/change-requests/{id}/commit` | POST | | 412 if under threshold / unmet dependency |
63
+ | Deny (reject) | `/iga/change-requests/{id}/deny` | POST | | 204 |
64
+ | Enclave sign (Tide MultiAdmin) | `/iga/change-requests/{id}/approval-model` | GET/POST | `application/json` | Two-phase: GET challenge POST `{ requestModel }` |
65
+ | Comments | `/iga/change-requests/{id}/comments` | GET/POST | `application/json` | `{ "comment": "..." }` (≤2000) |
66
+ | Manual ADOPT | `/iga/adopt` | POST | `application/json` | `{ "entityType": "USER", "entityId": "..." }` |
@@ -72,17 +72,18 @@ These happen before any admin uses the governance panel:
72
72
 
73
73
  Once bootstrap is complete, the governance panel operates as follows:
74
74
 
75
+ Endpoints use the current `/iga/change-requests/...` surface (replaces legacy `/tide-admin/change-set/...`, GAP-065; full spec `canon/iga-change-requests-api.md`).
76
+
75
77
  1. Admin logs in via TideCloak SSO (DPoP-bound)
76
- 2. Panel fetches change-set counts (`GET /tide-admin/change-set/counts`)
77
- 3. Panel lists pending requests by type (users, roles, groups, clients, settings, policies)
78
+ 2. Panel lists pending requests (`GET /iga/change-requests?status=PENDING`) and derives counts
79
+ 3. Panel groups pending CRs by `entityType` (users, roles, groups, clients, settings, policies)
78
80
  4. Admin reviews a change request's details, activity, and comments
79
- 5. Admin approves: `POST /tide-admin/change-set/sign/batch` (JSON body)
80
- - If MultiAdmin: enclave challenge popup appears; admin signs and submits via `POST /tideAdminResources/add-review` (**multipart/form-data**, not JSON)
81
- - If FirstAdmin: signed immediately with VRK
82
- 6. When approval count >= quorum threshold: `POST /tide-admin/change-set/commit/batch`
83
- 7. Admin can cancel pending requests: `POST /tide-admin/change-set/cancel/batch`
84
- 8. Admin can reject: `POST /tideAdminResources/add-rejection` (**multipart/form-data**)
85
- 9. Admin can add/edit/delete comments on change requests
81
+ 5. Admin approves: `POST /iga/change-requests/{id}/authorize` (body `{}`; 409 = four-eyes)
82
+ - If Tide MultiAdmin: complete the two-phase `GET`/`POST /iga/change-requests/{id}/approval-model` enclave exchange
83
+ - If FirstAdmin/Tideless: signed server-side immediately
84
+ 6. When `readyToCommit`: `POST /iga/change-requests/{id}/commit` (412 if under threshold)
85
+ 7. Admin can reject: `POST /iga/change-requests/{id}/deny`
86
+ 8. Admin can add/edit/delete comments on change requests
86
87
 
87
88
  ---
88
89
 
@@ -104,10 +105,11 @@ Once bootstrap is complete, the governance panel operates as follows:
104
105
 
105
106
  | Symptom | Likely cause | Fix |
106
107
  |---------|-------------|-----|
107
- | Sign returns 401 | Token expired or DPoP not attached | Re-login; ensure `secureFetch` with DPoP |
108
- | Sign returns enclave challenge (MultiAdmin) | Multiple admins exist | Handle popup flow via `add-review` endpoint |
109
- | Commit returns error | Not all required approvals met | Check approval count vs quorum threshold |
110
- | Change request stays PENDING after sign | Missing commit step | Call `commit/batch` after sign succeeds |
108
+ | Authorize returns 401 | Token expired or DPoP not attached | Re-login; ensure `secureFetch` with DPoP |
109
+ | Authorize returns 409 | Same admin re-signing (four-eyes) or CR not PENDING | Use a different admin; refresh the list |
110
+ | MultiAdmin: authorize does not record | Enclave step skipped | Complete `{id}/approval-model` two-phase exchange |
111
+ | Commit returns 412 | Under threshold / unmet dependency | Collect more approvals; commit `dependsOn` CR first |
112
+ | Change request stays PENDING after authorize | Missing commit step | Call `{id}/commit` once `readyToCommit` |
111
113
  | add-review returns 415 | Wrong content-type | Use `multipart/form-data`, not JSON |
112
114
  | Counts don't update after commit | Stale data | Refresh counts after every mutation |
113
115
  | Policy change requests don't appear | Realm policy not in pending state | Create pending realm policy first via `/tide-admin/realm-policy/pending` |
@@ -48,12 +48,11 @@ All steps must complete before users can safely use the app. Order matters.
48
48
 
49
49
  ## Phase 4: Change-set approval
50
50
 
51
- 10. **Approve pending change-sets**
52
- - IGA creates draft change-sets for client and user mutations made during setup.
53
- - List: `GET /admin/realms/{realm}/tide-admin/change-set/{type}/requests` for types `clients`, `users`.
54
- - For each pending change-set:
55
- - Sign: `POST /admin/realms/{realm}/tide-admin/change-set/sign` (requires admin doken).
56
- - Commit: `POST /admin/realms/{realm}/tide-admin/change-set/commit`.
51
+ 10. **Approve pending change requests** (current `/iga/change-requests/...` surface — see `canon/iga-change-requests-api.md`)
52
+ - IGA creates draft change requests for client and user mutations made during setup (each mutating write returns 202).
53
+ - List: `GET /admin/realms/{realm}/iga/change-requests?status=PENDING` (objects keyed by `id`).
54
+ - Approve: `POST /admin/realms/{realm}/iga/change-requests/bulk-authorize` (`{"actionTypeIn":["CREATE","DELETE"],"limit":100}`; FirstAdmin signs server-side).
55
+ - Commit: `POST /admin/realms/{realm}/iga/change-requests/{id}/commit` for each `readyToCommit` CR.
57
56
  - Without this step, client and user changes are in draft state and not active.
58
57
 
59
58
  ## Phase 5: IDP settings and adapter export
@@ -28,12 +28,12 @@ All steps must complete before users can safely use signing features. Order matt
28
28
  - `POST /admin/realms/{realm}/tide-admin/toggle-iga` with form-urlencoded `isIGAEnabled=true`.
29
29
  - Must happen after licensing. Enables change-set governance for role/user mutations.
30
30
 
31
- ## Phase 3: Approve initial change-sets
31
+ ## Phase 3: Approve initial change requests
32
32
 
33
- 6. **Approve client change-sets**
34
- - IGA creates draft change-sets for client mutations during realm import.
35
- - List: `GET /admin/realms/{realm}/tide-admin/change-set/clients/requests`.
36
- - For each: sign (`POST .../change-set/sign`) then commit (`POST .../change-set/commit`).
33
+ 6. **Approve initial change requests** (current `/iga/change-requests/...` surface — see `canon/iga-change-requests-api.md`)
34
+ - IGA creates draft change requests for client/role mutations during realm import (each mutating write returns 202).
35
+ - List: `GET /admin/realms/{realm}/iga/change-requests?status=PENDING` (objects keyed by `id`).
36
+ - Approve: `POST /admin/realms/{realm}/iga/change-requests/bulk-authorize` (`{"actionTypeIn":["CREATE","DELETE"],"limit":100}`), then `POST /admin/realms/{realm}/iga/change-requests/{id}/commit` for each `readyToCommit` CR. FirstAdmin bootstrap signs server-side.
37
37
 
38
38
  ## Phase 4: Admin user setup
39
39
 
@@ -52,11 +52,11 @@ All steps must complete before users can safely use signing features. Order matt
52
52
  - Poll `GET /admin/realms/{realm}/users/{userId}` until `attributes.tideUserKey` and `attributes.vuid` are present.
53
53
  - Do not proceed until both attributes exist.
54
54
 
55
- ## Phase 5: Approve user change-sets
55
+ ## Phase 5: Approve user change requests
56
56
 
57
- 11. **Approve user change-sets**
58
- - List: `GET /admin/realms/{realm}/tide-admin/change-set/users/requests`.
59
- - For each: sign then commit.
57
+ 11. **Approve user change requests**
58
+ - List: `GET /admin/realms/{realm}/iga/change-requests?status=PENDING`.
59
+ - Authorize then commit (see Phase 3 / `canon/iga-change-requests-api.md`).
60
60
 
61
61
  ## Phase 6: IDP settings and adapter export
62
62
 
@@ -54,7 +54,7 @@ Troubleshoot broken Tide integrations. Own login failures, missing roles/claims,
54
54
  |---------|-------------|----------|
55
55
  | Login hangs or blank screen | CSP missing `frame-src '*'`, redirect handler missing, DPoP auth page missing | `diagnose-broken-login` |
56
56
  | Login redirect loop | `@tidecloak/react` ESM alias missing (AP-42), provider not wired | `diagnose-broken-login` |
57
- | DPoP login fails | `tide_dpop_auth.html` missing or dynamic route not configured (I-12, L-07) | `diagnose-broken-login` |
57
+ | DPoP login fails | `tide_dpop_auth.html` missing, or its `app/tide_dpop/[...path]/route.ts` catch-all route handler / hash-pinned CSP / `Allow-CSP-From: *` header not configured (I-12, L-07) | `diagnose-broken-login` |
58
58
  | 401 on all API calls | JWT verification not implemented, or DPoP proof missing | `diagnose-missing-roles-or-claims` |
59
59
  | 403 on API calls | Role not assigned, IGA change-set not committed, or 120s refresh delay | `diagnose-missing-roles-or-claims` |
60
60
  | Roles missing from JWT | IGA change-set not committed, token not refreshed (up to 120s) | `diagnose-missing-roles-or-claims` |
@@ -17,11 +17,11 @@ Wire the Tide SDK into the application. Own SDK installation, provider setup, co
17
17
  | `tidecloak.json` placement and import | `tide-setup` if file doesn't exist and TideCloak not bootstrapped |
18
18
  | `silent-check-sso.html` in `public/` | — |
19
19
  | Post-auth redirect handler (`auth/redirect/page.tsx`) | — |
20
- | `tide_dpop_auth.html` + `next.config.ts` rewrites + DPoP headers (I-12) | — |
20
+ | `tide_dpop_auth.html` served via a `app/tide_dpop/[...path]/route.ts` catch-all route handler + DPoP headers (I-12) | — |
21
21
  | CSP (`frame-src '*'`) in `next.config` headers | — |
22
22
  | Webpack workarounds (`strictExportPresence`, `@tidecloak/react` ESM alias) | — |
23
23
  | Retrofit into existing apps | — |
24
- | Server-side delegation setup (`@tidecloak/server`, `createTideFetch`) | — |
24
+ | ASP.NET Core (.NET 10) backend via `Tide.Asgard.AspNetCore` SDK | Follow [playbooks/protect-aspnet-core-asgard.md](../../playbooks/protect-aspnet-core-asgard.md). Out of scope for the Next.js/React/Vanilla priority do not invent OIDC wiring; route to that playbook. |
25
25
  | Route/API protection | `tide-route-and-api-protection` |
26
26
  | Roles, RBAC, encryption | `tide-rbac-and-e2ee` |
27
27
  | TideCloak bootstrap, realm, licensing | `tide-setup` |
@@ -35,6 +35,7 @@ Wire the Tide SDK into the application. Own SDK installation, provider setup, co
35
35
  - `tide-setup` detected hardening gaps (Path D)
36
36
  - User asks to "add Tide to my app" or "add login"
37
37
  - Orchestrator routed here after bootstrap is confirmed
38
+ - User asks about adding Tide auth to an **ASP.NET Core / C# / .NET** API. Route directly to [playbooks/protect-aspnet-core-asgard.md](../../playbooks/protect-aspnet-core-asgard.md) — do not attempt to retrofit Node-side guidance.
38
39
 
39
40
  ### Scenario-disambiguation gate (I-17)
40
41
 
@@ -71,8 +72,8 @@ Follow playbook `add-auth-nextjs-fresh`:
71
72
  3. Place `tidecloak.json` at correct path (`data/` for Next.js, `public/` for React/Vite)
72
73
  4. Create `public/silent-check-sso.html`
73
74
  5. Create post-auth redirect handler at `auth/redirect/page.tsx`
74
- 6. Copy `tide_dpop_auth.html` to `public/` and add `rewrites()` + DPoP-specific `headers()` in `next.config.ts` (I-12). Do NOT use a route handler.
75
- 7. Configure CSP: `frame-src '*'` in `next.config` headers (global); `script-src 'unsafe-inline'` on `/tide_dpop/:path*` only
75
+ 6. Copy `tide_dpop_auth.html` to `public/` and serve it via a catch-all route handler at `app/tide_dpop/[...path]/route.ts` (matches the shipped `@tidecloak/create-nextjs` scaffold; I-12). The handler reads the single bundled `public/tide_dpop_auth.html` and returns it for any `/tide_dpop/...` path. Do NOT use `next.config.ts` rewrites for this.
76
+ 7. On that route handler's response, set a **sha256 hash-pinned** CSP (pin the file's inline `script`/`style` via `sha256-...` hashes — NOT `script-src 'unsafe-inline'`) plus an `Allow-CSP-From: *` header (lets the ORK embed the page cross-origin). Global CSP stays `frame-src '*'` in `next.config` headers.
76
77
  8. Add webpack workarounds to `next.config.ts`: `strictExportPresence = false` + `@tidecloak/react` ESM alias
77
78
 
78
79
  ### Existing app (has other auth)
@@ -82,16 +83,6 @@ Follow playbook `add-auth-nextjs-existing`:
82
83
  2. Replace existing auth provider with `TideCloakProvider`
83
84
  3. Remove old auth (NextAuth, Clerk, etc.) after Tide is working
84
85
 
85
- ### Server-side delegation
86
-
87
- Follow playbook `setup-server-delegation`:
88
- 1. Install `@tidecloak/server`
89
- 2. Create `TideDelegation` singleton with TideCloak config
90
- 3. Wire `/api/delegation` POST endpoint with `handleDelegation()`
91
- 4. Add `requireDelegation()` middleware to admin routes
92
- 5. Wrap browser fetch with `createTideFetch` from `@tidecloak/js`
93
- 6. Optionally scope delegation roles via `requireDelegation({ roles: { ... } })`
94
-
95
86
  ### Hardening gaps (from tide-setup Path D)
96
87
 
97
88
  Fix each missing item per the table in `tide-setup`.
@@ -105,11 +96,10 @@ Fix each missing item per the table in `tide-setup`.
105
96
  - [ ] `tidecloak.json` exists with `jwk`, `vendorId`, `homeOrkUrl`
106
97
  - [ ] `public/silent-check-sso.html` exists
107
98
  - [ ] Post-auth redirect handler exists at configured `redirectUri`
108
- - [ ] `public/tide_dpop_auth.html` exists + `next.config.ts` has `rewrites()` for `/tide_dpop/:path*` and DPoP-specific `headers()`
99
+ - [ ] `public/tide_dpop_auth.html` exists + `app/tide_dpop/[...path]/route.ts` catch-all route handler serves it with a sha256 hash-pinned CSP and `Allow-CSP-From: *`
109
100
  - [ ] CSP includes `frame-src '*'`
110
101
  - [ ] Webpack config has `strictExportPresence = false` + `@tidecloak/react` ESM alias
111
102
  - [ ] Login flow completes: redirect to Tide IdP -> auth -> callback -> app
112
- - [ ] If server-side admin calls needed: `@tidecloak/server` installed, delegation endpoints wired
113
103
 
114
104
  ---
115
105
 
@@ -132,5 +122,6 @@ Next: Security Engineer | STOP if integration incomplete
132
122
  - Do not create ad hoc auth wiring. Follow the playbook.
133
123
  - Do not use `NEXT_PUBLIC_TIDECLOAK_*` env vars. Use `tidecloak.json` directly. (AP-38)
134
124
  - Do not pass `useDPoP` as a JSX prop. It goes inside the config object. (AP-42, session-002)
135
- - Do not modify `tide_dpop_auth.html`. It is integrity-checked. (I-12, L-07)
136
- - Do not apply `script-src 'unsafe-inline'` globally. Only on the DPoP auth route. (L-07)
125
+ - Do not modify `tide_dpop_auth.html`. It is integrity-checked and its inline script/style are sha256-pinned in the route handler's CSP. (I-12, L-07)
126
+ - Do not serve the DPoP auth page with `script-src 'unsafe-inline'`. Pin its inline script/style with `sha256-...` hashes in the route handler's CSP instead. (L-07)
127
+ - Do not use `next.config.ts` `rewrites()` to serve the DPoP auth page. Use the `app/tide_dpop/[...path]/route.ts` catch-all route handler (the shipped `@tidecloak/create-nextjs` approach). (I-12)
@@ -0,0 +1,139 @@
1
+ # Role: MCP QA Engineer (Pre-Release Gate)
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ Decide whether the Tide MCP pack is safe to release. This role runs the deterministic test gate, then does the semantic review the gate cannot, and issues a single verdict: **SHIP**, **SHIP_WITH_WARNINGS**, or **BLOCK**. It is the pre-release quality gate for the pack (canon, playbooks, skills, prompts, reference-apps, and the MCP server that exposes them).
8
+
9
+ This role is **read-only and advisory about the release**. It runs tests and reads content; it does not fix defects it finds. When it finds a blocker, it names the file and the fix owner and blocks the release.
10
+
11
+ ## Two layers, one verdict
12
+
13
+ 1. **Deterministic gate (objective, must be green).** `cd mcp-server && npm test` — protocol smoke tests (all tools/prompts present, each returns correct-looking content, graceful errors) plus content-consistency checks (no stray legacy endpoints, no merge markers, referenced playbooks exist, SG-01..SG-18 present, GAP counts sum, honesty guards present). **Any red here is an automatic BLOCK.**
14
+ 2. **Semantic review (judgment, gate can't see it).** Spot-read tool outputs and doctrine for correctness, coherence, and honesty; drive a sample of eval cases; confirm new/changed capabilities actually lead an agent to the right behavior.
15
+
16
+ A release is **SHIP** only when the gate is green *and* semantic review finds no correctness/honesty blocker.
17
+
18
+ ---
19
+
20
+ ## Boundary
21
+
22
+ | This role owns | Does NOT own |
23
+ |----------------|--------------|
24
+ | Running `npm test` and interpreting it | Fixing failing tests or defects |
25
+ | Semantic/coherence review of tool outputs & doctrine | Writing new pack content |
26
+ | Driving sample eval cases | Building features |
27
+ | Honesty/overclaim audit | Changing doctrine to make a test pass |
28
+ | The release verdict (SHIP / WARN / BLOCK) | Publishing/releasing (that's the human's call after the verdict) |
29
+
30
+ **Hard rule:** never weaken a check or edit doctrine to turn a gate green. If a test is wrong, report it as a test defect; do not silence it.
31
+
32
+ ---
33
+
34
+ ## When to Trigger
35
+
36
+ - Before publishing/releasing the MCP pack or the npm package.
37
+ - After a substantial doctrine change (a reconciliation like the IGA API migration, a new capability, a merge of feature branches).
38
+ - On demand: "QA the MCP", "is the pack ready to release", "run the release gate".
39
+
40
+ ## When NOT to Trigger
41
+
42
+ - Building or editing pack content (that's the authoring roles).
43
+ - Diagnosing a live TideCloak app (that's `tide-diagnostics`).
44
+ - Security-analysing a customer system (that's `tide-security-analyst`).
45
+
46
+ ---
47
+
48
+ ## Execution
49
+
50
+ ### Step 1 — Run the deterministic gate
51
+ ```bash
52
+ cd mcp-server && npm test
53
+ ```
54
+ Record the summary line and every failing check verbatim. If the harness itself crashes, that is a BLOCK (the gate is not trustworthy). If any check is red, the verdict is **BLOCK** — capture the failures and skip to the report; do not rationalize a red gate into a ship.
55
+
56
+ ### Step 2 — Semantic review (only meaningful once the gate is green)
57
+ Call the tools through the MCP server (or read the assets directly) and judge what the grep-level gate cannot:
58
+
59
+ - **Correctness of guidance.** Read `tide_security_analysis`, `tide_hosting`, and the IGA reference (`tide_canon iga-change-requests-api`). Do the endpoints, payloads, and status codes read as internally consistent? Would an agent following them do the right thing?
60
+ - **Coherence after change.** For the area that changed since last release, read the primary canon + one consumer (a playbook or reference-app) and confirm they agree. (E.g. after the IGA migration: `canon/iga-change-requests-api.md` vs a bootstrap script vs `setup-iga-admin-panel` — same surface, same payloads.)
61
+ - **Routing.** `tide_choose_playbook` / `tide_choose_scenario` on a few real phrasings land on the right path (hosted→skycloak, "security analysis"→analyst, "org password manager"→scenario, ambiguous→disambiguation, not silent default).
62
+ - **Honesty audit (load-bearing).** Confirm the pack does NOT overclaim:
63
+ - Security analysis keeps the out-of-scope section and never claims Tide fixes injection/XSS/deps (AP-SEC-1).
64
+ - Hosting is not sold as fully turnkey before GAP-066 (AP-HOST-2); the Tideless-IGA caveat is present.
65
+ - IGA "no single bypass / tamper-evident" claims are scoped to Tide mode, not Tideless.
66
+ - Anything marked REQUIRES_RUNTIME_VALIDATION (e.g. the IGA bootstrap loop) is flagged as such, not asserted as verified.
67
+
68
+ ### Step 3 — Drive sample eval cases
69
+ Pick a representative spread from `evals/cases.yaml` (a fresh-setup case, a protection case, a security-analysis case, a hosting case, an IGA governance case). For each, reason: given the pack's current content, would an agent hit the `expected_behavior` and avoid the `failure_conditions`? Note any case the pack would now fail. (This is judgment, not execution — say so.)
70
+
71
+ ### Step 4 — Verdict + report
72
+ Emit the Release Readiness Report. One verdict:
73
+ - **SHIP** — gate green, no semantic blocker, no unmanaged overclaim.
74
+ - **SHIP_WITH_WARNINGS** — gate green; only non-blocking issues (cosmetic, low-risk doc gaps, known REQUIRES_RUNTIME_VALIDATION items that are correctly labelled). List them.
75
+ - **BLOCK** — any red gate check, any harness crash, any correctness or honesty defect, or a sample eval the pack would now fail. Name each blocker + the file + the fix owner (which authoring role).
76
+
77
+ ---
78
+
79
+ ## Release Readiness Report (required format)
80
+
81
+ ```
82
+ # Tide MCP — Release Readiness Report
83
+ Verdict: SHIP | SHIP_WITH_WARNINGS | BLOCK
84
+ Scope: <what changed since last release / full pack>
85
+
86
+ ## Deterministic gate
87
+ Command: cd mcp-server && npm test
88
+ Result: <N/M passed> (PASS/FAIL)
89
+ Failures: <each red check verbatim, or "none">
90
+
91
+ ## Semantic review
92
+ - Correctness: <observations, or "no issues">
93
+ - Coherence (changed area): <what you cross-checked → verdict>
94
+ - Routing: <phrasings tried → where they landed>
95
+ - Honesty audit: <overclaim checks → pass/fail with evidence>
96
+
97
+ ## Sample eval cases
98
+ <case id → would-pass / would-fail (why)>
99
+
100
+ ## Blockers (if any)
101
+ 1. <defect> — <file:line> — fix owner: <authoring role/skill>
102
+
103
+ ## Warnings (non-blocking)
104
+ - <item, why it's acceptable to ship>
105
+
106
+ ## Recommendation
107
+ <one line: release, or fix blockers then re-run the gate>
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Verification (of the QA pass itself)
113
+
114
+ - [ ] `npm test` was actually run this pass; its summary is quoted.
115
+ - [ ] A red gate or harness crash was reported as BLOCK (never rationalized).
116
+ - [ ] The honesty audit ran and its results are in the report.
117
+ - [ ] Every blocker names a file and a fix owner.
118
+ - [ ] No check was weakened and no doctrine was edited to pass the gate.
119
+
120
+ ## Anti-Patterns
121
+
122
+ - **AP-QA-1** — Shipping on a red or crashed gate. The deterministic gate is a hard floor.
123
+ - **AP-QA-2** — Editing a test or doctrine to turn a check green instead of reporting the defect. QA reports; it does not launder failures.
124
+ - **AP-QA-3** — Skipping the honesty audit. Overclaiming (turnkey hosting, crypto-tamper-evidence on Tideless, "fixes XSS") is a release blocker, not a warning.
125
+ - **AP-QA-4** — Reporting a green gate as SHIP without semantic review. Grep-green is necessary, not sufficient.
126
+ - **AP-QA-5** — Treating a correctly-labelled REQUIRES_RUNTIME_VALIDATION item as a blocker. It ships as a warning; an *unlabelled* one is the blocker.
127
+
128
+ ## Handoff Trace
129
+
130
+ ```
131
+ [TRACE]
132
+ Scenario: pre-release QA of the Tide MCP pack
133
+ Role: MCP QA Engineer
134
+ Reason: <release / post-change gate>
135
+ Preconditions: mcp-server builds; npm test runnable
136
+ Verdict: <SHIP | SHIP_WITH_WARNINGS | BLOCK>
137
+ Next: <release, or route blockers to named authoring roles>
138
+ [/TRACE]
139
+ ```
@@ -43,7 +43,7 @@ Self-encryption (this skill) is **permanently user-bound**. It cannot be upgrade
43
43
  - App is not Tide-enabled. Route to `tide-setup` skill.
44
44
  - API routes do not verify JWT yet. Route to `tide-route-and-api-protection` skill first. RBAC on unverified tokens is meaningless.
45
45
  - User needs shared/group encryption (policy-governed VVK encryption with Forseti contracts). That is covered by playbook `setup-forseti-e2ee`, not this skill. This skill covers self-encryption only.
46
- - User asks about Keycloak-to-TideCloak migration. Not yet documented (GAP-023).
46
+ - User asks about a **direct Keycloak-to-TideCloak** migration. That specific path is not yet documented (GAP-023). (Migrating an app off generic-OIDC / NextAuth / Clerk is a separate task covered by `playbooks/migrate-from-existing-auth.md`; the open gap is the KC→TideCloak realm/data migration itself.)
47
47
 
48
48
  **Early sharing detection**: If the user request mentions any of: "sharing", "share encrypted", "chosen recipients", "other users can decrypt", "cross-user decryption", "encrypt for others", "selective sharing" — route directly to `setup-forseti-e2ee`. Do NOT start with self-encryption and attempt to add sharing later. Self-encryption cannot be upgraded to shared encryption by renaming roles or changing parameters. The SDK call path must be different from the start. VERIFIED (session-001, F-07).
49
49
 
@@ -139,8 +139,8 @@ Self-encryption is user-bound. Only the encrypting user can decrypt their own da
139
139
  - [ ] Bypassing UI (direct curl) still enforces role server-side
140
140
 
141
141
  ### Self-Encryption (if applicable)
142
- - [ ] `doEncrypt('tag', data)` succeeds for user with `_tide_{tag}.selfencrypt` role
143
- - [ ] `doDecrypt('tag', ciphertext)` returns original data for same user
142
+ - [ ] `doEncrypt([{ data, tags: ['tag'] }])` succeeds for user with `_tide_{tag}.selfencrypt` role
143
+ - [ ] `doDecrypt([{ data: ciphertext, tags: ['tag'] }])` returns original data for same user
144
144
  - [ ] `doEncrypt` fails for user without the selfencrypt role
145
145
  - [ ] `doDecrypt` by a different user fails (expected behavior — self-encryption is user-bound; A-27 INFERRED, not runtime-confirmed)
146
146
  - [ ] Ciphertext stored in DB is not plaintext
@@ -181,8 +181,8 @@ Self-encryption is user-bound. Only the encrypting user can decrypt their own da
181
181
 
182
182
  This is a policy-flow failure, not a role issue. See T-14.
183
183
 
184
- 1. Check admin policy was fetched from `GET /realms/{realm}/tide-policy-resources/admin-policy`
185
- 2. Check admin policy bytes were base64-decoded before use
184
+ 1. Check admin policy was fetched (admin bearer) from `GET /admin/realms/{realm}/iga/role-policies`, reading the signed bytes from the `policy` field (the public `tide-policy-resources/admin-policy` endpoint does not exist on main)
185
+ 2. Check admin policy bytes were base64-decoded from the `policy` field before use
186
186
  3. Check `addPolicy(adminPolicyBytes)` was called after approval, not during request construction
187
187
  4. Check URL construction has no double slashes (strip trailing slash from `auth-server-url`)
188
188
 
@@ -65,7 +65,7 @@ The Reviewer is optional per-step but **mandatory before handing the app to the
65
65
  - [ ] Does `next.config.ts` have `strictExportPresence = false`? (not `reexportExportsPresence`)
66
66
  - [ ] Does `next.config.ts` have the `@tidecloak/react` ESM alias? (AP-42)
67
67
  - [ ] Does `public/tide_dpop_auth.html` exist? (I-12, L-07)
68
- - [ ] Does `next.config.ts` have `rewrites()` for `/tide_dpop/:path*` and DPoP-specific `headers()` with `script-src 'unsafe-inline'`? (I-12)
68
+ - [ ] Is the DPoP auth page served by a `app/tide_dpop/[...path]/route.ts` catch-all route handler (NOT `next.config.ts` rewrites) with a sha256 hash-pinned CSP (NOT `script-src 'unsafe-inline'`) and an `Allow-CSP-From: *` header? (I-12)
69
69
  - [ ] Does `public/silent-check-sso.html` exist? (I-07)
70
70
  - [ ] Does the redirect handler exist at the configured `redirectUri`? (I-16)
71
71
  - [ ] Is CSP set to `frame-src '*'`? (I-06)