@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,344 @@
1
+ # Set Up IGA Admin Panel
2
+
3
+ Build an admin UI for managing IGA change requests with quorum governance.
4
+
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - Building an admin dashboard that lists, approves, rejects, and commits IGA change requests
10
+ - Need to integrate the TideCloak change-request lifecycle into a custom UI
11
+ - Managing role assignments, group changes, or policy updates through quorum-governed workflows
12
+
13
+ **Do not use** if you only need end-user authentication. See [add-auth-nextjs-fresh.md](add-auth-nextjs-fresh.md) instead.
14
+
15
+ ---
16
+
17
+ ## Prerequisites
18
+
19
+ - TideCloak instance running and accessible
20
+ - TideCloak realm created with IGA enabled
21
+ - Admin user with sufficient privileges (realm-admin or equivalent)
22
+ - Admin bearer token obtainable (via admin-cli client or service account)
23
+ - Familiarity with the Tide enclave signing flow (required for multi-admin mode)
24
+
25
+ ---
26
+
27
+ ## API Base
28
+
29
+ All endpoints below are relative to:
30
+
31
+ ```
32
+ {tidecloak-url}/admin/realms/{realm}
33
+ ```
34
+
35
+ Example: `http://localhost:8080/admin/realms/myrealm`
36
+
37
+ ---
38
+
39
+ ## Change Request Lifecycle
40
+
41
+ ```
42
+ 1. Admin creates change (role assignment, group change, etc.)
43
+ -> Draft created automatically (DRAFT/PENDING)
44
+ 2. Admin reviews: GET /tide-admin/change-set/all/requests
45
+ 3. Admin approves: POST /tide-admin/change-set/sign/batch
46
+ -> If Tide multi-admin: returns signing challenge for enclave
47
+ -> Submit signed result: POST /tideAdminResources/add-review
48
+ 4. Once enough approvals: POST /tide-admin/change-set/commit/batch
49
+ 5. Or cancel: POST /tide-admin/change-set/cancel/batch
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Endpoints
55
+
56
+ ### Counts (Lightweight)
57
+
58
+ ```
59
+ GET /tide-admin/change-set/counts
60
+ ```
61
+
62
+ Use this for dashboard badges and summary views. Avoids fetching full request payloads.
63
+
64
+ ### List Requests
65
+
66
+ ```
67
+ GET /tide-admin/change-set/all/requests # All change requests (single call)
68
+ GET /tide-admin/change-set/users/requests # User changes only
69
+ GET /tide-admin/change-set/roles/requests # Role changes only
70
+ GET /tide-admin/change-set/clients/requests # Client changes only
71
+ GET /tide-admin/change-set/groups/requests # Group changes only
72
+ ```
73
+
74
+ ### Sign / Approve
75
+
76
+ ```
77
+ POST /tide-admin/change-set/sign/batch # Approve (sign) changes
78
+ ```
79
+
80
+ ### Commit / Cancel
81
+
82
+ ```
83
+ POST /tide-admin/change-set/commit/batch # Commit approved changes
84
+ POST /tide-admin/change-set/cancel/batch # Cancel pending changes
85
+ ```
86
+
87
+ ### Enclave Review and Rejection
88
+
89
+ ```
90
+ POST /tideAdminResources/add-review # Submit Tide enclave signed review (multipart/form-data)
91
+ POST /tideAdminResources/add-rejection # Reject a change (multipart/form-data)
92
+ ```
93
+
94
+ **Content-type warning**: Both `add-review` and `add-rejection` use `multipart/form-data`, NOT JSON. All other mutation endpoints (`sign/batch`, `commit/batch`, `cancel/batch`) use `application/json`. If you send JSON to `add-rejection`, the server silently ignores the payload.
95
+
96
+ ```typescript
97
+ // Rejection example — must use FormData, not JSON
98
+ const formData = new FormData();
99
+ formData.append("changeSetId", draftRecordId);
100
+ formData.append("actionType", actionType);
101
+ formData.append("changeSetType", changeSetType);
102
+
103
+ await fetch(`${TC_URL}/admin/realms/${realm}/tideAdminResources/add-rejection`, {
104
+ method: 'POST',
105
+ headers: { 'Authorization': `Bearer ${adminToken}` },
106
+ body: formData // NOT JSON.stringify
107
+ });
108
+ ```
109
+
110
+ ### Activity and Comments
111
+
112
+ ```
113
+ GET /tide-admin/change-set/{id}/activity # Activity log + comments
114
+ POST /tide-admin/change-set/{id}/comments # Add comment to change request
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Approve (Sign) Request -- Code Example
120
+
121
+ ```typescript
122
+ const response = await fetch(`${TC_URL}/admin/realms/${realm}/tide-admin/change-set/sign/batch`, {
123
+ method: 'POST',
124
+ headers: {
125
+ 'Authorization': `Bearer ${adminToken}`,
126
+ 'Content-Type': 'application/json'
127
+ },
128
+ body: JSON.stringify({
129
+ changeSets: [{
130
+ changeSetId: "draft-record-id",
131
+ changeSetType: "USER_ROLE",
132
+ actionType: "CREATE",
133
+ policyRoleId: "optional-role-uuid", // omit to use tide-realm-admin policy
134
+ dynamicData: ["optional-base64"] // custom Forseti contract input
135
+ }]
136
+ })
137
+ });
138
+
139
+ // If Tide multi-admin mode:
140
+ // response[0].requiresApprovalPopup === true
141
+ // -> decode changeSetDraftRequests (base64)
142
+ // -> present to Tide enclave for admin signing
143
+ // -> submit via POST /tideAdminResources/add-review
144
+ ```
145
+
146
+ ---
147
+
148
+ ## TypeScript Client (KcAdminClient) Usage
149
+
150
+ ```typescript
151
+ import KcAdminClient from "@keycloak/keycloak-admin-client";
152
+
153
+ const client = new KcAdminClient({ baseUrl: "http://localhost:8080" });
154
+ await client.auth({
155
+ username: "admin",
156
+ password: "admin",
157
+ grantType: "password",
158
+ clientId: "admin-cli"
159
+ });
160
+
161
+ // Counts
162
+ const counts = await client.tideUsersExt.getChangeSetCounts();
163
+
164
+ // All requests
165
+ const all = await client.tideUsersExt.getAllChangeSetRequests();
166
+
167
+ // Approve
168
+ await client.tideUsersExt.approveDraftChangeSet({
169
+ changeSets: [{ changeSetId: "id", changeSetType: "USER_ROLE", actionType: "CREATE" }]
170
+ });
171
+
172
+ // Commit
173
+ await client.tideUsersExt.commitDraftChangeSet({
174
+ changeSets: [{ changeSetId: "id", changeSetType: "USER_ROLE", actionType: "CREATE" }]
175
+ });
176
+
177
+ // Activity and comments
178
+ const activity = await client.tideUsersExt.getChangeSetActivity({ id: "draft-id" });
179
+ await client.tideUsersExt.addChangeSetComment({ id: "draft-id", comment: "LGTM" });
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Policy Management Endpoints
185
+
186
+ ```
187
+ PUT /tide-admin/forseti-contracts # Create/update Forseti contract
188
+ GET /tide-admin/forseti-contracts # List contracts
189
+ POST /tide-admin/policy-templates # Create policy template
190
+ GET /tide-admin/policy-templates # List templates
191
+ PUT /tide-admin/ssh-policies # Attach policy to role
192
+ GET /tide-admin/ssh-policies # List role policies
193
+ POST /tide-iga-provider/role-policy/{roleId}/init-cert # Store signed policy on role (JSON: initCert required, initCertSig optional; no doken field)
194
+ GET /tide-admin/role-policies # Roles with policy status
195
+ ```
196
+
197
+ ---
198
+
199
+ ## ChangeSetType Values
200
+
201
+ **VERIFIED** (vendor confirmation, GAP-041 resolved). Full enum (14 values):
202
+
203
+ | Value | Description | Draft Status |
204
+ |-------|-------------|-------------|
205
+ | `DEFAULT_ROLES` | Default role assignment | ACTIVE (auto) |
206
+ | `USER` | User creation | DRAFT |
207
+ | `USER_ROLE` | User-to-role assignment | DRAFT |
208
+ | `USER_GROUP_MEMBERSHIP` | User-to-group membership | DRAFT |
209
+ | `ROLE` | Role definition change | ACTIVE (auto, creation) / DRAFT (deletion if users assigned) |
210
+ | `COMPOSITE_ROLE` | Composite role change | DRAFT |
211
+ | `CLIENT` | Client configuration change | DRAFT (when users exist) |
212
+ | `CLIENT_DEFAULT_USER_CONTEXT` | Client scope mappings | DRAFT |
213
+ | `CLIENT_FULLSCOPE` | Client full-scope toggle | DRAFT |
214
+ | `GROUP` | Group definition change | — |
215
+ | `GROUP_ROLE` | Group-to-role assignment | DRAFT |
216
+ | `GROUP_MOVE` | Group hierarchy change | DRAFT |
217
+ | `REALM_LICENSING` | Realm license change | DRAFT |
218
+ | `POLICY` | Policy change (derived from USER_ROLE) | DRAFT |
219
+
220
+ **ACTIVE vs DRAFT**: Role creation and default-role assignment get ACTIVE status (recorded for audit but non-blocking — no sign/commit needed). All other actions get DRAFT status requiring quorum sign/commit. Master realm and IGA-disabled realms are always exempt.
221
+
222
+ ## Status Values
223
+
224
+ | Value | Meaning |
225
+ |-------|---------|
226
+ | `DRAFT` | Just created, not yet submitted for review |
227
+ | `PENDING` | Awaiting approvals |
228
+ | `APPROVED` | Enough approvals received, ready to commit |
229
+ | `DENIED` | Rejected by reviewer |
230
+ | `ACTIVE` | Committed and applied |
231
+
232
+ ---
233
+
234
+ ## Admin Panel Requirements
235
+
236
+ A working IGA admin panel must include:
237
+
238
+ 1. **Pending list** -- Display all change requests in `DRAFT` or `PENDING` status. Use `GET /tide-admin/change-set/all/requests` and filter by status.
239
+
240
+ 2. **Approve button** -- Triggers `POST /tide-admin/change-set/sign/batch` with selected change set IDs.
241
+
242
+ 3. **Enclave popup** -- When `requiresApprovalPopup === true` in the sign response, decode `changeSetDraftRequests` (base64) and present the Tide enclave iframe for the admin to complete threshold signing. Submit the signed result via `POST /tideAdminResources/add-review`.
243
+
244
+ 4. **Batch support** -- The sign, commit, and cancel endpoints accept arrays. The UI should support selecting multiple change requests and acting on them in a single call.
245
+
246
+ 5. **Role check** -- Only users with admin roles should see the panel. Verify the admin token includes the required realm-admin or delegated role before rendering the UI. This is UI gating only; the server enforces authorization on every API call.
247
+
248
+ ---
249
+
250
+ ## Verification Checklist
251
+
252
+ ### Lifecycle
253
+
254
+ - [ ] Creating a role assignment produces a DRAFT change request visible via `GET .../all/requests`
255
+ - [ ] Signing a change request transitions status from DRAFT/PENDING toward APPROVED
256
+ - [ ] In multi-admin mode, signing returns `requiresApprovalPopup === true` and enclave flow completes
257
+ - [ ] Committing an APPROVED change request applies the change and sets status to ACTIVE
258
+ - [ ] Cancelling a PENDING change request removes it from the pending list
259
+
260
+ ### Endpoints
261
+
262
+ - [ ] `GET .../counts` returns correct totals matching the full request list
263
+ - [ ] `GET .../all/requests` returns all change types (user, role, client, group)
264
+ - [ ] `POST .../sign/batch` accepts an array and processes all items
265
+ - [ ] `POST .../commit/batch` and `POST .../cancel/batch` work with arrays
266
+ - [ ] `POST /tideAdminResources/add-review` accepts the enclave-signed payload
267
+ - [ ] `POST /tideAdminResources/add-rejection` rejects and sets status to DENIED
268
+ - [ ] `GET .../{id}/activity` returns audit trail with timestamps
269
+ - [ ] `POST .../{id}/comments` persists and appears in activity
270
+
271
+ ### Authorization
272
+
273
+ - [ ] Unauthenticated requests return 401
274
+ - [ ] Non-admin tokens return 403
275
+ - [ ] Admin tokens with correct roles return 200
276
+
277
+ ---
278
+
279
+ ## Common Failures
280
+
281
+ ### Sign Response Ignored in Multi-Admin Mode
282
+
283
+ **Symptom**: Approve button appears to succeed but change request stays in PENDING.
284
+
285
+ **Cause**: The `requiresApprovalPopup` flag was not checked. In multi-admin mode, signing returns a challenge that must be completed through the Tide enclave.
286
+
287
+ **Fix**: After calling `sign/batch`, check `response[0].requiresApprovalPopup`. If `true`, decode `changeSetDraftRequests`, present the enclave iframe, and submit the signed result via `POST /tideAdminResources/add-review`.
288
+
289
+ ---
290
+
291
+ ### Committing Before Enough Approvals
292
+
293
+ **Symptom**: `POST .../commit/batch` returns an error or silently fails.
294
+
295
+ **Cause**: The change request has not reached APPROVED status. Quorum threshold not met.
296
+
297
+ **Fix**: Check the status before committing. Only commit change requests with status `APPROVED`. If still `PENDING`, more admin approvals are needed.
298
+
299
+ ---
300
+
301
+ ### Wrong ChangeSetType in Payload
302
+
303
+ **Symptom**: 400 Bad Request or unexpected behavior when signing/committing.
304
+
305
+ **Cause**: The `changeSetType` in the request body does not match the actual type of the draft record.
306
+
307
+ **Fix**: Read the `changeSetType` from the `GET .../all/requests` response and pass it back exactly. Do not hardcode a type.
308
+
309
+ ---
310
+
311
+ ### Missing Authorization Header
312
+
313
+ **Symptom**: 401 on all admin API calls.
314
+
315
+ **Cause**: Admin token not included or expired.
316
+
317
+ **Fix**: Obtain a fresh admin token before each batch of calls. If using `KcAdminClient`, call `client.auth(...)` and handle token refresh.
318
+
319
+ ---
320
+
321
+ ### Rejection Silently Fails
322
+
323
+ **Symptom**: Clicking "Reject" appears to succeed, but the change request stays in PENDING. No error returned.
324
+
325
+ **Cause**: The rejection endpoint (`POST /tideAdminResources/add-rejection`) expects `multipart/form-data`. If you send `Content-Type: application/json`, the server accepts the request but ignores the payload fields.
326
+
327
+ **Fix**: Use `FormData`, not `JSON.stringify`. Do not set `Content-Type` manually — the browser sets the correct multipart boundary automatically. See the code example in the Endpoints section above.
328
+
329
+ ---
330
+
331
+ ### Counts Out of Sync with List
332
+
333
+ **Symptom**: Dashboard badge shows a different number than the actual list.
334
+
335
+ **Cause**: `GET .../counts` and `GET .../all/requests` called at different times; a change was created or committed between calls.
336
+
337
+ **Fix**: Refresh counts after any mutation (sign, commit, cancel). Do not cache counts across operations.
338
+
339
+ ---
340
+
341
+ ## References
342
+
343
+ - Source: `sources/docs/CHANGE_REQUEST_API.md` (normative), operational exemplars (keylessh, forseti-crypto-quickstart, tidecloak-test-cases)
344
+ - Content-type asymmetry confirmed by: CHANGE_REQUEST_API.md, forseti-crypto-quickstart `tidecloakApi.ts` (uses FormData for `add-review`)
@@ -0,0 +1,142 @@
1
+ # Setup Server-Side Delegation
2
+
3
+ For apps where the backend needs to call TideCloak admin APIs on behalf of authenticated users.
4
+
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - Your server manages users, roles, or policies via TideCloak admin API
10
+ - You need server-side role management or access approvals
11
+ - You want to avoid using master admin credentials from your server
12
+
13
+ ---
14
+
15
+ ## Prerequisites
16
+
17
+ - TideCloak bootstrapped and running
18
+ - `tidecloak.json` exported with `jwk`, `vendorId`, `homeOrkUrl`
19
+ - App has working Tide login (provider wired, DPoP enabled)
20
+ - Node.js 18+ on the server (Ed25519 support)
21
+
22
+ ---
23
+
24
+ ## Steps
25
+
26
+ ### 1. Install server SDK
27
+
28
+ ```bash
29
+ npm install @tidecloak/server
30
+ ```
31
+
32
+ ### 2. Create delegation instance
33
+
34
+ ```ts
35
+ // server/lib/delegation.ts
36
+ import { TideDelegation } from '@tidecloak/server';
37
+
38
+ let _delegation: TideDelegation | null = null;
39
+
40
+ export function getDelegation(): TideDelegation {
41
+ if (!_delegation) {
42
+ _delegation = new TideDelegation({
43
+ tidecloakUrl: process.env.TIDECLOAK_URL || config['auth-server-url'],
44
+ realm: process.env.TIDECLOAK_REALM || config.realm,
45
+ clientId: process.env.TIDECLOAK_CLIENT_ID || config.resource,
46
+ });
47
+ }
48
+ return _delegation;
49
+ }
50
+ ```
51
+
52
+ Use a lazy singleton. Do not create a new instance per request.
53
+
54
+ ### 3. Wire delegation exchange endpoint
55
+
56
+ ```ts
57
+ app.post('/api/delegation', authenticate, getDelegation().handleDelegation());
58
+ ```
59
+
60
+ This endpoint receives signed delegation artifacts from the browser and exchanges them with TideCloak for a delegation token.
61
+
62
+ ### 4. Add delegation middleware to admin routes
63
+
64
+ ```ts
65
+ app.get('/api/admin/users',
66
+ authenticate,
67
+ requireAdmin,
68
+ getDelegation().requireDelegation(),
69
+ async (req, res) => {
70
+ const adminUrl = `${tidecloakUrl}/admin/realms/${realm}`;
71
+ const users = await req.delegation.fetch(`${adminUrl}/users`);
72
+ res.json(users);
73
+ }
74
+ );
75
+ ```
76
+
77
+ The middleware:
78
+ - Checks if a cached delegation token exists for this user session
79
+ - If yes: attaches `req.delegation` with `token` and `fetch()` helper
80
+ - If no: responds 419 with a delegation challenge
81
+
82
+ ### 5. Wrap browser fetch with createTideFetch
83
+
84
+ ```ts
85
+ import { createTideFetch } from '@tidecloak/js';
86
+
87
+ // Wrap the app's fetch function
88
+ const appFetch = createTideFetch(window.fetch);
89
+
90
+ // Use appFetch for all server calls that may need delegation
91
+ const users = await appFetch('/api/admin/users');
92
+ ```
93
+
94
+ `createTideFetch` intercepts 419 responses, handles the delegation signing flow with the browser's DPoP key, and retries the original request.
95
+
96
+ ### 6. Optional: scope delegation roles
97
+
98
+ ```ts
99
+ // Only include specific roles in the delegation token
100
+ getDelegation().requireDelegation({
101
+ roles: {
102
+ realm: ['admin'],
103
+ clients: { 'my-app': ['manage-users'] }
104
+ }
105
+ })
106
+ ```
107
+
108
+ If `roles` is specified, only listed roles appear in the delegation token. Unlisted realms/clients are stripped. Cannot add roles the user does not have.
109
+
110
+ ---
111
+
112
+ ## Verification
113
+
114
+ - [ ] `@tidecloak/server` in server package.json
115
+ - [ ] `/api/delegation` POST route wired with `handleDelegation()`
116
+ - [ ] Admin routes use `requireDelegation()` middleware
117
+ - [ ] Browser uses `createTideFetch` wrapper
118
+ - [ ] Admin API calls succeed: user list, role management, etc.
119
+ - [ ] 419 responses are handled transparently (no user-visible interruption)
120
+ - [ ] Delegation token is cached (no 419 on every request)
121
+
122
+ ---
123
+
124
+ ## Troubleshooting
125
+
126
+ **Repeated 419 responses**
127
+ Check that `/api/delegation` is wired correctly and the `authenticate` middleware sets `req.accessToken`.
128
+
129
+ **"No pending delegation challenge"**
130
+ Server restarted between 419 and delegation POST. Browser retries automatically.
131
+
132
+ **"Delegation exchange failed"**
133
+ Check TideCloak has `tide-chain-of-trust` client authenticator enabled and `DelegationToken:1` in VRK model whitelist. A VRK rotation is needed after adding the model ID.
134
+
135
+ **"DPoP is not initialized"**
136
+ `signDelegationRequest` needs the DPoP provider. Ensure `useDPoP` is in the TideCloakProvider config object.
137
+
138
+ **Delegation token has wrong roles**
139
+ If using scoped delegation, everything not listed is excluded. Omit `roles` for full permissions.
140
+
141
+ **Admin API returns 403**
142
+ The user does not have the required role (e.g., `tide-realm-admin` on `realm-management`). Delegation can only use roles the user already has.
@@ -0,0 +1,130 @@
1
+ # Start TideCloak for Development
2
+
3
+ Start a TideCloak container for local development. This produces a running server — no realm, no client, no admin user yet.
4
+
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - First step of any TideCloak bootstrap
10
+ - Rebuilding a local TideCloak after a wipe
11
+ - Need a clean TideCloak for testing
12
+
13
+ **Do not use** if TideCloak is already running and healthy. Check: `curl -sf http://localhost:8080 > /dev/null && echo "Running"`.
14
+
15
+ ---
16
+
17
+ ## Prerequisites
18
+
19
+ - Docker installed and running (`docker info` succeeds)
20
+ - `sudo` access (data directory permissions)
21
+ - Port 8080 available (or adjust mapping)
22
+
23
+ ---
24
+
25
+ ## Steps
26
+
27
+ ### Step 1: Choose image
28
+
29
+ | Need | Image |
30
+ |------|-------|
31
+ | Standard development and production | `tideorg/tidecloak-dev:latest` |
32
+ | Staging / testing pre-release features | `tideorg/tidecloak-stg-dev:latest` |
33
+
34
+ Both images include the full Tide protocol. `tidecloak-dev` is the production image. `tidecloak-stg-dev` is the development/staging image for testing.
35
+
36
+ **Do not append `start-dev` or any command** to `docker run`. TideCloak images have a pre-configured entrypoint. Appending `start-dev` (a vanilla Keycloak convention) breaks Tide initialization.
37
+
38
+ ### Step 2: Clean previous state
39
+
40
+ ```bash
41
+ docker stop tidecloak 2>/dev/null; docker rm tidecloak 2>/dev/null
42
+ mkdir -p ./data
43
+ sudo rm -f ./data/keycloakdb* 2>/dev/null
44
+ sudo chown -R 1000:1000 ./data
45
+ ```
46
+
47
+ Check port conflict:
48
+ ```bash
49
+ lsof -i :8080 >/dev/null 2>&1 && echo "ERROR: Port 8080 in use" && exit 1
50
+ ```
51
+
52
+ ### Step 3: Start container
53
+
54
+ **Dev image (recommended for development):**
55
+
56
+ No ORK/threshold env vars needed — the dev image has built-in defaults.
57
+
58
+ ```bash
59
+ sudo docker run -d --name tidecloak \
60
+ -v "$(pwd)/data:/opt/keycloak/data/h2" \
61
+ -p 8080:8080 \
62
+ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
63
+ -e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
64
+ tideorg/tidecloak-dev:latest
65
+ ```
66
+
67
+ **Staging image (pre-release testing only):**
68
+
69
+ Requires ORK, threshold, and payer config.
70
+
71
+ ```bash
72
+ sudo docker run -d --name tidecloak \
73
+ -v "$(pwd)/data:/opt/keycloak/data/h2" \
74
+ -p 8080:8080 \
75
+ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
76
+ -e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
77
+ -e KC_HOSTNAME="${TIDECLOAK_URL:-http://localhost:8080}" \
78
+ -e SYSTEM_HOME_ORK=https://sork1.tideprotocol.com \
79
+ -e USER_HOME_ORK=https://sork1.tideprotocol.com \
80
+ -e THRESHOLD_T=3 \
81
+ -e THRESHOLD_N=5 \
82
+ -e PAYER_PUBLIC=20000011d6a0e8212d682657147d864b82d10e92776c15ead43dcfdc100ebf4dcfe6a8 \
83
+ tideorg/tidecloak-stg-dev:latest
84
+ ```
85
+
86
+ ### Step 4: Wait for readiness
87
+
88
+ ```bash
89
+ for i in {1..15}; do
90
+ curl -sf http://localhost:8080 > /dev/null 2>&1 && echo "TideCloak ready" && break
91
+ echo "Waiting (attempt $i/15)..."
92
+ sleep 5
93
+ done
94
+ ```
95
+
96
+ TideCloak typically takes 30–60 seconds to start. If it does not respond after 15 attempts, check `docker logs tidecloak`.
97
+
98
+ ---
99
+
100
+ ## Verification
101
+
102
+ - [ ] `docker ps` shows `tidecloak` running
103
+ - [ ] `curl -sf http://localhost:8080` returns 200
104
+ - [ ] `docker logs tidecloak` shows no fatal errors
105
+
106
+ ---
107
+
108
+ ## Common Failures
109
+
110
+ | Symptom | Cause | Fix |
111
+ |---------|-------|-----|
112
+ | Container exits immediately | Stale H2 DB files | `sudo rm -f ./data/keycloakdb*` and restart |
113
+ | `AccessDeniedException` in logs | Wrong data directory mount or permissions | Mount `./data` not `.`, run `sudo chown -R 1000:1000 ./data` |
114
+ | Port already in use | Another container or service on 8080 | `docker rm -f $(docker ps -q --filter publish=8080)` |
115
+ | Exit code 2, HTTPS warnings | Named volume from previous docker-compose | `docker volume rm <volume_name>`, use fresh `./data` |
116
+
117
+ ---
118
+
119
+ ## Anti-Patterns
120
+
121
+ - **Do not** mount project root as data volume. Use `./data` subdirectory.
122
+ - **Do not** omit Tide env vars (`SYSTEM_HOME_ORK`, `USER_HOME_ORK`, etc.) when using the staging image. The dev image has built-in defaults and does not need them.
123
+ - **Do not** use `THRESHOLD_T=1` anywhere. Single-ORK compromise.
124
+ - **Do not** reuse data directory across environments. Clean between setups.
125
+
126
+ ---
127
+
128
+ ## Next Step
129
+
130
+ Proceed to playbook `bootstrap-realm-from-template`.