@tideorg/mcp 1.4.0

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 (76) hide show
  1. package/GAP_REGISTER.md +117 -0
  2. package/README.md +38 -0
  3. package/adapters/AGENTS.md +241 -0
  4. package/adapters/CLAUDE.md +146 -0
  5. package/adapters/replit.md +224 -0
  6. package/canon/anti-patterns.md +2133 -0
  7. package/canon/concepts.md +816 -0
  8. package/canon/custom-contracts.md +533 -0
  9. package/canon/delegation.md +195 -0
  10. package/canon/feature-mapping.md +637 -0
  11. package/canon/framework-matrix.md +1125 -0
  12. package/canon/invariants.md +851 -0
  13. package/canon/redirect-handler.md +254 -0
  14. package/canon/tidecloak-bootstrap.md +287 -0
  15. package/canon/tidecloak-endpoints.md +294 -0
  16. package/canon/troubleshooting.md +1494 -0
  17. package/canon/version-policy.md +89 -0
  18. package/mcp-server/dist/index.d.ts +2 -0
  19. package/mcp-server/dist/index.js +605 -0
  20. package/package.json +45 -0
  21. package/playbooks/add-auth-nextjs-existing.md +799 -0
  22. package/playbooks/add-auth-nextjs-fresh.md +654 -0
  23. package/playbooks/add-rbac-nextjs.md +190 -0
  24. package/playbooks/bootstrap-realm-from-template.md +170 -0
  25. package/playbooks/configure-e2ee-roles-and-policies.md +196 -0
  26. package/playbooks/deploy-tidecloak-docker.md +792 -0
  27. package/playbooks/diagnose-broken-login.md +234 -0
  28. package/playbooks/diagnose-missing-roles-or-claims.md +253 -0
  29. package/playbooks/initialize-admin-and-link-account.md +235 -0
  30. package/playbooks/migrate-from-existing-auth.md +198 -0
  31. package/playbooks/protect-api-nextjs.md +451 -0
  32. package/playbooks/protect-routes-nextjs.md +544 -0
  33. package/playbooks/setup-forseti-e2ee.md +756 -0
  34. package/playbooks/setup-iga-admin-panel.md +344 -0
  35. package/playbooks/setup-server-delegation.md +142 -0
  36. package/playbooks/start-tidecloak-dev.md +130 -0
  37. package/playbooks/verify-jwt-server-side.md +439 -0
  38. package/prompts/add-admin-approval-flow.md +50 -0
  39. package/prompts/build-private-customer-portal.md +85 -0
  40. package/prompts/migrate-generic-auth-to-tide.md +67 -0
  41. package/prompts/secure-existing-app.md +80 -0
  42. package/reference-apps/INDEX.md +87 -0
  43. package/reference-apps/encrypted-communication/anti-patterns.md +123 -0
  44. package/reference-apps/encrypted-communication/bootstrap-sequence.md +152 -0
  45. package/reference-apps/encrypted-communication/manifest.yaml +100 -0
  46. package/reference-apps/encrypted-communication/role-policy-matrix.md +80 -0
  47. package/reference-apps/encrypted-communication/scenario.md +134 -0
  48. package/reference-apps/git-pr-signing-service/anti-patterns.md +61 -0
  49. package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +157 -0
  50. package/reference-apps/git-pr-signing-service/manifest.yaml +80 -0
  51. package/reference-apps/git-pr-signing-service/role-policy-matrix.md +99 -0
  52. package/reference-apps/git-pr-signing-service/scenario.md +169 -0
  53. package/reference-apps/iga-admin-governance/anti-patterns.md +133 -0
  54. package/reference-apps/iga-admin-governance/bootstrap-sequence.md +153 -0
  55. package/reference-apps/iga-admin-governance/manifest.yaml +87 -0
  56. package/reference-apps/iga-admin-governance/role-policy-matrix.md +67 -0
  57. package/reference-apps/iga-admin-governance/scenario.md +126 -0
  58. package/reference-apps/organisation-password-manager/anti-patterns.md +71 -0
  59. package/reference-apps/organisation-password-manager/bootstrap-sequence.md +127 -0
  60. package/reference-apps/organisation-password-manager/manifest.yaml +86 -0
  61. package/reference-apps/organisation-password-manager/role-policy-matrix.md +59 -0
  62. package/reference-apps/organisation-password-manager/scenario.md +107 -0
  63. package/reference-apps/policy-governed-signing/anti-patterns.md +67 -0
  64. package/reference-apps/policy-governed-signing/bootstrap-sequence.md +128 -0
  65. package/reference-apps/policy-governed-signing/manifest.yaml +78 -0
  66. package/reference-apps/policy-governed-signing/role-policy-matrix.md +62 -0
  67. package/reference-apps/policy-governed-signing/scenario.md +113 -0
  68. package/skills/tide-diagnostics/SKILL.md +99 -0
  69. package/skills/tide-integration/SKILL.md +136 -0
  70. package/skills/tide-learning-capture/SKILL.md +174 -0
  71. package/skills/tide-rbac-and-e2ee/SKILL.md +214 -0
  72. package/skills/tide-reviewer/SKILL.md +133 -0
  73. package/skills/tide-route-and-api-protection/SKILL.md +201 -0
  74. package/skills/tide-scenario-resolver/SKILL.md +81 -0
  75. package/skills/tide-setup/SKILL.md +218 -0
  76. package/skills/tide-solutions-architect/SKILL.md +130 -0
