@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,792 @@
1
+ # Deploy TideCloak with Docker and Auto-Initialize
2
+
3
+ Deploy TideCloak in Docker, auto-initialize a realm with Tide licensing, IGA, admin creation, and adapter config export.
4
+
5
+ ---
6
+
7
+ ## When to Use
8
+
9
+ - Setting up TideCloak for the first time on a development or staging machine
10
+ - Automating realm creation instead of clicking through the Admin Console
11
+ - Need a repeatable, scriptable TideCloak deployment for CI or onboarding
12
+ - Rebuilding a TideCloak instance from scratch after a wipe
13
+
14
+ **Do not use** if:
15
+ - You already have a running TideCloak with a configured realm (use Admin Console or API directly)
16
+ - You are deploying to production with an external database (this playbook covers H2-backed dev/staging only)
17
+
18
+ ---
19
+
20
+ ## Prerequisites
21
+
22
+ ### Tools
23
+
24
+ - Docker installed and running (`docker info` succeeds)
25
+ - `curl` installed
26
+ - `jq` installed
27
+ - `lsof` installed (for port conflict detection)
28
+ - `sudo` access (for data directory permissions)
29
+
30
+ ### Network
31
+
32
+ - Port 8080 available on the host (or change mapping)
33
+ - Internet access to pull `tideorg/tidecloak-dev` or `tideorg/tidecloak-stg-dev` images
34
+ - Internet access to reach `https://sork1.tideprotocol.com` (Tide ORK network) if using staging image
35
+
36
+ ### Decision: Which Image
37
+
38
+ | Image | Use Case | Database | Tide Network |
39
+ |-------|----------|----------|--------------|
40
+ | `tideorg/tidecloak-dev:latest` | Local development, single node, no Tide network needed | H2 (embedded) | Not connected |
41
+ | `tideorg/tidecloak-stg-dev:latest` | Staging, connects to Tide ORK network, IGA fully functional | H2 (embedded) | Connected via ORK env vars |
42
+
43
+ Use `tidecloak-dev` for quick local work. Use `tidecloak-stg-dev` when you need the full Tide protocol (licensing, IGA approval flows, admin invite links).
44
+
45
+ **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.
46
+
47
+ ---
48
+
49
+ ## Environment Variables
50
+
51
+ | Variable | Purpose | Example | Required For |
52
+ |----------|---------|---------|--------------|
53
+ | `KC_BOOTSTRAP_ADMIN_USERNAME` | Initial admin user | `admin` | Both images |
54
+ | `KC_BOOTSTRAP_ADMIN_PASSWORD` | Initial admin password | `password` | Both images |
55
+ | `KC_HOSTNAME` | Public-facing URL | `https://auth.myapp.com` | Staging only |
56
+ | `SYSTEM_HOME_ORK` | Tide network entry point | `https://sork1.tideprotocol.com` | Staging only |
57
+ | `USER_HOME_ORK` | User-facing ORK URL | `https://sork1.tideprotocol.com` | Staging only |
58
+ | `THRESHOLD_T` | Signing threshold | `3` | Staging only |
59
+ | `THRESHOLD_N` | Total ORK nodes | `5` | Staging only |
60
+ | `PAYER_PUBLIC` | Payer ORK public key | hex string | Staging only |
61
+ | `KC_PROXY_HEADERS` | Trust forwarded headers from reverse proxy | `xforwarded` | Behind reverse proxy only |
62
+
63
+ ---
64
+
65
+ ## Exact Step Sequence
66
+
67
+ ### Step 1: Start TideCloak (Development)
68
+
69
+ For local development without Tide network connection:
70
+
71
+ ```bash
72
+ mkdir -p ./data
73
+ sudo docker run -d \
74
+ --name tidecloak \
75
+ -v ./data:/opt/keycloak/data/h2 \
76
+ -p 8080:8080 \
77
+ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
78
+ -e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
79
+ tideorg/tidecloak-dev:latest
80
+ ```
81
+
82
+ ---
83
+
84
+ ### Step 1 (Alternative): Start TideCloak (Staging)
85
+
86
+ For staging with full Tide protocol:
87
+
88
+ ```bash
89
+ mkdir -p ./data
90
+ docker run -d \
91
+ --name tidecloak \
92
+ -v ./data:/opt/keycloak/data/h2 \
93
+ -p 8080:8080 \
94
+ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
95
+ -e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
96
+ -e KC_HOSTNAME=https://auth.myapp.com \
97
+ -e SYSTEM_HOME_ORK=https://sork1.tideprotocol.com \
98
+ -e USER_HOME_ORK=https://sork1.tideprotocol.com \
99
+ -e THRESHOLD_T=3 \
100
+ -e THRESHOLD_N=5 \
101
+ -e PAYER_PUBLIC=20000011d6a0e8212d682657147d864b82d10e92776c15ead43dcfdc100ebf4dcfe6a8 \
102
+ tideorg/tidecloak-stg-dev:latest
103
+ ```
104
+
105
+ **Important:** Always mount a dedicated `data/` subdirectory, not the project root (`.`). Mounting the project root causes H2 database permission errors (`AccessDeniedException`) because the container writes DB files as a different user than your project files.
106
+
107
+ ---
108
+
109
+ ### Step 2: Create the realm.json Template
110
+
111
+ Create a file named `realm.json` in your working directory. This template is used by the auto-initialization script.
112
+
113
+ **CRITICAL:** This template must be used exactly as shown. Do not create a minimal version. Every section is required for Tide to initialize correctly. Missing `requiredActions` causes invite link failures. Missing `authenticationFlows` causes login failures. Missing `protocolMappers` causes missing claims in JWTs.
114
+
115
+ Replace `REALM_NAME` and `CLIENT_NAME` with your values. Replace `CLIENT_APP_URL` with your app's URL. The init script performs these replacements automatically via `sed`.
116
+
117
+ ```json
118
+ {
119
+ "realm": "REALM_NAME",
120
+ "accessTokenLifespan": 600,
121
+ "enabled": true,
122
+ "sslRequired": "external",
123
+ "registrationAllowed": false,
124
+ "duplicateEmailsAllowed": true,
125
+ "roles": {
126
+ "realm": [
127
+ { "name": "appUser", "description": "Standard application user" },
128
+ { "name": "_tide_enabled", "description": "Represents a tide user thats allowed perform actions on tide" },
129
+ {
130
+ "name": "default-roles-REALM_NAME",
131
+ "description": "${role_default-roles}",
132
+ "composite": true,
133
+ "composites": { "realm": ["_tide_enabled", "appUser"] }
134
+ }
135
+ ],
136
+ "client": { "CLIENT_NAME": [] }
137
+ },
138
+ "defaultRole": {
139
+ "name": "default-roles-REALM_NAME",
140
+ "description": "${role_default-roles}",
141
+ "composite": true,
142
+ "clientRole": false
143
+ },
144
+ "clients": [
145
+ {
146
+ "clientId": "CLIENT_NAME",
147
+ "enabled": true,
148
+ "redirectUris": [
149
+ "CLIENT_APP_URL",
150
+ "CLIENT_APP_URL/*",
151
+ "CLIENT_APP_URL/silent-check-sso.html",
152
+ "CLIENT_APP_URL/auth/redirect"
153
+ ],
154
+ "webOrigins": ["CLIENT_APP_URL"],
155
+ "standardFlowEnabled": true,
156
+ "implicitFlowEnabled": false,
157
+ "publicClient": true,
158
+ "fullScopeAllowed": true,
159
+ "protocolMappers": [
160
+ {
161
+ "name": "Tide User Key",
162
+ "protocol": "openid-connect",
163
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
164
+ "consentRequired": false,
165
+ "config": {
166
+ "introspection.token.claim": "true",
167
+ "userinfo.token.claim": "true",
168
+ "user.attribute": "tideUserKey",
169
+ "lightweight.claim": "true",
170
+ "id.token.claim": "true",
171
+ "access.token.claim": "true",
172
+ "claim.name": "tideuserkey",
173
+ "jsonType.label": "String"
174
+ }
175
+ },
176
+ {
177
+ "name": "Tide IGA Role Mapper",
178
+ "protocol": "openid-connect",
179
+ "protocolMapper": "tide-roles-mapper",
180
+ "consentRequired": false,
181
+ "config": {
182
+ "lightweight.claim": "true",
183
+ "access.token.claim": "true"
184
+ }
185
+ },
186
+ {
187
+ "name": "Tide vuid",
188
+ "protocol": "openid-connect",
189
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
190
+ "consentRequired": false,
191
+ "config": {
192
+ "introspection.token.claim": "true",
193
+ "userinfo.token.claim": "true",
194
+ "user.attribute": "vuid",
195
+ "lightweight.claim": "true",
196
+ "id.token.claim": "true",
197
+ "access.token.claim": "true",
198
+ "claim.name": "vuid",
199
+ "jsonType.label": "String"
200
+ }
201
+ }
202
+ ]
203
+ }
204
+ ],
205
+ "components": {
206
+ "org.keycloak.userprofile.UserProfileProvider": [
207
+ {
208
+ "providerId": "declarative-user-profile",
209
+ "config": {
210
+ "kc.user.profile.config": [
211
+ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}"
212
+ ]
213
+ }
214
+ }
215
+ ]
216
+ },
217
+ "authenticationFlows": [
218
+ {
219
+ "alias": "tidebrowser",
220
+ "providerId": "basic-flow",
221
+ "topLevel": true,
222
+ "authenticationExecutions": [
223
+ {
224
+ "authenticator": "auth-cookie",
225
+ "authenticatorFlow": false,
226
+ "requirement": "ALTERNATIVE",
227
+ "priority": 10,
228
+ "userSetupAllowed": false
229
+ },
230
+ {
231
+ "authenticatorConfig": "tide browser",
232
+ "authenticator": "identity-provider-redirector",
233
+ "authenticatorFlow": false,
234
+ "requirement": "ALTERNATIVE",
235
+ "priority": 25,
236
+ "userSetupAllowed": false
237
+ }
238
+ ]
239
+ }
240
+ ],
241
+ "authenticatorConfig": [
242
+ {
243
+ "alias": "tide browser",
244
+ "config": { "defaultProvider": "tide" }
245
+ }
246
+ ],
247
+ "browserFlow": "tidebrowser",
248
+ "requiredActions": [
249
+ {
250
+ "alias": "link-tide-account-action",
251
+ "name": "Link Tide Account",
252
+ "providerId": "link-tide-account-action",
253
+ "enabled": true
254
+ },
255
+ {
256
+ "alias": "UPDATE_PASSWORD",
257
+ "name": "Update Password",
258
+ "providerId": "UPDATE_PASSWORD",
259
+ "enabled": false
260
+ }
261
+ ]
262
+ }
263
+ ```
264
+
265
+ ---
266
+
267
+ ### Step 3: Run the Auto-Initialization Script
268
+
269
+ This script automates the full realm setup: container launch, realm creation, Tide licensing, IGA enablement, admin user creation, invite link generation, change request approval, IdP configuration, and adapter config export.
270
+
271
+ **Initialization sequence order (never skip or reorder):**
272
+
273
+ 1. Clean up previous state
274
+ 2. Start container
275
+ 3. Wait for TideCloak readiness
276
+ 4. Get admin token
277
+ 5. Create realm from template
278
+ 6. Initialize Tide realm + enable IGA
279
+ 7. Approve client change requests
280
+ 8. Create admin user
281
+ 9. Approve user creation change request (user is not queryable until committed)
282
+ 10. Assign `tide-realm-admin` role (validate role lookup succeeds)
283
+ 11. Generate invite link (admin links their Tide account)
284
+ 12. Wait for admin to link their Tide account
285
+ 13. Approve role assignment change requests
286
+ 14. Update CustomAdminUIDomain for enclave approval popups
287
+ 15. Download adapter config
288
+
289
+ Save the following as `init-tidecloak.sh`:
290
+
291
+ ```bash
292
+ #!/bin/bash
293
+ # Configuration
294
+ TIDECLOAK_URL="${TIDECLOAK_LOCAL_URL:-http://localhost:8080}"
295
+ CLIENT_APP_URL="${CLIENT_APP_URL:-http://localhost:3000}"
296
+ REALM_NAME="${NEW_REALM_NAME:-myapp}"
297
+ CLIENT_NAME="${CLIENT_NAME:-myclient}"
298
+ ADAPTER_OUTPUT="./data/tidecloak.json"
299
+
300
+ # 0. Clean up any previous state
301
+ # - Stop and remove existing container (if any)
302
+ # - Remove stale H2 database files (cause "Could not open file" errors)
303
+ # - Fix directory permissions (container runs as UID 1000)
304
+ # - Handle port conflicts
305
+ docker stop tidecloak 2>/dev/null
306
+ docker rm tidecloak 2>/dev/null
307
+ mkdir -p ./data
308
+ sudo rm -f ./data/keycloakdb* 2>/dev/null
309
+ sudo chown -R 1000:1000 ./data
310
+
311
+ # Check if port 8080 is already in use
312
+ if lsof -i :8080 >/dev/null 2>&1; then
313
+ echo "ERROR: Port 8080 is already in use. Stop the existing service or change the port."
314
+ exit 1
315
+ fi
316
+
317
+ # Dev image (default): no ORK/threshold env vars needed.
318
+ # For staging image (tidecloak-stg-dev), add: KC_HOSTNAME, SYSTEM_HOME_ORK,
319
+ # USER_HOME_ORK, THRESHOLD_T, THRESHOLD_N, PAYER_PUBLIC.
320
+ sudo docker run -d --name tidecloak \
321
+ -v "$(pwd)/data:/opt/keycloak/data/h2" \
322
+ -p 8080:8080 \
323
+ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
324
+ -e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
325
+ tideorg/tidecloak-dev:latest
326
+
327
+ if [ $? -ne 0 ]; then
328
+ echo "ERROR: Docker failed to start. Check 'docker logs tidecloak' for details."
329
+ exit 1
330
+ fi
331
+
332
+ # 1. Wait for TideCloak to be ready
333
+ for i in {1..15}; do
334
+ curl -s -f "$TIDECLOAK_URL" > /dev/null 2>&1 && break
335
+ echo "Waiting for TideCloak (attempt $i/15)..."
336
+ sleep 5
337
+ done
338
+
339
+ # 2. Get admin token
340
+ get_token() {
341
+ curl -s -X POST "$TIDECLOAK_URL/realms/master/protocol/openid-connect/token" \
342
+ -H "Content-Type: application/x-www-form-urlencoded" \
343
+ -d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
344
+ | jq -r '.access_token'
345
+ }
346
+
347
+ # 3. Create realm from template
348
+ # Replace placeholders in realm.json with actual values
349
+ TMP_REALM="$(mktemp)"
350
+ cp realm.json "$TMP_REALM"
351
+ sed -i "s|REALM_NAME|$REALM_NAME|g" "$TMP_REALM"
352
+ sed -i "s|CLIENT_NAME|$CLIENT_NAME|g" "$TMP_REALM"
353
+ sed -i "s|CLIENT_APP_URL|$CLIENT_APP_URL|g" "$TMP_REALM"
354
+
355
+ TOKEN="$(get_token)"
356
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms" \
357
+ -H "Authorization: Bearer $TOKEN" \
358
+ -H "Content-Type: application/json" \
359
+ --data-binary @"$TMP_REALM"
360
+ rm -f "$TMP_REALM"
361
+
362
+ # 4. Initialize Tide realm + enable IGA
363
+ TOKEN="$(get_token)"
364
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/setUpTideRealm" \
365
+ -H "Authorization: Bearer $TOKEN" \
366
+ -H "Content-Type: application/x-www-form-urlencoded" \
367
+ --data-urlencode "email=admin@yourorg.com" \
368
+ --data-urlencode "isRagnarokEnabled=true"
369
+
370
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/toggle-iga" \
371
+ -H "Authorization: Bearer $TOKEN" \
372
+ -H "Content-Type: application/x-www-form-urlencoded" \
373
+ --data-urlencode "isIGAEnabled=true"
374
+
375
+ # 5. Auto-approve client change requests (created during realm setup)
376
+ approve_and_commit() {
377
+ local TYPE=$1
378
+ TOKEN="$(get_token)"
379
+ local requests=$(curl -s -X GET \
380
+ "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/$TYPE/requests" \
381
+ -H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "[]")
382
+
383
+ echo "$requests" | jq -c '.[]' | while read -r req; do
384
+ local payload=$(jq -n \
385
+ --arg id "$(echo "$req" | jq -r .draftRecordId)" \
386
+ --arg cst "$(echo "$req" | jq -r .changeSetType)" \
387
+ --arg at "$(echo "$req" | jq -r .actionType)" \
388
+ '{changeSetId:$id,changeSetType:$cst,actionType:$at}')
389
+
390
+ # Sign then commit
391
+ TOKEN="$(get_token)"
392
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/sign" \
393
+ -H "Authorization: Bearer $TOKEN" \
394
+ -H "Content-Type: application/json" -d "$payload"
395
+
396
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tide-admin/change-set/commit" \
397
+ -H "Authorization: Bearer $TOKEN" \
398
+ -H "Content-Type: application/json" -d "$payload"
399
+ done
400
+ }
401
+
402
+ approve_and_commit clients
403
+
404
+ # 6. Create admin user
405
+ TOKEN="$(get_token)"
406
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users" \
407
+ -H "Authorization: Bearer $TOKEN" \
408
+ -H "Content-Type: application/json" \
409
+ -d '{"username":"admin","email":"admin@yourorg.com","enabled":true}'
410
+
411
+ # 6b. Approve user creation (user is not queryable until committed with IGA enabled)
412
+ sleep 2
413
+ approve_and_commit users
414
+
415
+ # 6c. Assign tide-realm-admin role
416
+ TOKEN="$(get_token)"
417
+ USER_ID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
418
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
419
+
420
+ if [ -z "$USER_ID" ] || [ "$USER_ID" = "null" ]; then
421
+ echo "ERROR: Could not find admin user. Was the user change request approved?"
422
+ exit 1
423
+ fi
424
+
425
+ CLIENT_UUID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=realm-management" \
426
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
427
+
428
+ ROLE_JSON=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients/$CLIENT_UUID/roles/tide-realm-admin" \
429
+ -H "Authorization: Bearer $TOKEN")
430
+
431
+ if echo "$ROLE_JSON" | jq -e '.error' > /dev/null 2>&1; then
432
+ echo "ERROR: tide-realm-admin role not found: $ROLE_JSON"
433
+ exit 1
434
+ fi
435
+
436
+ TOKEN="$(get_token)"
437
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users/$USER_ID/role-mappings/clients/$CLIENT_UUID" \
438
+ -H "Authorization: Bearer $TOKEN" \
439
+ -H "Content-Type: application/json" \
440
+ -d "[$ROLE_JSON]"
441
+
442
+ # 7. Generate invite link for admin to link their Tide account
443
+ TOKEN="$(get_token)"
444
+ INVITE_LINK=$(curl -s -X POST \
445
+ "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tideAdminResources/get-required-action-link?userId=$USER_ID&lifespan=43200" \
446
+ -H "Authorization: Bearer $TOKEN" \
447
+ -H "Content-Type: application/json" \
448
+ -d '["link-tide-account-action"]')
449
+
450
+ echo "Open this link to link your admin Tide account:"
451
+ echo "$INVITE_LINK"
452
+
453
+ # 8. Wait for admin to link their Tide account
454
+ while true; do
455
+ TOKEN="$(get_token)"
456
+ KEY=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/users?username=admin" \
457
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].attributes.tideUserKey[0] // empty')
458
+ [ -n "$KEY" ] && break
459
+ sleep 5
460
+ done
461
+
462
+ # 9. Approve role assignment change requests
463
+ sleep 2
464
+ approve_and_commit users
465
+ approve_and_commit roles
466
+
467
+ # 10. Update CustomAdminUIDomain for enclave approval popups
468
+ # CustomAdminUIDomain is OPTIONAL and APP-SPECIFIC: only needed when a separate
469
+ # application (not the built-in Admin Console) hosts the change-set approval UI.
470
+ # sign-idp-settings is ALWAYS required after any Tide IDP config change.
471
+ TOKEN="$(get_token)"
472
+ INST=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/identity-provider/instances/tide" \
473
+ -H "Authorization: Bearer $TOKEN")
474
+ UPDATED=$(echo "$INST" | jq --arg d "$CLIENT_APP_URL" '.config.CustomAdminUIDomain=$d')
475
+ curl -s -X PUT "$TIDECLOAK_URL/admin/realms/$REALM_NAME/identity-provider/instances/tide" \
476
+ -H "Authorization: Bearer $TOKEN" \
477
+ -H "Content-Type: application/json" -d "$UPDATED"
478
+ curl -s -X POST "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/sign-idp-settings" \
479
+ -H "Authorization: Bearer $TOKEN"
480
+
481
+ # 11. Download adapter config
482
+ TOKEN="$(get_token)"
483
+ CLIENT_UUID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=$CLIENT_NAME" \
484
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
485
+ curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/get-installations-provider?clientId=$CLIENT_UUID&providerId=keycloak-oidc-keycloak-json" \
486
+ -H "Authorization: Bearer $TOKEN" > "$ADAPTER_OUTPUT"
487
+
488
+ echo "Done! Adapter config saved to $ADAPTER_OUTPUT"
489
+ ```
490
+
491
+ Make it executable and run:
492
+
493
+ ```bash
494
+ chmod +x init-tidecloak.sh
495
+ ./init-tidecloak.sh
496
+ ```
497
+
498
+ Override defaults with environment variables:
499
+
500
+ ```bash
501
+ TIDECLOAK_LOCAL_URL=http://localhost:8080 \
502
+ CLIENT_APP_URL=http://localhost:3000 \
503
+ NEW_REALM_NAME=myapp \
504
+ CLIENT_NAME=myclient \
505
+ ./init-tidecloak.sh
506
+ ```
507
+
508
+ ---
509
+
510
+ ### Step 4: Download Adapter Config
511
+
512
+ The init script saves the adapter config to `./data/tidecloak.json` automatically.
513
+
514
+ If you need to download it manually after initialization:
515
+
516
+ ```bash
517
+ TIDECLOAK_URL="http://localhost:8080"
518
+ REALM_NAME="myapp"
519
+ CLIENT_NAME="myclient"
520
+
521
+ TOKEN=$(curl -s -X POST "$TIDECLOAK_URL/realms/master/protocol/openid-connect/token" \
522
+ -H "Content-Type: application/x-www-form-urlencoded" \
523
+ -d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
524
+ | jq -r '.access_token')
525
+
526
+ CLIENT_UUID=$(curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/clients?clientId=$CLIENT_NAME" \
527
+ -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')
528
+
529
+ curl -s -X GET "$TIDECLOAK_URL/admin/realms/$REALM_NAME/vendorResources/get-installations-provider?clientId=$CLIENT_UUID&providerId=keycloak-oidc-keycloak-json" \
530
+ -H "Authorization: Bearer $TOKEN" > ./data/tidecloak.json
531
+ ```
532
+
533
+ ---
534
+
535
+ ## Key Initialization Endpoints
536
+
537
+ | Endpoint | Purpose |
538
+ |----------|---------|
539
+ | `POST /admin/realms` | Create realm from JSON template |
540
+ | `POST /admin/realms/{realm}/vendorResources/setUpTideRealm` | Initialize Tide licensing + VRK |
541
+ | `POST /admin/realms/{realm}/tide-admin/toggle-iga` | Enable IGA governance |
542
+ | `POST /admin/realms/{realm}/tide-admin/change-set/sign` | Approve a change request |
543
+ | `POST /admin/realms/{realm}/tide-admin/change-set/commit` | Commit an approved change |
544
+ | `POST /admin/realms/{realm}/tideAdminResources/get-required-action-link` | Generate admin invite link |
545
+ | `GET /admin/realms/{realm}/vendorResources/get-installations-provider` | Download adapter config |
546
+ | `POST /admin/realms/{realm}/vendorResources/sign-idp-settings` | Sign IdP settings with VRK. **ALWAYS required** after any Tide IDP config change (logo, background, backup toggle, custom admin domain, or client origin changes). Without it, the enclave rejects settings as unsigned. Requires active VRK/license. |
547
+ | `POST /admin/realms/{realm}/tide-idp-admin-resources/images/upload` | Upload branding (logo/background) |
548
+
549
+ ---
550
+
551
+ ## Verification Checklist
552
+
553
+ ### Container Running
554
+
555
+ ```bash
556
+ docker ps --filter name=tidecloak
557
+ # Should show running container
558
+ ```
559
+
560
+ ### TideCloak Responds
561
+
562
+ ```bash
563
+ curl -s -f http://localhost:8080
564
+ # Should return HTML (Keycloak welcome page)
565
+ ```
566
+
567
+ ### Admin Token Works
568
+
569
+ ```bash
570
+ curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
571
+ -H "Content-Type: application/x-www-form-urlencoded" \
572
+ -d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
573
+ | jq -r '.access_token'
574
+ # Should return a JWT string, not null
575
+ ```
576
+
577
+ ### Realm Exists
578
+
579
+ ```bash
580
+ TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
581
+ -H "Content-Type: application/x-www-form-urlencoded" \
582
+ -d "username=admin&password=password&grant_type=password&client_id=admin-cli" \
583
+ | jq -r '.access_token')
584
+
585
+ curl -s -X GET "http://localhost:8080/admin/realms/myapp" \
586
+ -H "Authorization: Bearer $TOKEN" | jq '.realm'
587
+ # Should return "myapp"
588
+ ```
589
+
590
+ ### Adapter Config Downloaded
591
+
592
+ ```bash
593
+ cat ./data/tidecloak.json | jq '.realm'
594
+ # Should return your realm name
595
+
596
+ cat ./data/tidecloak.json | jq '.resource'
597
+ # Should return your client name
598
+
599
+ cat ./data/tidecloak.json | jq '.["auth-server-url"]'
600
+ # Should return your TideCloak URL
601
+ ```
602
+
603
+ ### IGA Enabled
604
+
605
+ ```bash
606
+ curl -s -X GET "http://localhost:8080/admin/realms/myapp" \
607
+ -H "Authorization: Bearer $TOKEN" | jq '.attributes.isIGAEnabled'
608
+ # Should return "true"
609
+ ```
610
+
611
+ ---
612
+
613
+ ## Common Failures
614
+
615
+ ### Wrong Mount Path
616
+
617
+ **Symptom**: `AccessDeniedException` or `Could not open file` errors in `docker logs tidecloak`. H2 database files appear in the project root.
618
+
619
+ **Cause**: Mounted the project root (`.`) instead of a dedicated `data/` subdirectory. The container writes DB files as UID 1000, conflicting with your project file ownership.
620
+
621
+ **Fix**:
622
+ ```bash
623
+ docker stop tidecloak && docker rm tidecloak
624
+ rm -f keycloakdb* # Remove stale DB files from project root
625
+ mkdir -p ./data
626
+ sudo chown -R 1000:1000 ./data
627
+ # Restart with -v "$(pwd)/data:/opt/keycloak/data/h2"
628
+ ```
629
+
630
+ ---
631
+
632
+ ### Permission Errors on Data Directory
633
+
634
+ **Symptom**: Container starts but crashes immediately. Logs show permission denied on `/opt/keycloak/data/h2`.
635
+
636
+ **Cause**: The `./data` directory is owned by root or your user, but the container process runs as UID 1000.
637
+
638
+ **Fix**:
639
+ ```bash
640
+ sudo chown -R 1000:1000 ./data
641
+ ```
642
+
643
+ ---
644
+
645
+ ### Port 8080 Already in Use
646
+
647
+ **Symptom**: `docker run` fails or container exits immediately. `docker logs tidecloak` shows bind error.
648
+
649
+ **Cause**: Another service (another TideCloak instance, a web server, Jenkins, etc.) is already using port 8080.
650
+
651
+ **Fix**:
652
+ ```bash
653
+ # Find what is using the port
654
+ lsof -i :8080
655
+
656
+ # Either stop that service, or remap:
657
+ docker run -d --name tidecloak \
658
+ -v "$(pwd)/data:/opt/keycloak/data/h2" \
659
+ -p 9080:8080 \
660
+ ...
661
+ # Then use TIDECLOAK_LOCAL_URL=http://localhost:9080
662
+ ```
663
+
664
+ ---
665
+
666
+ ### Stale Volumes / Old Database State
667
+
668
+ **Symptom**: Realm creation fails with "realm already exists". Or Tide initialization returns unexpected errors. Or old users/clients appear from a previous run.
669
+
670
+ **Cause**: The `./data` directory contains H2 database files from a previous run.
671
+
672
+ **Fix**:
673
+ ```bash
674
+ docker stop tidecloak && docker rm tidecloak
675
+ sudo rm -f ./data/keycloakdb*
676
+ sudo chown -R 1000:1000 ./data
677
+ # Restart the container
678
+ ```
679
+
680
+ ---
681
+
682
+ ### Wrong Endpoint Paths
683
+
684
+ **Symptom**: `404 Not Found` from curl calls during initialization. Script silently fails at a step.
685
+
686
+ **Cause**: Incorrect URL construction. Common mistakes:
687
+ - Missing `/admin/` prefix on admin endpoints
688
+ - Using `/auth/` prefix (Keycloak < 17 pattern, not used by TideCloak)
689
+ - Wrong realm name in URL
690
+
691
+ **Fix**: Verify the endpoint paths from the Key Initialization Endpoints table above. TideCloak does not use the `/auth/` prefix. All admin endpoints start with `/admin/realms/{realm}/`.
692
+
693
+ ---
694
+
695
+ ### Missing Environment Variables (Staging Image)
696
+
697
+ **Symptom**: Container starts but Tide realm initialization (`setUpTideRealm`) fails or hangs. ORK-dependent operations time out.
698
+
699
+ **Cause**: Using `tidecloak-stg-dev` image without setting `SYSTEM_HOME_ORK`, `USER_HOME_ORK`, `THRESHOLD_T`, `THRESHOLD_N`, or `PAYER_PUBLIC`.
700
+
701
+ **Fix**: Provide all required staging environment variables. See the Environment Variables table. If you do not need Tide network connectivity, use `tidecloak-dev` instead.
702
+
703
+ ---
704
+
705
+ ### Admin Token Expired Mid-Script
706
+
707
+ **Symptom**: A curl call returns `401 Unauthorized` partway through initialization.
708
+
709
+ **Cause**: Admin tokens are short-lived. The script re-fetches tokens via `get_token()` before each group of calls, but long waits (e.g., waiting for admin to link their Tide account in step 8) can cause expiry.
710
+
711
+ **Fix**: The init script already calls `get_token()` before each major step. If you modify the script, always refresh the token before any API call that follows a wait or delay.
712
+
713
+ ---
714
+
715
+ ### Invite Link Expired or Missing
716
+
717
+ **Symptom**: The admin invite link returns an error page. Or `get-required-action-link` returns an empty response.
718
+
719
+ **Cause**: The `link-tide-account-action` required action is missing from `realm.json`, or the invite link lifespan (43200 seconds = 12 hours) has expired.
720
+
721
+ **Fix**: Verify `realm.json` includes the `requiredActions` section with `link-tide-account-action`. Regenerate the link:
722
+ ```bash
723
+ TOKEN="$(get_token)"
724
+ curl -s -X POST \
725
+ "$TIDECLOAK_URL/admin/realms/$REALM_NAME/tideAdminResources/get-required-action-link?userId=$USER_ID&lifespan=43200" \
726
+ -H "Authorization: Bearer $TOKEN" \
727
+ -H "Content-Type: application/json" \
728
+ -d '["link-tide-account-action"]'
729
+ ```
730
+
731
+ ---
732
+
733
+ ## Anti-Patterns
734
+
735
+ ### Do Not Skip or Reorder Initialization Steps
736
+
737
+ The initialization sequence has strict ordering dependencies. Tide realm setup must happen before IGA enablement. Client change requests must be approved before admin user creation. User change requests must be approved after admin links their Tide account. Skipping or reordering steps causes silent failures or broken state.
738
+
739
+ ---
740
+
741
+ ### Do Not Minimize the realm.json Template
742
+
743
+ Every section in the template is required:
744
+ - `protocolMappers` -- without these, JWTs lack `tideuserkey`, `vuid`, and IGA role claims
745
+ - `authenticationFlows` + `authenticatorConfig` + `browserFlow` -- without these, login redirects to standard Keycloak login instead of Tide
746
+ - `requiredActions` -- without `link-tide-account-action`, admin invite links fail
747
+ - `roles` including `_tide_enabled` -- without this, Tide protocol features are disabled for users
748
+ - `components` (user profile) -- without this, user attributes behave incorrectly
749
+
750
+ ---
751
+
752
+ ### Do Not Use the Project Root as the Docker Volume Mount
753
+
754
+ ```bash
755
+ # WRONG
756
+ docker run -v .:/opt/keycloak/data/h2 ...
757
+
758
+ # CORRECT
759
+ docker run -v ./data:/opt/keycloak/data/h2 ...
760
+ ```
761
+
762
+ Mounting the project root causes H2 database files to be written alongside your source code with different ownership (UID 1000), leading to `AccessDeniedException` and polluted working directories.
763
+
764
+ ---
765
+
766
+ ### Do Not Hardcode Tokens
767
+
768
+ Admin tokens expire. Always call `get_token()` before each API call or group of calls. Do not capture a token once and reuse it across the entire script without refresh.
769
+
770
+ ---
771
+
772
+ ### Do Not Run the Init Script Against an Already-Initialized Realm
773
+
774
+ The script assumes a clean state. Running it twice creates duplicate users, fails on "realm already exists", or leaves change requests in an inconsistent state. If you need to re-initialize, clean up first:
775
+ ```bash
776
+ docker stop tidecloak && docker rm tidecloak
777
+ sudo rm -f ./data/keycloakdb*
778
+ ```
779
+
780
+ ---
781
+
782
+ ### Do Not Use tidecloak-dev Image When You Need IGA Approval Flows
783
+
784
+ The `tidecloak-dev` image is not connected to the Tide ORK network. Tide-specific features like `setUpTideRealm`, IGA change request signing, and admin invite links require the `tidecloak-stg-dev` image with ORK environment variables configured.
785
+
786
+ ---
787
+
788
+ ## References
789
+
790
+ - Source: operational exemplars: keylessh, forseti-crypto-quickstart, tidecloak-test-cases
791
+ - Reference scripts: keylessh `script/tidecloak/start.sh`, forseti-crypto-quickstart `init/tcinit.sh`, tidecloak-test-cases `tests/scripts/init-tidecloak.sh` + `handover-admin.sh`
792
+ - The tidecloak-test-cases `handover-admin.sh` provides a reusable CLI for admin setup: `-i` (invite link), `-c` (confirm linked), `-r` (assign role), `-a` (approve change-sets)