@tideorg/mcp 1.4.1 → 1.9.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 (91) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/GAP_REGISTER.md +19 -9
  3. package/PRIVACY.md +41 -0
  4. package/README.md +204 -120
  5. package/adapters/AGENTS.md +3 -2
  6. package/adapters/CLAUDE.md +1 -1
  7. package/adapters/replit.md +0 -0
  8. package/canon/anti-patterns.md +55 -62
  9. package/canon/concepts.md +46 -34
  10. package/canon/custom-contracts.md +12 -8
  11. package/canon/feature-mapping.md +27 -75
  12. package/canon/framework-matrix.md +69 -22
  13. package/canon/hosting-options.md +111 -0
  14. package/canon/iga-change-requests-api.md +211 -0
  15. package/canon/invariants.md +33 -35
  16. package/canon/redirect-handler.md +0 -0
  17. package/canon/security-gap-mapping.md +506 -0
  18. package/canon/security-runtime-probes.md +177 -0
  19. package/canon/tidecloak-bootstrap.md +84 -15
  20. package/canon/tidecloak-endpoints.md +60 -98
  21. package/canon/troubleshooting.md +201 -53
  22. package/canon/version-policy.md +8 -12
  23. package/mcp-server/dist/http.d.ts +2 -0
  24. package/mcp-server/dist/http.js +46 -0
  25. package/mcp-server/dist/index.d.ts +0 -0
  26. package/mcp-server/dist/index.js +2 -601
  27. package/mcp-server/dist/server.d.ts +2 -0
  28. package/mcp-server/dist/server.js +617 -0
  29. package/package.json +48 -45
  30. package/playbooks/add-auth-nextjs-existing.md +33 -17
  31. package/playbooks/add-auth-nextjs-fresh.md +1 -1
  32. package/playbooks/add-rbac-nextjs.md +7 -2
  33. package/playbooks/bootstrap-realm-from-template.md +33 -18
  34. package/playbooks/configure-e2ee-roles-and-policies.md +0 -0
  35. package/playbooks/deploy-tidecloak-docker.md +31 -28
  36. package/playbooks/diagnose-broken-login.md +0 -0
  37. package/playbooks/diagnose-missing-roles-or-claims.md +2 -2
  38. package/playbooks/initialize-admin-and-link-account.md +22 -19
  39. package/playbooks/migrate-from-existing-auth.md +0 -0
  40. package/playbooks/protect-api-nextjs.md +40 -1
  41. package/playbooks/protect-aspnet-core-asgard.md +313 -0
  42. package/playbooks/protect-routes-nextjs.md +24 -10
  43. package/playbooks/provision-tidecloak-skycloak.md +213 -0
  44. package/playbooks/setup-forseti-e2ee.md +32 -50
  45. package/playbooks/setup-iga-admin-panel.md +113 -171
  46. package/playbooks/start-tidecloak-dev.md +0 -0
  47. package/playbooks/verify-jwt-server-side.md +20 -1
  48. package/prompts/add-admin-approval-flow.md +0 -0
  49. package/prompts/build-private-customer-portal.md +1 -1
  50. package/prompts/migrate-generic-auth-to-tide.md +0 -0
  51. package/prompts/secure-existing-app.md +0 -0
  52. package/prompts/security-gap-analysis.md +55 -0
  53. package/reference-apps/INDEX.md +0 -0
  54. package/reference-apps/encrypted-communication/anti-patterns.md +3 -3
  55. package/reference-apps/encrypted-communication/bootstrap-sequence.md +2 -2
  56. package/reference-apps/encrypted-communication/manifest.yaml +0 -0
  57. package/reference-apps/encrypted-communication/role-policy-matrix.md +0 -0
  58. package/reference-apps/encrypted-communication/scenario.md +2 -2
  59. package/reference-apps/git-pr-signing-service/anti-patterns.md +0 -0
  60. package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +8 -8
  61. package/reference-apps/git-pr-signing-service/manifest.yaml +0 -0
  62. package/reference-apps/git-pr-signing-service/role-policy-matrix.md +0 -0
  63. package/reference-apps/git-pr-signing-service/scenario.md +0 -0
  64. package/reference-apps/iga-admin-governance/anti-patterns.md +18 -16
  65. package/reference-apps/iga-admin-governance/bootstrap-sequence.md +10 -5
  66. package/reference-apps/iga-admin-governance/manifest.yaml +0 -0
  67. package/reference-apps/iga-admin-governance/role-policy-matrix.md +12 -13
  68. package/reference-apps/iga-admin-governance/scenario.md +15 -13
  69. package/reference-apps/organisation-password-manager/anti-patterns.md +0 -0
  70. package/reference-apps/organisation-password-manager/bootstrap-sequence.md +5 -6
  71. package/reference-apps/organisation-password-manager/manifest.yaml +0 -0
  72. package/reference-apps/organisation-password-manager/role-policy-matrix.md +0 -0
  73. package/reference-apps/organisation-password-manager/scenario.md +0 -0
  74. package/reference-apps/policy-governed-signing/anti-patterns.md +0 -0
  75. package/reference-apps/policy-governed-signing/bootstrap-sequence.md +9 -9
  76. package/reference-apps/policy-governed-signing/manifest.yaml +0 -0
  77. package/reference-apps/policy-governed-signing/role-policy-matrix.md +0 -0
  78. package/reference-apps/policy-governed-signing/scenario.md +0 -0
  79. package/skills/tide-diagnostics/SKILL.md +1 -1
  80. package/skills/tide-integration/SKILL.md +9 -18
  81. package/skills/tide-learning-capture/SKILL.md +0 -0
  82. package/skills/tide-mcp-qa/SKILL.md +139 -0
  83. package/skills/tide-rbac-and-e2ee/SKILL.md +5 -5
  84. package/skills/tide-reviewer/SKILL.md +1 -1
  85. package/skills/tide-route-and-api-protection/SKILL.md +0 -0
  86. package/skills/tide-scenario-resolver/SKILL.md +0 -0
  87. package/skills/tide-security-analyst/SKILL.md +182 -0
  88. package/skills/tide-setup/SKILL.md +1 -1
  89. package/skills/tide-solutions-architect/SKILL.md +0 -0
  90. package/canon/delegation.md +0 -195
  91. package/playbooks/setup-server-delegation.md +0 -142
