@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.
- package/GAP_REGISTER.md +117 -0
- package/README.md +38 -0
- package/adapters/AGENTS.md +241 -0
- package/adapters/CLAUDE.md +146 -0
- package/adapters/replit.md +224 -0
- package/canon/anti-patterns.md +2133 -0
- package/canon/concepts.md +816 -0
- package/canon/custom-contracts.md +533 -0
- package/canon/delegation.md +195 -0
- package/canon/feature-mapping.md +637 -0
- package/canon/framework-matrix.md +1125 -0
- package/canon/invariants.md +851 -0
- package/canon/redirect-handler.md +254 -0
- package/canon/tidecloak-bootstrap.md +287 -0
- package/canon/tidecloak-endpoints.md +294 -0
- package/canon/troubleshooting.md +1494 -0
- package/canon/version-policy.md +89 -0
- package/mcp-server/dist/index.d.ts +2 -0
- package/mcp-server/dist/index.js +605 -0
- package/package.json +45 -0
- package/playbooks/add-auth-nextjs-existing.md +799 -0
- package/playbooks/add-auth-nextjs-fresh.md +654 -0
- package/playbooks/add-rbac-nextjs.md +190 -0
- package/playbooks/bootstrap-realm-from-template.md +170 -0
- package/playbooks/configure-e2ee-roles-and-policies.md +196 -0
- package/playbooks/deploy-tidecloak-docker.md +792 -0
- package/playbooks/diagnose-broken-login.md +234 -0
- package/playbooks/diagnose-missing-roles-or-claims.md +253 -0
- package/playbooks/initialize-admin-and-link-account.md +235 -0
- package/playbooks/migrate-from-existing-auth.md +198 -0
- package/playbooks/protect-api-nextjs.md +451 -0
- package/playbooks/protect-routes-nextjs.md +544 -0
- package/playbooks/setup-forseti-e2ee.md +756 -0
- package/playbooks/setup-iga-admin-panel.md +344 -0
- package/playbooks/setup-server-delegation.md +142 -0
- package/playbooks/start-tidecloak-dev.md +130 -0
- package/playbooks/verify-jwt-server-side.md +439 -0
- package/prompts/add-admin-approval-flow.md +50 -0
- package/prompts/build-private-customer-portal.md +85 -0
- package/prompts/migrate-generic-auth-to-tide.md +67 -0
- package/prompts/secure-existing-app.md +80 -0
- package/reference-apps/INDEX.md +87 -0
- package/reference-apps/encrypted-communication/anti-patterns.md +123 -0
- package/reference-apps/encrypted-communication/bootstrap-sequence.md +152 -0
- package/reference-apps/encrypted-communication/manifest.yaml +100 -0
- package/reference-apps/encrypted-communication/role-policy-matrix.md +80 -0
- package/reference-apps/encrypted-communication/scenario.md +134 -0
- package/reference-apps/git-pr-signing-service/anti-patterns.md +61 -0
- package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +157 -0
- package/reference-apps/git-pr-signing-service/manifest.yaml +80 -0
- package/reference-apps/git-pr-signing-service/role-policy-matrix.md +99 -0
- package/reference-apps/git-pr-signing-service/scenario.md +169 -0
- package/reference-apps/iga-admin-governance/anti-patterns.md +133 -0
- package/reference-apps/iga-admin-governance/bootstrap-sequence.md +153 -0
- package/reference-apps/iga-admin-governance/manifest.yaml +87 -0
- package/reference-apps/iga-admin-governance/role-policy-matrix.md +67 -0
- package/reference-apps/iga-admin-governance/scenario.md +126 -0
- package/reference-apps/organisation-password-manager/anti-patterns.md +71 -0
- package/reference-apps/organisation-password-manager/bootstrap-sequence.md +127 -0
- package/reference-apps/organisation-password-manager/manifest.yaml +86 -0
- package/reference-apps/organisation-password-manager/role-policy-matrix.md +59 -0
- package/reference-apps/organisation-password-manager/scenario.md +107 -0
- package/reference-apps/policy-governed-signing/anti-patterns.md +67 -0
- package/reference-apps/policy-governed-signing/bootstrap-sequence.md +128 -0
- package/reference-apps/policy-governed-signing/manifest.yaml +78 -0
- package/reference-apps/policy-governed-signing/role-policy-matrix.md +62 -0
- package/reference-apps/policy-governed-signing/scenario.md +113 -0
- package/skills/tide-diagnostics/SKILL.md +99 -0
- package/skills/tide-integration/SKILL.md +136 -0
- package/skills/tide-learning-capture/SKILL.md +174 -0
- package/skills/tide-rbac-and-e2ee/SKILL.md +214 -0
- package/skills/tide-reviewer/SKILL.md +133 -0
- package/skills/tide-route-and-api-protection/SKILL.md +201 -0
- package/skills/tide-scenario-resolver/SKILL.md +81 -0
- package/skills/tide-setup/SKILL.md +218 -0
- 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.
|