@@ -0,0 +1,174 @@
1
+ # Role: Learning Capture
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ Convert raw session findings into structured pack learnings, refinement backlog entries, and pack update candidates. This skill closes the feedback loop: real-world build failures become pack improvements.
8
+
9
+ ---
10
+
11
+ ## Boundary
12
+
13
+ | This skill owns | Does NOT own |
14
+ |----------------|-------------|
15
+ | Formatting raw findings into L-## entries | Deciding whether to apply the fix |
16
+ | Creating RB-### backlog entries | Applying patches to canon/playbooks/skills |
17
+ | Creating PUC-### update candidates | Reviewing or accepting implementations |
18
+ | Assigning severity and affected layer | Running evals or regression tests |
19
+ | Detecting duplicates against existing learnings | Changing MCP server code |
20
+
21
+ This skill captures and structures. It does not patch. Hand the structured output to the appropriate specialist or reteach pass.
22
+
23
+ ---
24
+
25
+ ## When to Trigger
26
+
27
+ - After a build session where something went wrong or behaved unexpectedly
28
+ - After a reviewer flags an issue that is not yet in the pack
29
+ - When raw notes exist but are not yet in the standard format
30
+ - When the user says "capture this", "log this finding", or "reteach"
31
+
32
+ ---
33
+
34
+ ## When NOT to Trigger
35
+
36
+ - For app-specific bugs that do not affect pack guidance
37
+ - For issues already captured in an existing LEARNINGS file (check first)
38
+ - For fixes that are already PATCHED in the refinement backlog
39
+
40
+ ---
41
+
42
+ ## Inputs
43
+
44
+ The skill accepts one or more raw findings. Each finding must include:
45
+
46
+ 1. **What happened** — the observed behavior
47
+ 2. **What should have happened** — the expected behavior
48
+ 3. **Where it happened** — which playbook, skill, scenario, canon file, or template was involved
49
+
50
+ Optional but valuable:
51
+ - Error messages or logs
52
+ - The user request that triggered the issue
53
+ - Which MCP tool or scenario was in use
54
+
55
+ ---
56
+
57
+ ## Output Format
58
+
59
+ ### Step 1: LEARNINGS entry (L-##)
60
+
61
+ Write to `notes/test-findings/LEARNINGS-<batch-name>.md`. Use the next available L-## number within that file.
62
+
63
+ Each entry follows this exact structure:
64
+
65
+ ```markdown
66
+ ## L-##: <short title>
67
+
68
+ **Finding**: <what happened and what the agent or user observed>
69
+
70
+ **Root cause**: <why it happened — which pack artifact gave wrong guidance, or what was missing>
71
+
72
+ **Proposed fix (pack layer)**: <which file(s) to change and what the change should be>
73
+
74
+ **Scenario affected**: <which scenario(s) or "all scenarios">
75
+
76
+ **Regression test**: <how to verify the fix works — ideally a concrete check>
77
+ ```
78
+
79
+ Rules:
80
+ - If the finding is app-specific and does not affect pack guidance, mark it `**Status**: App-specific. Not a pack issue.` and stop. Do not create RB or PUC entries.
81
+ - If the finding duplicates an existing learning, reference it instead: `**Status**: Duplicate of LEARNINGS-batch-007 L-03. No new entry needed.`
82
+
83
+ ### Step 2: Refinement backlog entry (RB-###)
84
+
85
+ Append one row to `notes/refinement-backlog.md`. Use the next available RB-### number.
86
+
87
+ Format:
88
+
89
+ ```
90
+ | RB-### | <issue summary> | <LEARNINGS file> L-##: <evidence> | <file(s) to patch> | <severity> | OPEN | <eval ID or "—"> |
91
+ ```
92
+
93
+ Severity levels:
94
+ - **HIGH** — agent produces broken or insecure output
95
+ - **MEDIUM** — agent produces suboptimal output or user must manually correct
96
+ - **LOW** — cosmetic, documentation, or edge case
97
+
98
+ ### Step 3: Pack update candidate (PUC-###)
99
+
100
+ Append to `notes/pack-update-candidates.md`. Use the next available PUC-### number.
101
+
102
+ Format:
103
+
104
+ ```markdown
105
+ ## PUC-###: <short title> (RB-###)
106
+
107
+ **Change**: <what to change, in one sentence>
108
+
109
+ **Files**:
110
+ - <file path 1>
111
+ - <file path 2>
112
+
113
+ **Why this layer**: <why this is the right file to patch, not a different one>
114
+
115
+ **MCP**: <"No MCP code change needed" or describe the MCP change>
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Duplicate Detection
121
+
122
+ Before creating any new entry, check:
123
+
124
+ 1. Scan existing `notes/test-findings/LEARNINGS*.md` files for similar root causes
125
+ 2. Scan `notes/refinement-backlog.md` for matching RB entries
126
+ 3. If a match exists and is PATCHED, check whether this is a regression (same issue returned) or a new variant
127
+
128
+ If duplicate: reference the existing entry and note whether it's a confirmed regression or a false alarm.
129
+
130
+ ---
131
+
132
+ ## Numbering
133
+
134
+ - **L-## numbers** are scoped per LEARNINGS file. Each file starts at L-01.
135
+ - **RB-### numbers** are global across the refinement backlog. Continue from the highest existing number.
136
+ - **PUC-### numbers** are global across pack-update-candidates. Continue from the highest existing number.
137
+
138
+ To find the next number, read the tail of the respective file.
139
+
140
+ ---
141
+
142
+ ## Batch Naming
143
+
144
+ Name the LEARNINGS file based on context:
145
+ - Session-based: `LEARNINGS-session-###.md` (for interactive build sessions)
146
+ - Batch-based: `LEARNINGS-batch-###.md` (for eval runs or bulk testing)
147
+ - Project-based: `<project>-learnings-batch-####.md` (for findings from a specific external project)
148
+
149
+ ---
150
+
151
+ ## Anti-Patterns
152
+
153
+ - **Do not create learnings for things that work correctly.** Only capture deviations.
154
+ - **Do not propose fixes outside the pack.** If the issue is in the SDK or TideCloak itself, note it as `**Status**: SDK/platform issue. Outside pack scope.` and stop after the L-## entry.
155
+ - **Do not combine multiple root causes into one entry.** If one session surfaced three issues, create three separate L-## entries, three RB rows, and three PUC sections.
156
+ - **Do not mark severity based on frequency.** A rare issue that produces insecure output is HIGH. A common issue that produces a cosmetic glitch is LOW.
157
+ - **Do not skip the duplicate check.** The most common waste is re-capturing something already patched.
158
+
159
+ ---
160
+
161
+ ## Handoff Trace
162
+
163
+ The skill emits a `[CAPTURE]` block when complete:
164
+
165
+ ```
166
+ [CAPTURE]
167
+ Batch: <LEARNINGS file name>
168
+ Entries: L-01 through L-##
169
+ Backlog: RB-### through RB-###
170
+ Candidates: PUC-### through PUC-###
171
+ Duplicates skipped: <count or "none">
172
+ Next: reteach pass to apply PUC entries, or manual review
173
+ [/CAPTURE]
174
+ ```
@@ -0,0 +1,214 @@
1
+ # Role: IAM / Policy Engineer
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ Add role-based access control to protected APIs using verified JWT claims. Optionally add self-encryption (user-bound E2EE) using Tide's tag-based role system. Both features require prior auth + API protection.
8
+
9
+ ---
10
+
11
+ ## When to Trigger
12
+
13
+ - User asks to "add roles", "add RBAC", "add permissions", or "add access levels"
14
+ - User asks to "encrypt data", "add E2EE", or "add field-level encryption" — **but first check the sharing gate below**
15
+ - User asks about `_tide_*` roles or tag-based encryption roles
16
+ - Agent detects API routes that verify JWT but do not check roles
17
+
18
+ ### Scenario-disambiguation gate (I-17)
19
+
20
+ Before starting encryption or RBAC work, resolve these branches:
21
+
22
+ | Branch | How to resolve |
23
+ |--------|---------------|
24
+ | Self-encryption vs shared encryption | Does anyone other than the encrypting user need to decrypt? If yes → `setup-forseti-e2ee`, not this skill. |
25
+ | Simple RBAC vs policy governance | Does the app need multi-admin approval, IGA change-sets, or Forseti contracts? If yes → `setup-forseti-e2ee` or `setup-iga-admin-panel`. |
26
+ | RBAC only vs RBAC + E2EE | Does the user need encryption at all, or just role-based access control? |
27
+
28
+ **Mandatory sharing gate** (instance of I-17 for encryption):
29
+
30
+ If the user's request mentions ANY of the following, **STOP** and route to playbook `setup-forseti-e2ee` instead of this skill:
31
+ - "share", "sharing", "shared"
32
+ - "other users can decrypt", "recipients", "chosen people"
33
+ - "cross-user", "multi-user", "group encryption"
34
+ - "encrypt for others", "selective access"
35
+ - any indication that someone other than the encrypting user needs to decrypt
36
+
37
+ Self-encryption (this skill) is **permanently user-bound**. It cannot be upgraded to shared encryption later. The SDK call path must be different from the start (`IAMService.doEncrypt(data, policyBytes)` instead of `doEncrypt`/`doDecrypt`). Building self-encryption first and trying to add sharing is a known failure mode (AP-24, session-001).
38
+
39
+ ---
40
+
41
+ ## When NOT to Trigger
42
+
43
+ - App is not Tide-enabled. Route to `tide-setup` skill.
44
+ - API routes do not verify JWT yet. Route to `tide-route-and-api-protection` skill first. RBAC on unverified tokens is meaningless.
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).
47
+
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
+
50
+ ---
51
+
52
+ ## Required Repo Inspection
53
+
54
+ ```bash
55
+ # 1. Confirm API protection exists (hard prerequisite)
56
+ ls lib/auth/tideJWT.ts lib/auth/protect.ts lib/auth/tidecloakConfig.ts 2>/dev/null
57
+ # ALL three must exist. If any is missing, complete tide-route-and-api-protection first.
58
+
59
+ # 2. Check jose is installed
60
+ grep '"jose"' package.json
61
+
62
+ # 3. Check existing role usage
63
+ grep -r 'hasRole\|withRole\|realm_access\|resource_access' app/api/ lib/auth/ --include="*.ts" 2>/dev/null
64
+
65
+ # 4. Check for E2EE usage
66
+ grep -r 'doEncrypt\|doDecrypt' app/ components/ --include="*.tsx" --include="*.ts" 2>/dev/null
67
+
68
+ # 5. Check for _tide_* role references
69
+ grep -r '_tide_' app/ components/ lib/ --include="*.ts" --include="*.tsx" 2>/dev/null
70
+ ```
71
+
72
+ If check 1 fails, stop. The `tide-route-and-api-protection` skill must be completed first.
73
+
74
+ ---
75
+
76
+ ## Preconditions
77
+
78
+ - Tide setup complete (SDK, provider, adapter JSON)
79
+ - Login working
80
+ - Server-side JWT verification implemented (`lib/auth/tideJWT.ts` with `verifyTideJWT()` and `hasRole()`)
81
+ - Auth middleware implemented (`lib/auth/protect.ts` with `withAuth()` and `withRole()`)
82
+ - Roles created in TideCloak Admin Console and assigned to users (via IGA if enabled)
83
+
84
+ ---
85
+
86
+ ## Execution Workflow
87
+
88
+ ### Part 1: Server-Side RBAC
89
+
90
+ **Playbook**: `add-rbac-nextjs`
91
+
92
+ Follow the playbook to apply `withRole()` to API routes and optionally add client-side `hasRealmRole()` / `hasClientRole()` for UI gating.
93
+
94
+ **Key facts** (not repeated from playbook — these are pitfalls the playbook assumes you know):
95
+
96
+ - Roles live in `realm_access.roles` (realm) and `resource_access.{client}.roles` (client). The server-side `hasRole()` utility from `verify-jwt-server-side` checks both.
97
+ - The client SDK hook exports `hasRealmRole(role)` and `hasClientRole(role, client?)`. There is no generic `hasRole()` on the hook.
98
+ - `tide-realm-admin` is a **client role** on `realm-management`, not a realm role. Use `hasClientRole('tide-realm-admin', 'realm-management')`, not `hasRealmRole('tide-realm-admin')`. (AP-29)
99
+ - Client-side `hasRealmRole()` / `hasClientRole()` from `useTideCloak()` is UX only — the API still enforces server-side.
100
+
101
+ ### Part 2: Self-Encryption (E2EE) — private data only, NO sharing
102
+
103
+ **STOP CHECK**: Did the user's request mention sharing, recipients, or multiple users decrypting? If yes, do NOT proceed. Route to `setup-forseti-e2ee` immediately. Self-encryption CANNOT be shared. This is not fixable by renaming roles.
104
+
105
+ Self-encryption is user-bound. Only the encrypting user can decrypt their own data. For shared encryption between users, use policy-governed VVK encryption instead (playbook `setup-forseti-e2ee`).
106
+
107
+ **Playbook**: `add-rbac-nextjs` (E2EE section) covers the implementation steps: creating `_tide_{tag}.selfencrypt` / `_tide_{tag}.selfdecrypt` roles, calling `doEncrypt` / `doDecrypt`, and storing ciphertext.
108
+
109
+ **Prerequisites for E2EE** (verify before starting):
110
+ - IGA enabled on the realm (adapter JSON must have `jwk` field)
111
+ - Tag roles created and assigned to users in TideCloak
112
+ - Users have at least one `_tide_*` role for voucher gating
113
+
114
+ **Key facts not in the playbook**:
115
+ - Tags are fully application-defined, case-sensitive, max ~237 chars. See `canon/feature-mapping.md`.
116
+ - The server stores ciphertext only. It cannot decrypt — decryption requires live Fabric threshold participation in the user's browser.
117
+ - Use `TEXT` or `BYTEA` DB columns. `VARCHAR(512)` covers plaintexts up to ~150 bytes after base64 overhead.
118
+
119
+ ---
120
+
121
+ ## Safety Checks
122
+
123
+ - **Do not mix `_tide_*` roles with app roles.** `_tide_ssn.selfencrypt` is for E2EE voucher gating, not for granting admin access.
124
+ - **Self-encryption is NOT shared encryption.** Only the encrypting user can decrypt. For shared data, use Forseti-governed VVK encryption (playbook `setup-forseti-e2ee`).
125
+ - **Do not rename roles to enable sharing.** Changing `selfencrypt` → `encrypt` in role names does not switch encryption models. The SDK call path determines self vs shared. See AP-26 in `canon/anti-patterns.md`.
126
+ - **E2EE requires online Fabric access.** No offline decryption. No server-side decryption. Do not cache session keys. (I-11, AP-04)
127
+ - **Do not invent E2EE patterns** not covered by `canon/feature-mapping.md` or the `add-rbac-nextjs` playbook.
128
+ - **Token refresh delay**: After IGA role assignment, roles appear in JWT/doken after next token refresh (up to 120s).
129
+
130
+ ---
131
+
132
+ ## Verification Checklist
133
+
134
+ ### RBAC
135
+ - [ ] API with `withRole('admin')` returns 403 for non-admin user
136
+ - [ ] API with `withRole('admin')` returns 200 for admin user
137
+ - [ ] Client-side `hasRole('admin')` shows correct UI for each role
138
+ - [ ] Composite roles (if used) propagate permissions correctly
139
+ - [ ] Bypassing UI (direct curl) still enforces role server-side
140
+
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
144
+ - [ ] `doEncrypt` fails for user without the selfencrypt role
145
+ - [ ] `doDecrypt` by a different user fails (expected behavior — self-encryption is user-bound; A-27 INFERRED, not runtime-confirmed)
146
+ - [ ] Ciphertext stored in DB is not plaintext
147
+
148
+ ---
149
+
150
+ ## Repair Path
151
+
152
+ ### Role always missing (403 for everyone)
153
+ 1. Decode JWT: `node -e "console.log(JSON.parse(atob('TOKEN_PAYLOAD_PART')))"` — check `realm_access.roles`
154
+ 2. Verify role exists in TideCloak Admin Console → Roles
155
+ 3. Verify role is assigned to user → Users → Role Mappings
156
+ 4. If IGA is enabled, verify the role assignment change-set was approved and committed
157
+ 5. Wait up to 120s or force token refresh (logout/login)
158
+ 6. See playbook `diagnose-missing-roles-or-claims`
159
+
160
+ ### E2EE fails with "User has not been given any access"
161
+
162
+ **This is a self-encryption setup problem.** Do not rename roles or switch encryption models.
163
+
164
+ 1. Check user has `_tide_{tag}.selfencrypt` or `_tide_{tag}.selfdecrypt` role
165
+ 2. Check user has at least one `_tide_*` role for voucher gating
166
+ 3. Check tag name matches exactly (case-sensitive)
167
+ 4. Check adapter JSON has `jwk` field (IGA must be enabled)
168
+ 5. Check IGA change requests for roles are approved and committed
169
+ 6. User must re-login after role assignment (up to 120s propagation delay)
170
+
171
+ **Forbidden response**: Do not rename `_tide_{tag}.selfencrypt` to `_tide_{tag}.encrypt`. The role suffix does not change the encryption model. See AP-26 and T-13.
172
+
173
+ **If the user actually needs cross-user decryptability**: This is a different encryption model. Route to playbook `setup-forseti-e2ee`. It requires a Forseti contract, policy signing, and `IAMService.doEncrypt(data, signedPolicyBytes)`. Do not attempt this by renaming roles.
174
+
175
+ ### E2EE ciphertext is undefined or empty
176
+ 1. Check Fabric connectivity (SWE iframe loads, no CSP errors)
177
+ 2. Check browser console for ORK errors
178
+ 3. Ensure `doEncrypt` is called after user is authenticated
179
+
180
+ ### Policy commit fails with "Policy supplied has not been signed"
181
+
182
+ This is a policy-flow failure, not a role issue. See T-14.
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
186
+ 3. Check `addPolicy(adminPolicyBytes)` was called after approval, not during request construction
187
+ 4. Check URL construction has no double slashes (strip trailing slash from `auth-server-url`)
188
+
189
+ ---
190
+
191
+ ## Handoff Trace
192
+
193
+ ```
194
+ [TRACE]
195
+ Scenario: <scenario>
196
+ Role: IAM / Policy Engineer
197
+ Reason: <RBAC needed | self-encryption needed | shared encryption needed>
198
+ Preconditions: JWT verification exists (lib/auth/tideJWT.ts), sharing gate resolved
199
+ Next: Reviewer / QA Engineer | STOP
200
+ [/TRACE]
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Do Not Do This
206
+
207
+ - **Do not implement RBAC without prior JWT verification.** Role checks on unverified tokens are meaningless.
208
+ - **Do not use `_tide_*` roles for application permissions.** They are for E2EE voucher gating. Use separate realm or client roles for app-level access control.
209
+ - **Do not implement offline decryption** or cache decrypted session keys. (AP-04)
210
+ - **Do not use self-encryption for data that multiple users need to access.** Use policy-governed VVK encryption with a Forseti contract instead. (AP-24 in canon/anti-patterns.md)
211
+ - **Do not assume E2EE is available without IGA.** The `jwk` field (required for E2EE) is only in the adapter JSON when IGA is enabled.
212
+ - **Do not store decrypted data server-side.** The whole point of Tide E2EE is that plaintext exists only in the user's browser.
213
+ - **Do not rename `selfencrypt`/`selfdecrypt` to `encrypt`/`decrypt` to "fix" encryption failures or enable sharing.** The role suffix is a naming convention. The SDK call path determines the encryption model. See AP-26.
214
+ - **Do not treat policy commit failure as a role issue.** Missing admin policy on commit is a bootstrap/policy-flow failure. See T-14.
@@ -0,0 +1,133 @@
1
+ # Role: Reviewer / QA Engineer
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ Check implementation work against pack doctrine, security invariants, scenario correctness, and anti-patterns. The Reviewer is a compliance/sanity/security gate — not a second builder.
8
+
9
+ ---
10
+
11
+ ## Boundary
12
+
13
+ | This subagent owns | Does NOT own |
14
+ |-------------------|-------------|
15
+ | Compliance checking against canon/invariants | Writing new features |
16
+ | Scenario correctness verification | Performing setup or bootstrap |
17
+ | Security invariant validation | Fixing detected issues (hand back to the responsible subagent) |
18
+ | Anti-pattern detection | Broad refactoring or rewrites |
19
+ | Stale convention detection | Playbook execution |
20
+ | Accept / warn / reject decisions | Final user-facing output |
21
+
22
+ The Reviewer detects problems. It does not fix them. When it finds an issue, it reports the issue and names the subagent that should fix it.
23
+
24
+ ---
25
+
26
+ ## When to Trigger
27
+
28
+ - After a major build or scaffold pass (Setup + Integration + Protection)
29
+ - After auth/bootstrap setup completes
30
+ - After route/API protection work completes
31
+ - After RBAC/E2EE work completes
32
+ - Before final handoff to the user
33
+ - Before writing learnings back into the pack
34
+ - When the orchestrator wants a compliance gate before proceeding
35
+
36
+ The Reviewer is optional per-step but **mandatory before handing the app to the user**. No implementation is delivered without a review pass. If the Reviewer rejects, the named specialist fixes the issue, then the Reviewer runs again.
37
+
38
+ ---
39
+
40
+ ## When NOT to Trigger
41
+
42
+ - During initial planning or routing (that is the orchestrator's job)
43
+ - As a replacement for running a subagent (the Reviewer reviews, it does not build)
44
+ - On trivial single-file changes that don't touch auth, config, or security
45
+
46
+ ---
47
+
48
+ ## What the Reviewer Checks
49
+
50
+ ### Scenario correctness
51
+ - [ ] Was a scenario match attempted before building? (I-17)
52
+ - [ ] If a scenario matched, was its manifest used for roles/policies/playbooks?
53
+ - [ ] Was scenario ambiguity resolved before action, not after?
54
+ - [ ] If no scenario matched, was generic routing used correctly?
55
+
56
+ ### Setup/bootstrap compliance
57
+ - [ ] Was bootstrap completed before integration? (I-18 ordering)
58
+ - [ ] Does the realm template include all required sections? (tidecloak-bootstrap canon)
59
+ - [ ] Is `tidecloak.json` present with `jwk`, `vendorId`, `homeOrkUrl`?
60
+ - [ ] Are master admin credentials absent from generated app code? (AP-41)
61
+ - [ ] Was the init-script path used when available?
62
+
63
+ ### Integration compliance
64
+ - [ ] Is `useDPoP` inside the config object, not as a JSX prop? (AP-42, session-002)
65
+ - [ ] Does `next.config.ts` have `strictExportPresence = false`? (not `reexportExportsPresence`)
66
+ - [ ] Does `next.config.ts` have the `@tidecloak/react` ESM alias? (AP-42)
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)
69
+ - [ ] Does `public/silent-check-sso.html` exist? (I-07)
70
+ - [ ] Does the redirect handler exist at the configured `redirectUri`? (I-16)
71
+ - [ ] Is CSP set to `frame-src '*'`? (I-06)
72
+
73
+ ### Protection compliance
74
+ - [ ] Do protected APIs verify JWT server-side? (I-03)
75
+ - [ ] Is `createLocalJWKSet(config.jwk)` used, not `createRemoteJWKSet`? (I-04)
76
+ - [ ] Is DPoP enforced when `cnf.jkt` is present in the token?
77
+ - [ ] Are `hasRealmRole()` / `hasClientRole()` used only for UI gating, not API auth? (I-08)
78
+
79
+ ### RBAC/E2EE compliance
80
+ - [ ] If sharing was requested, was policy-governed encryption used (not self-encryption)? (AP-24)
81
+ - [ ] Were roles NOT renamed to enable sharing? (AP-26)
82
+ - [ ] Is `hasClientRole('tide-realm-admin', 'realm-management')` used for admin checks, not `hasRealmRole`? (AP-29)
83
+ - [ ] If shared encryption: does an admin signing page/flow exist? Is shared mode gated on signed policy?
84
+ - [ ] No silent fallback from shared to self-encryption?
85
+
86
+ ### Config artifact compliance
87
+ - [ ] Is the adapter file named `tidecloak.json` (not `adapter.json` or `keycloak.json`)?
88
+ - [ ] Is it at the correct path (`data/` for Next.js, `public/` for React/Vite)?
89
+ - [ ] Are no `NEXT_PUBLIC_TIDECLOAK_*` env vars created? (AP-38)
90
+ - [ ] Does the provider load config from `tidecloak.json`, not env vars?
91
+
92
+ ### Stale convention detection
93
+ - [ ] No `reexportExportsPresence` in webpack config (invalid property)
94
+ - [ ] No `require.resolve()` for `@tidecloak/react` alias (throws ERR_PACKAGE_PATH_NOT_EXPORTED)
95
+ - [ ] No `middleware.ts` on Next.js 16+ (should be `proxy.ts`)
96
+ - [ ] No `hasRole()` from the SDK hook (should be `hasRealmRole` / `hasClientRole`)
97
+ - [ ] No `tidecloak-oidc-keycloak-json` provider ID (does not exist)
98
+
99
+ ---
100
+
101
+ ## Conclusions
102
+
103
+ The Reviewer must conclude with one of:
104
+
105
+ | Conclusion | Meaning | Action |
106
+ |-----------|---------|--------|
107
+ | **ACCEPT** | All checks pass. No issues found. | Proceed to handoff. |
108
+ | **ACCEPT_WITH_WARNINGS** | Minor issues found that do not block functionality. | List warnings. Proceed with awareness. |
109
+ | **REJECT_AND_REROUTE** | Critical issue found. Name the subagent that must fix it. | Do not proceed. Route back to the named subagent. |
110
+
111
+ ---
112
+
113
+ ## Handoff Trace
114
+
115
+ The Reviewer emits a `[REVIEW]` block instead of a `[TRACE]`:
116
+ ```
117
+ [REVIEW]
118
+ Verdict: ACCEPT | ACCEPT_WITH_WARNINGS | REJECT_AND_REROUTE
119
+ Reason: <short reason>
120
+ Violations: <list or "none">
121
+ Next: <specialist to fix or "handoff to user">
122
+ [/REVIEW]
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Do Not Do This
128
+
129
+ - Do not fix issues yourself. Report them and name the responsible subagent.
130
+ - Do not become a second builder. Your job is to check, not to code.
131
+ - Do not perform broad rewrites. Flag specific violations.
132
+ - Do not promote app-specific workarounds into doctrine.
133
+ - Do not review areas outside your checklist unless specifically asked.