@@ -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)
File without changes
File without changes
@@ -0,0 +1,182 @@
1
+ # Role: Security Analyst (Gap Analysis)
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ Analyze an **existing** system — one that may have no Tide involvement at all — and produce an honest security gap analysis that maps concrete weaknesses to the Tide capabilities that remove them. This is the pack's entry point for "here is my current app; where am I exposed, and what would Tide change?"
8
+
9
+ This role is **read-only and advisory**. It inspects and reports. It does not install Tide, write app code, or run bootstrap. When the operator accepts a finding, it hands off to the execution roles (`tide-setup`, `tide-integration`, `tide-route-and-api-protection`, `tide-rbac-and-e2ee`) via the normal team model (I-18).
10
+
11
+ It is the inverse of the rest of the pack: the pack builds Tide apps; this role audits non-Tide apps and shows where Tide fits.
12
+
13
+ ## Two tiers: static, then runtime
14
+
15
+ The analysis runs in two tiers. Do the first always; do the second only with explicit authorization.
16
+
17
+ 1. **Static (always)** — inspect code, config, and schema. Produces *candidate* findings, mostly tagged INFERRED. This tier never touches the running system.
18
+ 2. **Runtime confirmation (opt-in, authorized)** — send a small number of non-destructive probes to the live target to confirm which candidates are real, upgrading INFERRED → VERIFIED (or dropping false positives). Governed entirely by `canon/security-runtime-probes.md`, including a hard authorization gate and non-exploitation limits. Do NOT run this tier unless the operator owns or is authorized to test the target and consents. When in doubt, stay static-only.
19
+
20
+ A static hypothesis and a runtime-confirmed finding are different strengths of evidence — never present the former as the latter.
21
+
22
+ ---
23
+
24
+ ## Boundary
25
+
26
+ | This role owns | Does NOT own |
27
+ |----------------|--------------|
28
+ | Inspecting an existing codebase / deployment | Writing or modifying app code |
29
+ | Identifying trust concentrations | Running TideCloak bootstrap |
30
+ | Mapping gaps to Tide capabilities (`canon/security-gap-mapping.md`) | Installing the Tide SDK |
31
+ | Severity triage and evidence collection | Fixing the gaps it finds |
32
+ | Producing the gap-analysis report | Compliance review of Tide-built code (→ `tide-reviewer`) |
33
+ | Naming the remediation playbook sequence | Executing the remediation |
34
+ | Honestly scoping what Tide does NOT fix | Overstating Tide's coverage to close a sale |
35
+
36
+ **Hard rule**: This role never claims a security property Tide provides without an evidence tag and a source. It never presents Tide as fixing a gap listed in the "Out of scope" table of `canon/security-gap-mapping.md`.
37
+
38
+ ---
39
+
40
+ ## When to Trigger
41
+
42
+ - "Do a security analysis of my app / system."
43
+ - "Where is my current auth weak?"
44
+ - "What would Tide change about my security posture?"
45
+ - "Audit this codebase for identity/key/authorization gaps."
46
+ - "We're evaluating Tide — show me the gaps it would close in what we have."
47
+ - Before a migration, to justify and scope the work (`migrate-from-existing-auth`).
48
+
49
+ ## When NOT to Trigger
50
+
51
+ - Building a new Tide app → `tide-setup` + team model (I-18).
52
+ - Reviewing already-written Tide code for compliance → `tide-reviewer`.
53
+ - Diagnosing a broken Tide login → `tide-diagnostics`.
54
+ - Full cryptographic red-team of a Tide construction → the internal `tide-crypto-redteam` skill (different surface: it attacks Tide's own crypto, this maps a customer's gaps).
55
+
56
+ ---
57
+
58
+ ## Core Doctrine
59
+
60
+ 1. **A gap is a named trust concentration, not a technology.** "Uses passwords" is not a finding. "Password verification collapses to trust in one database and one server process; a dump enables offline cracking of every account" is. Every finding must name the single party or artifact that, once compromised, defeats the control.
61
+
62
+ 2. **Evidence before claim.** Every finding cites a concrete artifact — file path + line, config value, schema column, or a runtime observation (an HTTP response). Tag each: **VERIFIED** (observed directly), **INFERRED** (strongly implied by observed code), **ASSUMED** (operator statement, unconfirmed). No evidence → not a finding, at most a question.
63
+
64
+ 3. **Honesty is the product.** The out-of-scope section is mandatory. Presenting Tide as fixing injection, XSS, or dependency CVEs destroys the credibility of the real findings. A visibly honest analysis is more persuasive than an inflated one. (AP-SEC-1)
65
+
66
+ 4. **Map to capability, not to slogans.** Each gap maps to a specific Tide mechanism with sourcing from `canon/feature-mapping.md` / `canon/invariants.md`, and to a real playbook in `playbooks/`. If no playbook exists, say so and point at `GAP_REGISTER.md` — do not improvise.
67
+
68
+ 5. **Severity is about blast radius under single compromise**, per the rubric in `canon/security-gap-mapping.md`. Ask: "If exactly one party here is compromised, what does the attacker get?"
69
+
70
+ 6. **Confirm the mode before claiming a cryptographic property.** IGA/QEA has two modes: Tide (`iga.attestor=tide`, licensed — cryptographic) and Tideless (`iga.attestor=simple`/unset, default — username attestation, server-enforced, no crypto). Findings SG-07/SG-14/SG-16 lean on IGA; their strong "no single bypass / tamper-evident" claims hold **only in Tide mode**. Never assert them for a Tideless realm. Same discipline for any capability whose guarantee is mode- or config-dependent — verify the target's actual configuration, don't assume the strongest variant. See the IGA-model note in `canon/security-gap-mapping.md`.
71
+
72
+ ---
73
+
74
+ ## Execution
75
+
76
+ ### Step 1 — Scope and consent
77
+ Confirm what you may inspect (repo, running instance, config) and that you are authorized to analyze it. State the boundary: read-only, no changes. If a live target is in scope, confirm it is the operator's own system or an authorized engagement.
78
+
79
+ ### Step 2 — Inventory the trust architecture
80
+ Before grepping for patterns, answer four questions from the code/config:
81
+ - **Identity**: How are users authenticated? Where do credentials live?
82
+ - **Authority**: How are tokens/sessions signed? Who holds the signing key?
83
+ - **Authorization**: Where is the access decision made — client, server, or both?
84
+ - **Data**: What sensitive data exists, and who can read it in the clear?
85
+
86
+ ### Step 3 — Run the static gap sweep
87
+ Load `canon/security-gap-mapping.md`. Work through SG-01 … SG-18. For each, run the detection commands against the target and record hits with evidence. Do NOT stop at the first finding — enumerate exhaustively (especially SG-05, where a sampled API audit gives false assurance).
88
+
89
+ Also collect anything matching the "Out of scope" table — you will report these separately.
90
+
91
+ ### Step 4 — Triage
92
+ For each candidate gap:
93
+ - Name the trust concentration.
94
+ - Assign severity (CRITICAL / HIGH / MEDIUM / INFO).
95
+ - Attach evidence with a confidence tag (mostly INFERRED at this point).
96
+ - Note gap interactions (e.g. SG-02 + SG-07: one admin who also controls the signing key is worse than either alone; SG-13 nullifies SG-02's protection entirely; SG-07 + SG-14: an admin who acts *and* erases the trail).
97
+
98
+ ### Step 4b — Runtime confirmation (opt-in, only if authorized)
99
+ If — and only if — the operator owns or is authorized to test a live target and consents, run the runtime tier per `canon/security-runtime-probes.md`. Confirm the authorization gate first. Send the small set of non-destructive probes for the candidate findings, upgrade confirmed ones to VERIFIED with a Runtime confirmation line, and downgrade/drop those a probe refutes. Never mutate data, never reach beyond the operator's own test account, never exploit an out-of-scope class to "confirm" it. If not authorized, skip this step and keep findings at their static confidence.
100
+
101
+ ### Step 5 — Map to Tide
102
+ For each in-scope gap, pull the Tide replacement and remediation path from `canon/security-gap-mapping.md`. Verify each named playbook exists (`tide_list playbooks`). Carry the **honesty note** for each — the limits of the Tide replacement are part of the finding, not an appendix.
103
+
104
+ ### Step 6 — Report
105
+ Emit the report in the format below. Lead with the trust-architecture summary and the highest-severity findings. Put the honest out-of-scope section in the body, not buried.
106
+
107
+ ### Step 7 — Hand off
108
+ Offer the remediation sequence. If the operator accepts, hand to the execution roles via I-18 (Scenario Resolver → Setup → Application → Security → IAM/Policy → Reviewer). Do not begin remediation from this role.
109
+
110
+ ---
111
+
112
+ ## Report Format
113
+
114
+ ```
115
+ # Tide Security Gap Analysis — <system name>
116
+ Scope: <what was inspected> | Authorization: <confirmed by whom>
117
+ Method: static (repo) | both (static + runtime) — list which SG findings were probed
118
+
119
+ ## Trust Architecture
120
+ - Identity: <where credentials live>
121
+ - Authority: <who holds signing keys>
122
+ - Authorization: <where the decision is made>
123
+ - Sensitive data: <what, readable by whom>
124
+
125
+ ## Findings (most severe first)
126
+ ### [SEVERITY] SG-XX — <title>
127
+ - Trust concentration: <the single party/artifact>
128
+ - Evidence: <file:line / config / HTTP obs> [VERIFIED|INFERRED|ASSUMED]
129
+ - Runtime confirmation: <exact request → observed response> [VERIFIED] (omit if static-only or not probed)
130
+ - Failure scenario: <what one compromise yields>
131
+ - Tide replacement: <mechanism> (source: canon/...) [sourcing tag]
132
+ - Remediation: <playbook sequence>
133
+ - Limits: <honesty note — what this does NOT cover>
134
+
135
+ ## Gap Interactions
136
+ <pairs that compound, e.g. SG-02 + SG-07>
137
+
138
+ ## Not Addressed by Tide (found in scope)
139
+ <injection / XSS / deps / rate-limiting / etc., with standard remediation pointers>
140
+ <or: "None observed in the inspected scope.">
141
+
142
+ ## Recommended Remediation Sequence
143
+ 1. <playbook> — closes <SG-XX>
144
+ 2. ...
145
+ Handoff: <execution role to engage first, per I-18>
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Verification
151
+
152
+ This analysis is complete only when:
153
+ - [ ] Every SG in `canon/security-gap-mapping.md` (SG-01 … SG-18) was checked (not sampled), or its non-applicability is stated.
154
+ - [ ] Every finding names a trust concentration and carries evidence with a confidence tag.
155
+ - [ ] Every Tide-replacement claim cites `canon/feature-mapping.md` or `canon/invariants.md`.
156
+ - [ ] Every remediation names a playbook that actually exists.
157
+ - [ ] The out-of-scope section is present and non-empty (or explicitly states none found).
158
+ - [ ] No finding claims Tide fixes an out-of-scope gap class.
159
+ - [ ] If the runtime tier ran: authorization was confirmed, every probe was non-destructive and confined to the operator's own account, and probed findings carry a Runtime confirmation line. Unprobed findings remain at their static confidence.
160
+
161
+ ## Anti-Patterns
162
+
163
+ - **AP-SEC-1** — Presenting Tide as fixing injection/XSS/CSRF/deps/rate-limiting. Kills credibility of the real findings.
164
+ - **AP-SEC-2** — A trust-concentration claim with no evidence artifact. Every finding needs a file, config value, or runtime observation.
165
+ - **AP-SEC-3** — Sampling APIs instead of enumerating them (SG-05). A "clean" sampled audit is worse than no audit — it manufactures false assurance.
166
+ - **AP-SEC-4** — Relocating a single point instead of removing it and calling it a fix. Moving a JWT secret from env to KMS is not what SG-02 asks for; threshold signing removes the whole-key.
167
+ - **AP-SEC-5** — Overstating severity to motivate migration. Report SG-10 (remote JWKS over TLS) as a hardening delta of the Tide model, not as a live vulnerability, unless TLS is also broken.
168
+ - **AP-SEC-6** — Beginning remediation from this role. Analyst reports; execution roles build. Do not blur the boundary.
169
+ - Runtime-tier anti-patterns (AP-SEC-7 … AP-SEC-10) live in `canon/security-runtime-probes.md`: no unauthorized probing, no mutating "checks", no escalation into exploitation, no runtime-level confidence on an unprobed hypothesis.
170
+
171
+ ## Handoff Trace
172
+
173
+ ```
174
+ [TRACE]
175
+ Scenario: security gap analysis of existing (non-Tide) system
176
+ Role: Security Analyst
177
+ Reason: <operator requested audit / migration scoping>
178
+ Preconditions: read-only scope confirmed, authorization confirmed
179
+ Findings: <N gaps, severities>
180
+ Next: <Setup / Scenario Resolver for remediation, or "report only — awaiting operator decision">
181
+ [/TRACE]
182
+ ```
@@ -33,7 +33,7 @@ If multiple paths are plausible and the repo does not resolve it, ask the user b
33
33
  ## When NOT to Trigger
34
34
 
35
35
  - App already has working Tide auth (provider present, adapter JSON loaded, login functional). Proceed to route-and-api-protection or rbac-and-e2ee skill instead.
36
- - User is asking about migrating from Keycloak. Migration is not yet documented (GAP-023). Say so and stop.
36
+ - User is asking about a **direct Keycloak-to-TideCloak** migration. That specific path is not yet documented (GAP-023) say so and stop. (Retrofitting an app off generic-OIDC / NextAuth / Clerk is a different task, covered by `playbooks/migrate-from-existing-auth.md`; the open gap is the KC→TideCloak realm/data migration itself.)
37
37
 
38
38
  ---
39
39
 
File without changes
@@ -1,195 +0,0 @@
1
- # Server-Side Delegation - Canon
2
-
3
- Canonical reference for server-side admin API calls via delegation tokens.
4
-
5
- When your app server needs to call TideCloak admin APIs (manage users, roles, policies), it cannot use the browser's DPoP-bound access token. The delegation flow issues a new token bound to the server's ephemeral key.
6
-
7
- ---
8
-
9
- ## Packages
10
-
11
- | Package | Side | Purpose |
12
- |---------|------|---------|
13
- | `@tidecloak/server` | Server | `TideDelegation` class, middleware, DPoP proof generation |
14
- | `@tidecloak/js` | Browser | `createTideFetch` wrapper, `signDelegationRequest`, `signDpopApproval` |
15
-
16
- ---
17
-
18
- ## Flow: Forgetful Interrupt Pattern
19
-
20
- 1. Browser calls server endpoint (e.g., `GET /api/admin/users`)
21
- 2. Server `requireDelegation()` middleware checks cache. No cached token.
22
- 3. Server generates ephemeral Ed25519 key, responds **419** with challenge
23
- 4. `createTideFetch` intercepts 419, calls `IAMService.signDelegationRequest(payload)` and `IAMService.signDpopApproval(serverJkt)`
24
- 5. Browser POSTs signed artifacts to `/api/delegation`
25
- 6. Server `handleDelegation()` exchanges with TideCloak token endpoint
26
- 7. TideCloak validates chain of trust, signs delegation token via ORK network
27
- 8. Server caches delegation token, responds `{ delegated: true }`
28
- 9. `createTideFetch` retries original request. Server finds cached token, proceeds.
29
-
30
- Subsequent requests use the cached token until it expires.
31
-
32
- ---
33
-
34
- ## Setup
35
-
36
- ### Server
37
-
38
- ```js
39
- import { TideDelegation } from '@tidecloak/server';
40
-
41
- const delegation = new TideDelegation({
42
- tidecloakUrl: process.env.TIDECLOAK_URL,
43
- realm: process.env.TIDECLOAK_REALM,
44
- clientId: process.env.TIDECLOAK_CLIENT_ID,
45
- });
46
-
47
- // Exchange endpoint
48
- app.post('/api/delegation', authenticate, delegation.handleDelegation());
49
-
50
- // Protected route with delegation
51
- app.get('/api/admin/users',
52
- authenticate,
53
- delegation.requireDelegation(),
54
- async (req, res) => {
55
- const users = await req.delegation.fetch(adminUrl + '/users');
56
- res.json(users);
57
- }
58
- );
59
- ```
60
-
61
- ### Browser
62
-
63
- ```js
64
- import { createTideFetch } from '@tidecloak/js';
65
-
66
- const appFetch = createTideFetch(window.fetch);
67
-
68
- // Delegation is transparent
69
- const users = await appFetch('/api/admin/users');
70
- ```
71
-
72
- ---
73
-
74
- ## Scoped Delegation
75
-
76
- Limit roles in the delegation token. If `roles` is specified, only listed roles/clients appear. Everything else is stripped.
77
-
78
- ```js
79
- // Only read role for my-app client
80
- delegation.requireDelegation({
81
- roles: {
82
- clients: { 'my-app': ['read'] }
83
- }
84
- })
85
-
86
- // Realm + multiple clients
87
- delegation.requireDelegation({
88
- roles: {
89
- realm: ['admin'],
90
- clients: {
91
- 'my-app': ['read', 'write'],
92
- 'other-app': ['view']
93
- }
94
- }
95
- })
96
-
97
- // No filtering (default)
98
- delegation.requireDelegation()
99
- ```
100
-
101
- Rules:
102
- - Omitting `roles` entirely keeps all user roles
103
- - Once `roles` is specified, unlisted realms/clients are stripped
104
- - Cannot escalate. Requesting a role the user does not have silently drops it.
105
-
106
- ---
107
-
108
- ## Token Exchange Endpoint
109
-
110
- The server exchanges artifacts at TideCloak's standard OIDC token endpoint:
111
-
112
- ```
113
- POST /realms/{realm}/protocol/openid-connect/token
114
- ```
115
-
116
- Parameters (form-encoded):
117
- - `grant_type`: `urn:ietf:params:oauth:grant-type:token-exchange`
118
- - `client_id`: app client ID
119
- - `subject_token`: user's access token
120
- - `subject_token_type`: `urn:ietf:params:oauth:token-type:access_token`
121
- - `actor_token`: browser-signed delegation request JWT
122
- - `actor_token_type`: `urn:ietf:params:oauth:token-type:jwt`
123
- - `dpop_approval`: session key approval of server's DPoP key
124
-
125
- Headers:
126
- - `DPoP`: server's DPoP proof JWT (Ed25519, contains server's public key)
127
-
128
- Response: `{ access_token, token_type: "DPoP", expires_in, issued_token_type }`
129
-
130
- ---
131
-
132
- ## Chain of Trust
133
-
134
- Two-hop validation ensures the delegation token is legitimately authorized:
135
-
136
- **HOP 1**: `subject_token.cnf.jkt == thumbprint(BROWSER_KEY from actor_token header)`
137
- The browser that owns the access token is the same one that signed the delegation request.
138
-
139
- **HOP 2**: `actor_token.cnf.jkt == thumbprint(SERVER_KEY from DPoP header)`
140
- The delegation request authorizes the server's specific ephemeral key.
141
-
142
- Validated at two layers:
143
- 1. TideCloak authenticator (Java) - before token is built
144
- 2. ORK signing model (C#) - before token is threshold-signed
145
-
146
- ---
147
-
148
- ## Using req.delegation.fetch()
149
-
150
- The `fetch` helper on `req.delegation` handles DPoP proof generation and Authorization headers:
151
-
152
- ```js
153
- // GET
154
- const users = await req.delegation.fetch(url);
155
-
156
- // POST with JSON
157
- await req.delegation.fetch(url, { method: 'POST', body: { name: 'role' } });
158
-
159
- // PUT
160
- await req.delegation.fetch(url, { method: 'PUT', body: { enabled: true } });
161
-
162
- // DELETE
163
- await req.delegation.fetch(url, { method: 'DELETE' });
164
- ```
165
-
166
- ---
167
-
168
- ## Anti-Patterns
169
-
170
- - **Do not use master admin credentials** for server-side admin calls. Use delegation.
171
- - **Do not forward the user's browser token** to admin API. It is DPoP-bound to the browser's key.
172
- - **Do not skip DPoP on delegation requests**. The delegation token is DPoP-bound to the server's ephemeral key. Every admin API call needs a fresh DPoP proof with `ath` claim.
173
- - **Do not share delegation tokens across users**. They are cached per session ID.
174
- - **Do not hardcode server keys**. They are ephemeral Ed25519 keys, generated per session.
175
-
176
- ---
177
-
178
- ## Prerequisites
179
-
180
- - TideCloak with `tide-chain-of-trust` client authenticator enabled (added by default in recent TideCloak builds)
181
- - `DelegationToken:1` in the VRK model whitelist (requires VRK rotation after adding)
182
- - DPoP enabled on the client
183
- - `@tidecloak/server` installed on the backend
184
- - `createTideFetch` wrapping fetch on the frontend
185
- - An `authenticate` middleware that sets `req.accessToken` from the Authorization header
186
-
187
- ---
188
-
189
- ## Invariants
190
-
191
- - Delegation tokens are short-lived (max 600s / 10 minutes)
192
- - Server key is ephemeral per session, cleared when delegation cache expires
193
- - The browser must approve each server key via ORK enclave (DPoP approval)
194
- - Scoped delegation can only reduce roles, never add them
195
- - Delegation tokens have `act: { sub: clientId }` per RFC 8693