@tideorg/mcp 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GAP_REGISTER.md +117 -0
- package/README.md +38 -0
- package/adapters/AGENTS.md +241 -0
- package/adapters/CLAUDE.md +146 -0
- package/adapters/replit.md +224 -0
- package/canon/anti-patterns.md +2133 -0
- package/canon/concepts.md +816 -0
- package/canon/custom-contracts.md +533 -0
- package/canon/delegation.md +195 -0
- package/canon/feature-mapping.md +637 -0
- package/canon/framework-matrix.md +1125 -0
- package/canon/invariants.md +851 -0
- package/canon/redirect-handler.md +254 -0
- package/canon/tidecloak-bootstrap.md +287 -0
- package/canon/tidecloak-endpoints.md +294 -0
- package/canon/troubleshooting.md +1494 -0
- package/canon/version-policy.md +89 -0
- package/mcp-server/dist/index.d.ts +2 -0
- package/mcp-server/dist/index.js +605 -0
- package/package.json +45 -0
- package/playbooks/add-auth-nextjs-existing.md +799 -0
- package/playbooks/add-auth-nextjs-fresh.md +654 -0
- package/playbooks/add-rbac-nextjs.md +190 -0
- package/playbooks/bootstrap-realm-from-template.md +170 -0
- package/playbooks/configure-e2ee-roles-and-policies.md +196 -0
- package/playbooks/deploy-tidecloak-docker.md +792 -0
- package/playbooks/diagnose-broken-login.md +234 -0
- package/playbooks/diagnose-missing-roles-or-claims.md +253 -0
- package/playbooks/initialize-admin-and-link-account.md +235 -0
- package/playbooks/migrate-from-existing-auth.md +198 -0
- package/playbooks/protect-api-nextjs.md +451 -0
- package/playbooks/protect-routes-nextjs.md +544 -0
- package/playbooks/setup-forseti-e2ee.md +756 -0
- package/playbooks/setup-iga-admin-panel.md +344 -0
- package/playbooks/setup-server-delegation.md +142 -0
- package/playbooks/start-tidecloak-dev.md +130 -0
- package/playbooks/verify-jwt-server-side.md +439 -0
- package/prompts/add-admin-approval-flow.md +50 -0
- package/prompts/build-private-customer-portal.md +85 -0
- package/prompts/migrate-generic-auth-to-tide.md +67 -0
- package/prompts/secure-existing-app.md +80 -0
- package/reference-apps/INDEX.md +87 -0
- package/reference-apps/encrypted-communication/anti-patterns.md +123 -0
- package/reference-apps/encrypted-communication/bootstrap-sequence.md +152 -0
- package/reference-apps/encrypted-communication/manifest.yaml +100 -0
- package/reference-apps/encrypted-communication/role-policy-matrix.md +80 -0
- package/reference-apps/encrypted-communication/scenario.md +134 -0
- package/reference-apps/git-pr-signing-service/anti-patterns.md +61 -0
- package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +157 -0
- package/reference-apps/git-pr-signing-service/manifest.yaml +80 -0
- package/reference-apps/git-pr-signing-service/role-policy-matrix.md +99 -0
- package/reference-apps/git-pr-signing-service/scenario.md +169 -0
- package/reference-apps/iga-admin-governance/anti-patterns.md +133 -0
- package/reference-apps/iga-admin-governance/bootstrap-sequence.md +153 -0
- package/reference-apps/iga-admin-governance/manifest.yaml +87 -0
- package/reference-apps/iga-admin-governance/role-policy-matrix.md +67 -0
- package/reference-apps/iga-admin-governance/scenario.md +126 -0
- package/reference-apps/organisation-password-manager/anti-patterns.md +71 -0
- package/reference-apps/organisation-password-manager/bootstrap-sequence.md +127 -0
- package/reference-apps/organisation-password-manager/manifest.yaml +86 -0
- package/reference-apps/organisation-password-manager/role-policy-matrix.md +59 -0
- package/reference-apps/organisation-password-manager/scenario.md +107 -0
- package/reference-apps/policy-governed-signing/anti-patterns.md +67 -0
- package/reference-apps/policy-governed-signing/bootstrap-sequence.md +128 -0
- package/reference-apps/policy-governed-signing/manifest.yaml +78 -0
- package/reference-apps/policy-governed-signing/role-policy-matrix.md +62 -0
- package/reference-apps/policy-governed-signing/scenario.md +113 -0
- package/skills/tide-diagnostics/SKILL.md +99 -0
- package/skills/tide-integration/SKILL.md +136 -0
- package/skills/tide-learning-capture/SKILL.md +174 -0
- package/skills/tide-rbac-and-e2ee/SKILL.md +214 -0
- package/skills/tide-reviewer/SKILL.md +133 -0
- package/skills/tide-route-and-api-protection/SKILL.md +201 -0
- package/skills/tide-scenario-resolver/SKILL.md +81 -0
- package/skills/tide-setup/SKILL.md +218 -0
- package/skills/tide-solutions-architect/SKILL.md +130 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Prompt: Secure an Existing App with Tide
|
|
2
|
+
|
|
3
|
+
Copy-paste this prompt to an AI coding agent to add Tide to an existing Next.js application safely and incrementally.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Prompt
|
|
8
|
+
|
|
9
|
+
> I have an existing Next.js app. Add Tide authentication and server-side API protection to it. Preserve the existing app behavior. Do not rewrite the app.
|
|
10
|
+
>
|
|
11
|
+
> **Route first, then act.** Use the development team model. Team sequence: Scenario Resolver → Setup → Application Engineer (retrofit) → Security Engineer → IAM/Policy if needed → Reviewer before handoff. Check `reference-apps/INDEX.md` for a matching scenario.
|
|
12
|
+
>
|
|
13
|
+
> **Assumptions**:
|
|
14
|
+
> - The app already has pages, routes, and possibly API endpoints.
|
|
15
|
+
> - The app may or may not have existing auth (e.g., NextAuth, custom JWT, session cookies).
|
|
16
|
+
>
|
|
17
|
+
> **Inspect first** (before any code changes):
|
|
18
|
+
> 1. Is TideCloak running? `curl -sf http://localhost:8080 > /dev/null && echo "Running" || echo "Not running"`
|
|
19
|
+
> 2. Check if Tide is already installed: `grep '@tidecloak' package.json`
|
|
20
|
+
> 3. Check for existing auth: `grep -r "auth\|login\|session\|jwt\|NextAuth" app/ pages/ lib/ --include="*.ts" --include="*.tsx" | head -20`
|
|
21
|
+
> 4. Check for existing request interception: `cat proxy.ts 2>/dev/null || cat middleware.ts 2>/dev/null`
|
|
22
|
+
> 5. Check for existing API routes: `find app/api pages/api -name "*.ts" 2>/dev/null`
|
|
23
|
+
> 6. Check if any API route already verifies JWTs: `grep -r "jwtVerify\|verifyToken\|verifyJWT" app/api/ lib/ --include="*.ts" 2>/dev/null`
|
|
24
|
+
>
|
|
25
|
+
> **If TideCloak is not running**, bootstrap it first:
|
|
26
|
+
> 1. Playbook `start-tidecloak-dev` — start the Docker container
|
|
27
|
+
> 2. Playbook `bootstrap-realm-from-template` — create realm, enable licensing + IGA
|
|
28
|
+
> 3. Playbook `initialize-admin-and-link-account` — create admin, link Tide account, export adapter JSON
|
|
29
|
+
>
|
|
30
|
+
> All three playbooks are sequential and mandatory. Bootstrap is NOT complete until a user with a non-empty `tideUserKey` attribute exists and `data/tidecloak.json` is exported. Do not attempt Tide SDK wiring until all three steps finish. VERIFIED (atproto-learnings L-01).
|
|
31
|
+
>
|
|
32
|
+
> **If TideCloak is running but Tide is not installed**, determine the right setup path:
|
|
33
|
+
> - No existing auth at all → playbook `add-auth-nextjs-fresh`
|
|
34
|
+
> - Has existing auth to replace or supplement → playbook `add-auth-nextjs-existing`
|
|
35
|
+
> - Follow the `tide-setup` skill to complete all checks.
|
|
36
|
+
>
|
|
37
|
+
> **If Tide is already installed**, determine what is missing:
|
|
38
|
+
> - No server-side JWT verification (`lib/auth/tideJWT.ts` absent) → follow `tide-route-and-api-protection` skill
|
|
39
|
+
> - JWT verification exists but no role checks → follow `tide-rbac-and-e2ee` skill
|
|
40
|
+
> - Everything in place → audit existing API routes for gaps (see below)
|
|
41
|
+
>
|
|
42
|
+
> **Package versions** (when installing Tide packages):
|
|
43
|
+
> 1. Run `npm view @tidecloak/nextjs version` before adding to package.json. Pin the exact version. Do not use `"latest"`.
|
|
44
|
+
> 2. If the resolved version is 0.99.x, skip it. Fall back to `canon/version-policy.md`.
|
|
45
|
+
> 3. Do not use `--force` or `--legacy-peer-deps` as the default. Align versions to resolve peer conflicts.
|
|
46
|
+
> 4. Include a short version-selection summary in your output.
|
|
47
|
+
>
|
|
48
|
+
> **Playbook sequence** (follow in order, skip steps already completed):
|
|
49
|
+
> 1. `add-auth-nextjs-fresh` or `add-auth-nextjs-existing` — setup. Verify login works before proceeding.
|
|
50
|
+
> 2. `protect-routes-nextjs` — client-side route guards (UX only).
|
|
51
|
+
> 3. `protect-api-nextjs` then `verify-jwt-server-side` — server-side JWT + DPoP verification. This is real authorization.
|
|
52
|
+
> 4. `add-rbac-nextjs` — role-based access control. Only after step 3 is verified.
|
|
53
|
+
>
|
|
54
|
+
> Use the `tide-route-and-api-protection` skill's diagnostic table to audit any existing API routes for gaps.
|
|
55
|
+
>
|
|
56
|
+
> **Preserve existing behavior**:
|
|
57
|
+
> - Do not remove existing pages or routes.
|
|
58
|
+
> - Do not restructure the project unless required for Tide wiring.
|
|
59
|
+
> - Add Tide as a layer: provider wrapping the app, verification in API routes.
|
|
60
|
+
> - Test that existing non-auth functionality still works after each step.
|
|
61
|
+
> - If the app uses existing auth (NextAuth, custom), decide with me whether to replace or run in parallel. Do not silently remove it.
|
|
62
|
+
>
|
|
63
|
+
> **Do not**: Create fake auth helpers, plain-text role cookies, or ad hoc local auth. Do not treat `hasRole()` or route guards as real API protection. Do not skip DPoP. See `adapters/AGENTS.md` Forbidden Shortcuts for the full list.
|
|
64
|
+
>
|
|
65
|
+
> **If the app is not Tide-enabled at all**, stop all other work. Complete setup first. Do not diagnose Tide issues on an app without Tide installed.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Acceptance Criteria
|
|
70
|
+
|
|
71
|
+
- [ ] TideCloak running with realm configured and adapter JSON exported
|
|
72
|
+
- [ ] Tide provider wraps the app with DPoP enabled
|
|
73
|
+
- [ ] Adapter JSON present with `jwk`, `vendorId`, `homeOrkUrl`
|
|
74
|
+
- [ ] Login flow works (redirect to TideCloak and back)
|
|
75
|
+
- [ ] Every API route that handles sensitive data verifies JWT server-side
|
|
76
|
+
- [ ] Unauthenticated curl to protected API returns 401
|
|
77
|
+
- [ ] Existing non-auth functionality still works
|
|
78
|
+
- [ ] No fake auth helpers or plain-text role cookies in codebase
|
|
79
|
+
- [ ] Post-auth redirect handler exists at configured `redirectUri` path
|
|
80
|
+
- [ ] Browser console shows no CSP violations
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Scenario-Pattern Library
|
|
2
|
+
|
|
3
|
+
Known app scenarios with pre-defined Tide role, policy, and bootstrap patterns.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
When a user describes an app that matches a known scenario, agents use the scenario manifest to determine:
|
|
8
|
+
- which Tide features are required
|
|
9
|
+
- which roles and policies to create
|
|
10
|
+
- who creates and approves them
|
|
11
|
+
- the correct playbook sequence
|
|
12
|
+
- what must happen before users can use the app
|
|
13
|
+
|
|
14
|
+
## How to use
|
|
15
|
+
|
|
16
|
+
1. When a user says "build me an app" or describes an app type, check this index.
|
|
17
|
+
2. If the description matches **one** scenario, read its `manifest.yaml` for the full pattern.
|
|
18
|
+
3. If the description matches **multiple** scenarios or is ambiguous, resolve the branch before proceeding. Use the discriminating questions in the table below. If unresolved, ask the user — do not silently pick one. (I-17)
|
|
19
|
+
4. Use the matched manifest to drive role creation, policy deployment, and playbook ordering.
|
|
20
|
+
5. If no scenario matches and the request is a standard auth/protection flow, fall back to `tide-setup` skill detection and playbook routing.
|
|
21
|
+
6. If no scenario matches and the request describes a **novel pattern** not covered by existing playbooks, route to `tide-solutions-architect` to explore safe implementation options within pack constraints.
|
|
22
|
+
|
|
23
|
+
## Disambiguation
|
|
24
|
+
|
|
25
|
+
If the request could map to multiple scenarios, resolve with these questions:
|
|
26
|
+
|
|
27
|
+
| Ambiguity | Discriminating question | If yes | If no |
|
|
28
|
+
|-----------|------------------------|--------|-------|
|
|
29
|
+
| Encryption vs signing | Does the app encrypt/decrypt data, or produce cryptographic signatures? | Encryption → check sharing/real-time. Signing → `policy-governed-signing`. | Neither → check governance or standard auth. |
|
|
30
|
+
| Self-encryption vs shared | Do other users need to decrypt the same ciphertext? | `organisation-password-manager` or `setup-forseti-e2ee` | Self-encryption via `tide-rbac-and-e2ee` or `encrypted-communication` |
|
|
31
|
+
| Real-time E2E vs stored data | Does the app need real-time encrypted communication (chat, video, audio) with external crypto? | `encrypted-communication` | `organisation-password-manager` or standard self-encryption |
|
|
32
|
+
| Vault/org app vs generic app | Is this a credential vault, password manager, or team-shared encrypted store? | `organisation-password-manager` | Check other scenarios or use standard playbooks |
|
|
33
|
+
| Governance vs end-user app | Does the app need a custom admin UI for approving IGA change requests? | `iga-admin-governance` | Standard auth playbooks |
|
|
34
|
+
| Admin panel vs built-in console | Is the built-in TideCloak Admin Console sufficient for governance? | Standard playbooks (no custom governance needed) | `iga-admin-governance` |
|
|
35
|
+
| Signing service vs direct signing | Does the app sign git commits/merges server-side after PR-based admin approval? | `git-pr-signing-service` | `policy-governed-signing` (direct in-app signing) |
|
|
36
|
+
| Single scenario vs no match | Does the description mention domain-specific keywords (SSH, vault, signing, admin, governance, chat, video, messaging)? | Use matched scenario | Use generic playbook routing |
|
|
37
|
+
|
|
38
|
+
## Scenarios
|
|
39
|
+
|
|
40
|
+
| ID | Scenario | Primary Tide capability | Default roles | Policy roles | When to use |
|
|
41
|
+
|----|----------|------------------------|---------------|-------------|-------------|
|
|
42
|
+
| `organisation-password-manager` | Shared credential vault for teams | Self-encryption + policy-governed encryption (Forseti) | `_tide_enabled`, `_tide_<tag>.selfencrypt`, `_tide_<tag>.selfdecrypt` | `appUser`, `orgOwner` | Password manager, credential vault, team vault, secret store |
|
|
43
|
+
| `policy-governed-signing` | Cryptographic signing via Forseti contracts on ORK network | Policy-governed threshold signing (Forseti) | `_tide_enabled` | Per-resource signing roles (e.g., `ssh:<user>`) | Keyless SSH, document signing, transaction signing, any policy-governed signing |
|
|
44
|
+
| `git-pr-signing-service` | Enterprise PR-based commit signing via GitHub webhooks | Policy-governed threshold signing (Forseti) + GitHub API | `_tide_enabled` | Per-branch signing roles (e.g., `git-sign:main`) | Git commit signing, verified commits, enterprise code signing, PR signing service |
|
|
45
|
+
| `iga-admin-governance` | Custom admin panel for IGA change request governance | IGA change-set lifecycle + multi-admin quorum | `_tide_enabled`, `tide-realm-admin` | None (governance, not data) | Admin dashboard, approval workflow, change request management, governance panel |
|
|
46
|
+
| `encrypted-communication` | Real-time encrypted chat/video with Tide-protected key storage | Self-encryption (key storage) + external crypto (runtime E2E) | `_tide_enabled`, `_tide_<tag>.selfencrypt`, `_tide_<tag>.selfdecrypt` | None | Encrypted chat, encrypted video calls, secure messaging, zero-knowledge server |
|
|
47
|
+
|
|
48
|
+
## Matching rules
|
|
49
|
+
|
|
50
|
+
- Match on app description keywords, not exact names.
|
|
51
|
+
- `organisation-password-manager` matches: "password manager", "credential vault", "shared secrets", "team vault", "org vault", "shared password store", "secret store", "vaultwarden", "bitwarden", "encrypted sharing", "share encrypted data", "cross-user decryption", "shared encryption", "encrypt and share", "chosen recipients", "selective sharing", "encrypt for others".
|
|
52
|
+
- `policy-governed-signing` matches: "keyless ssh", "ssh signing", "policy signing", "threshold signing", "document signing", "transaction signing", "forseti signing", "ork signing", "decentralised signing", "sign with tide".
|
|
53
|
+
- `git-pr-signing-service` matches: "git signing", "git commit signing", "commit signing", "signed commits", "verified commits", "github signing", "github verified", "pr signing", "merge signing", "sign commits", "sign merges", "code signing service", "enterprise commit signing", "verified badge", "git tag signing".
|
|
54
|
+
- `iga-admin-governance` matches: "admin panel", "admin dashboard", "governance panel", "governance dashboard", "change request", "approval workflow", "multi-admin", "quorum approval", "iga admin", "iga panel", "manage change requests", "approve changes", "admin console", "policy management panel".
|
|
55
|
+
- `encrypted-communication` matches: "encrypted chat", "encrypted messaging", "encrypted video", "e2e chat", "e2e messaging", "secure chat", "secure messaging", "private messaging", "zero knowledge", "encrypted group chat", "encrypted video call", "signal-like", "whatsapp-like", "chat app with encryption", "video app with encryption", "encrypted collaboration", "forward secrecy", "key exchange".
|
|
56
|
+
- If the user describes a **single-user private vault** with no sharing, do NOT use the password manager scenario. Use the `nextjs-e2ee-vault` template instead.
|
|
57
|
+
- If the user describes **only authentication** with no signing or encryption, do NOT use these scenarios. Use the standard playbook sequence.
|
|
58
|
+
- If the user only needs the **built-in TideCloak Admin Console** for governance, do NOT use the `iga-admin-governance` scenario. Use the standard `setup-iga-admin-panel` playbook directly.
|
|
59
|
+
|
|
60
|
+
## What a scenario defines
|
|
61
|
+
|
|
62
|
+
Each scenario directory contains:
|
|
63
|
+
|
|
64
|
+
| File | Purpose |
|
|
65
|
+
|------|---------|
|
|
66
|
+
| `scenario.md` | What the app is, what Tide features it needs |
|
|
67
|
+
| `manifest.yaml` | Machine-readable: roles, policies, playbook sequence, bootstrap requirements |
|
|
68
|
+
| `role-policy-matrix.md` | Which roles exist, what each policy checks, who approves |
|
|
69
|
+
| `bootstrap-sequence.md` | Step-by-step pre-user setup (admin-only, before app goes live) |
|
|
70
|
+
| `anti-patterns.md` | Scenario-specific mistakes |
|
|
71
|
+
|
|
72
|
+
## Key differences between scenarios
|
|
73
|
+
|
|
74
|
+
| Aspect | organisation-password-manager | policy-governed-signing | git-pr-signing-service | iga-admin-governance | encrypted-communication |
|
|
75
|
+
|--------|------------------------------|------------------------|------------------------|---------------------|------------------------|
|
|
76
|
+
| Primary operation | Encrypt/decrypt data | Sign data | Sign git merge commits | Approve/commit admin changes | Real-time encrypted communication |
|
|
77
|
+
| Encryption | Self-encryption + policy-governed | None | None | None | Self-encryption (key storage) + external crypto (runtime) |
|
|
78
|
+
| Signing | None | Forseti contract-authorized threshold signing | Forseti contract-authorized threshold signing (git commits) | Enclave signing for change-set approval | None |
|
|
79
|
+
| Org-scoped roles | Yes (`org:{uuid}:{role}`) | No | No | No | No |
|
|
80
|
+
| E2EE roles | Yes (`_tide_<tag>.selfencrypt/selfdecrypt`) | No | No | No | Yes (`_tide_<tag>.selfencrypt/selfdecrypt`) |
|
|
81
|
+
| Forseti contracts | Validate org scope + VVK signatures | Validate data + authorize signer | Validate commit content + authorize approvers + verify service | Optional: manage role-policy attachments | None |
|
|
82
|
+
| External integration | No | No | Yes (GitHub webhooks, Checks API, Git Data API) | No | No |
|
|
83
|
+
| External crypto library | No | No | No (SSH wire format wrapping only) | No | Yes (libsodium, WebCrypto, etc.) |
|
|
84
|
+
| IGA required | Yes | Yes | Yes | Yes (core purpose) | Yes |
|
|
85
|
+
| Admin pre-approval | Yes (appUser, orgOwner policies) | Yes (signing policies per role) | Yes (signing policies per branch) | Yes (quorum for all admin mutations) | Yes (E2E roles) |
|
|
86
|
+
| Min admins | 1 | 1 | 2 (signing quorum) | 2 (for quorum governance) | 1 |
|
|
87
|
+
| Developer interacts with Tide | Yes | Yes | No (developers push code normally) | Yes | Yes |
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Encrypted Communication — Anti-Patterns
|
|
2
|
+
|
|
3
|
+
Scenario-specific mistakes for encrypted communication apps using the hybrid Tide + external crypto pattern. General Tide anti-patterns (I-01 through I-16, AP-01 through AP-42) still apply.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## AP-EC01: Storing private keys in plaintext
|
|
8
|
+
|
|
9
|
+
**Mistake**: Storing the user's private key in the database without Tide encryption, or storing it in localStorage/sessionStorage.
|
|
10
|
+
|
|
11
|
+
**Why it fails**: The private key is the root of trust for all E2E operations. If stored in plaintext, a database breach or XSS attack exposes all encrypted communications.
|
|
12
|
+
|
|
13
|
+
**Fix**: Always encrypt the private key with `doEncrypt(privateKeyBytes, tag)` before storing. Always decrypt with `doDecrypt()` on login. Never persist the decrypted private key outside of in-memory variables.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## AP-EC02: Generating keys server-side
|
|
18
|
+
|
|
19
|
+
**Mistake**: Generating the user's asymmetric keypair on the server and sending the private key to the client.
|
|
20
|
+
|
|
21
|
+
**Why it fails**: The server sees the private key in plaintext, breaking the zero-knowledge property. The private key transits over the network unprotected by Tide.
|
|
22
|
+
|
|
23
|
+
**Fix**: Generate keypairs client-side only. The server never sees the private key. It only stores the Tide-encrypted ciphertext.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## AP-EC03: Using Tide doEncrypt/doDecrypt for runtime message encryption
|
|
28
|
+
|
|
29
|
+
**Mistake**: Encrypting every chat message or media frame with Tide's `doEncrypt()`.
|
|
30
|
+
|
|
31
|
+
**Why it fails**: Tide self-encryption is identity-bound (only the encrypting user can decrypt). Other participants cannot decrypt messages encrypted this way. Also, `doEncrypt()` requires a round-trip to the ORK network, making it too slow for real-time frame-by-frame media encryption.
|
|
32
|
+
|
|
33
|
+
**Fix**: Use Tide self-encryption only for private key storage (once per login). Use an external crypto library for runtime E2E operations (message encryption, frame encryption, key exchange).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## AP-EC04: Skipping key material API protection
|
|
38
|
+
|
|
39
|
+
**Mistake**: Serving the encrypted key material endpoint without JWT verification. Anyone can fetch any user's encrypted private key.
|
|
40
|
+
|
|
41
|
+
**Why it fails**: While the encrypted private key cannot be decrypted without Tide (identity-bound), exposing it unnecessarily increases attack surface. The public key endpoint may also leak presence information.
|
|
42
|
+
|
|
43
|
+
**Fix**: Protect key material API endpoints with server-side JWT verification. Only serve a user's own encrypted private key to that authenticated user. Public keys may be more broadly accessible but still require authentication.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## AP-EC05: Trusting client-supplied public keys without verification
|
|
48
|
+
|
|
49
|
+
**Mistake**: Accepting a user's public key from the client without verifying it corresponds to the encrypted private key.
|
|
50
|
+
|
|
51
|
+
**Why it fails**: An attacker could submit a different public key, enabling a key substitution attack. Other users would encrypt messages to the attacker's key instead of the real user's key.
|
|
52
|
+
|
|
53
|
+
**Fix**: Verify the public key matches the private key on the client side before storing. For libsodium Ed25519, derive the public key from the private key and compare. The server cannot verify this (it never sees the private key), so the client must do it.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## AP-EC06: No key rotation on membership change
|
|
58
|
+
|
|
59
|
+
**Mistake**: Using the same conversation key after a member leaves a group chat or call.
|
|
60
|
+
|
|
61
|
+
**Why it fails**: The departed member still has the conversation key and can decrypt future messages if they intercept the ciphertext.
|
|
62
|
+
|
|
63
|
+
**Fix**: Rotate the conversation key when any member leaves. Generate a new key and distribute it (encrypted) to remaining members only. Store key history with version numbers so old messages remain decryptable with the old key.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## AP-EC07: Encrypting conversation keys with Tide instead of public-key crypto
|
|
68
|
+
|
|
69
|
+
**Mistake**: Using `doEncrypt()` to encrypt conversation keys for distribution to other users.
|
|
70
|
+
|
|
71
|
+
**Why it fails**: Tide self-encryption is identity-bound. If user A encrypts the conversation key with `doEncrypt()`, only user A can decrypt it. User B cannot decrypt it.
|
|
72
|
+
|
|
73
|
+
**Fix**: Use the external crypto library's public-key encryption for key distribution. Encrypt the conversation key with each recipient's public key individually.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## AP-EC08: Treating ProtectedRoute as API protection
|
|
78
|
+
|
|
79
|
+
**Mistake**: Using a client-side `ProtectedRoute` component as the only access control for encrypted data.
|
|
80
|
+
|
|
81
|
+
**Why it fails**: Client-side route guards are UI gating only. The API endpoints serving key material and encrypted messages are still accessible via direct HTTP requests.
|
|
82
|
+
|
|
83
|
+
**Fix**: Every API endpoint that serves or accepts encrypted data must have server-side JWT verification. The `ProtectedRoute` component is a UX convenience, not a security boundary.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## AP-EC09: Hardcoding the encryption tag
|
|
88
|
+
|
|
89
|
+
**Mistake**: Using a hardcoded tag string (e.g., `"self"`) without ensuring matching roles exist in the realm.
|
|
90
|
+
|
|
91
|
+
**Why it fails**: `doEncrypt(data, "chat")` requires the user to have `_tide_chat.selfencrypt`. If the realm only has `_tide_self.selfencrypt`, the operation fails with "User has not been given any access to 'chat'".
|
|
92
|
+
|
|
93
|
+
**Fix**: Choose one tag name. Ensure the realm template includes matching `_tide_<tag>.selfencrypt` and `_tide_<tag>.selfdecrypt` roles. Document the tag name. Do not change it after users have encrypted data with it.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## AP-EC10: Initializing crypto before TideCloak auth completes
|
|
98
|
+
|
|
99
|
+
**Mistake**: Calling `doEncrypt()` or `doDecrypt()` before the TideCloak provider has finished initializing and the user is authenticated.
|
|
100
|
+
|
|
101
|
+
**Why it fails**: The SDK requires an active session with valid doken. Calling encryption methods before auth completes throws errors or returns undefined.
|
|
102
|
+
|
|
103
|
+
**Fix**: Wait for `isAuthenticated === true` before attempting any Tide encryption operations. Gate the key initialization flow on authentication completion.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## AP-EC11: Losing the decrypted private key on token refresh
|
|
108
|
+
|
|
109
|
+
**Mistake**: Clearing the in-memory private key state when the TideCloak token refreshes.
|
|
110
|
+
|
|
111
|
+
**Why it fails**: Token refresh is a normal background operation. Clearing crypto state forces the user to re-decrypt their private key, which may briefly break active E2E sessions.
|
|
112
|
+
|
|
113
|
+
**Fix**: Keep the decrypted private key in a React ref or module-level variable that survives re-renders and token refreshes. Only clear it on explicit logout.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## AP-EC12: Using `@tidecloak/react` version 0.99.x
|
|
118
|
+
|
|
119
|
+
**Mistake**: Installing a 0.99.x pre-release version of the Tide SDK.
|
|
120
|
+
|
|
121
|
+
**Why it fails**: 0.99.x versions are unstable pre-releases. The stable version is 0.13.26 (per pack version policy).
|
|
122
|
+
|
|
123
|
+
**Fix**: Pin `@tidecloak/react` to `0.13.26`. See `canon/version-policy.md`.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Encrypted Communication — Bootstrap Sequence
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Phase 1: TideCloak Infrastructure
|
|
6
|
+
|
|
7
|
+
1. Start TideCloak container:
|
|
8
|
+
```bash
|
|
9
|
+
docker run -d --name tidecloak -p 8080:8080 tideorg/tidecloak-dev:latest
|
|
10
|
+
```
|
|
11
|
+
Wait until `http://localhost:8080` responds.
|
|
12
|
+
|
|
13
|
+
2. Obtain master admin token:
|
|
14
|
+
```bash
|
|
15
|
+
TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
|
|
16
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
17
|
+
-d "grant_type=password&client_id=admin-cli&username=admin&password=password" \
|
|
18
|
+
| jq -r '.access_token')
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Phase 2: Realm Setup
|
|
24
|
+
|
|
25
|
+
3. Create realm from template.
|
|
26
|
+
|
|
27
|
+
The realm template must include:
|
|
28
|
+
- `_tide_enabled` in default composite role
|
|
29
|
+
- `_tide_<tag>.selfencrypt` and `_tide_<tag>.selfdecrypt` in default composite role
|
|
30
|
+
- `tidebrowser` authentication flow
|
|
31
|
+
- `link-tide-account-action` required action
|
|
32
|
+
- Tide protocol mappers (`tideUserKey`, `vuid`)
|
|
33
|
+
- `UPDATE_PASSWORD` required action disabled
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -s -X POST "http://localhost:8080/admin/realms" \
|
|
37
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
38
|
+
-H "Content-Type: application/json" \
|
|
39
|
+
-d @realm.json
|
|
40
|
+
```
|
|
41
|
+
**Fail-fast**: Check HTTP status. Abort on non-2xx.
|
|
42
|
+
|
|
43
|
+
4. License realm:
|
|
44
|
+
```bash
|
|
45
|
+
curl -s -X POST "http://localhost:8080/admin/realms/$REALM/vendorResources/setUpTideRealm" \
|
|
46
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
47
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
48
|
+
-d "email=admin@example.com"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
5. Enable IGA:
|
|
52
|
+
```bash
|
|
53
|
+
curl -s -X POST "http://localhost:8080/admin/realms/$REALM/tide-admin/toggle-iga" \
|
|
54
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
55
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
56
|
+
-d "isIGAEnabled=true"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
6. Approve and commit initial client change request.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Phase 3: Admin User
|
|
64
|
+
|
|
65
|
+
7. Create admin user (include `firstName` and `lastName` to avoid `VERIFY_PROFILE` block).
|
|
66
|
+
|
|
67
|
+
8. Approve and commit user creation change request.
|
|
68
|
+
|
|
69
|
+
9. Assign `tide-realm-admin` (client role on `realm-management`).
|
|
70
|
+
|
|
71
|
+
10. Approve and commit role assignment change request.
|
|
72
|
+
|
|
73
|
+
11. Generate invite link (`lifespan=43200`). Admin completes Tide account linking.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Phase 4: IDP Settings and Adapter Export
|
|
78
|
+
|
|
79
|
+
12. Sign IDP settings:
|
|
80
|
+
```bash
|
|
81
|
+
curl -s -X POST "http://localhost:8080/admin/realms/$REALM/vendorResources/sign-idp-settings" \
|
|
82
|
+
-H "Authorization: Bearer $TOKEN"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
13. Export adapter JSON:
|
|
86
|
+
```bash
|
|
87
|
+
curl -s "http://localhost:8080/admin/realms/$REALM/vendorResources/get-installations-provider?clientId=$CLIENT_UUID&providerId=keycloak-oidc-keycloak-json" \
|
|
88
|
+
-H "Authorization: Bearer $TOKEN" > tidecloak.json
|
|
89
|
+
```
|
|
90
|
+
Verify `jwk` field is present.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Phase 5: App Wiring
|
|
95
|
+
|
|
96
|
+
14. Install SDK (`@tidecloak/react` or `@tidecloak/nextjs`).
|
|
97
|
+
|
|
98
|
+
15. Wire TideCloak provider with adapter JSON config.
|
|
99
|
+
|
|
100
|
+
16. Create redirect handler at configured `redirectUri` path.
|
|
101
|
+
|
|
102
|
+
17. Add `public/silent-check-sso.html`.
|
|
103
|
+
|
|
104
|
+
18. Set CSP `frame-src '*'` in server config or framework config.
|
|
105
|
+
|
|
106
|
+
19. Implement server-side JWT verification for API routes that handle key material.
|
|
107
|
+
|
|
108
|
+
20. Create API endpoint for storing/retrieving encrypted key material (protected by JWT verification).
|
|
109
|
+
|
|
110
|
+
21. Create database table for user keys (user_id, public_key, encrypted_private_key).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Phase 6: Client-Side Key Management
|
|
115
|
+
|
|
116
|
+
22. Install external crypto library (e.g., `libsodium-wrappers`).
|
|
117
|
+
|
|
118
|
+
23. Implement first-login flow:
|
|
119
|
+
- Generate keypair client-side
|
|
120
|
+
- Encrypt private key with `doEncrypt(privateKeyBytes, tag)`
|
|
121
|
+
- Store encrypted private key + public key via protected API
|
|
122
|
+
|
|
123
|
+
24. Implement returning-login flow:
|
|
124
|
+
- Fetch encrypted key material via protected API
|
|
125
|
+
- Decrypt private key with `doDecrypt(encryptedPrivateKey, tag)`
|
|
126
|
+
- Private key available in memory for runtime E2E operations
|
|
127
|
+
|
|
128
|
+
25. Implement runtime E2E operations using the external crypto library (not Tide).
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Pre-User Checklist
|
|
133
|
+
|
|
134
|
+
Before any user accesses the app:
|
|
135
|
+
|
|
136
|
+
- [ ] TideCloak running and reachable
|
|
137
|
+
- [ ] Realm created with self-encryption roles in default composite
|
|
138
|
+
- [ ] Realm licensed (`setUpTideRealm`)
|
|
139
|
+
- [ ] IGA enabled (`toggle-iga`)
|
|
140
|
+
- [ ] At least one admin user with `tide-realm-admin` linked
|
|
141
|
+
- [ ] All initial change requests approved and committed
|
|
142
|
+
- [ ] IDP settings signed (`sign-idp-settings`)
|
|
143
|
+
- [ ] Adapter JSON exported with `jwk` field present
|
|
144
|
+
- [ ] App has TideCloak provider wired with config
|
|
145
|
+
- [ ] Redirect handler exists at configured path
|
|
146
|
+
- [ ] `silent-check-sso.html` in public directory
|
|
147
|
+
- [ ] CSP includes `frame-src '*'`
|
|
148
|
+
- [ ] Server-side JWT verification on key material API endpoints
|
|
149
|
+
- [ ] Database table for encrypted key storage exists
|
|
150
|
+
- [ ] External crypto library installed and initialized
|
|
151
|
+
- [ ] First-login keypair generation flow tested
|
|
152
|
+
- [ ] Returning-login key decryption flow tested
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Encrypted Real-Time Communication — Scenario Manifest
|
|
2
|
+
|
|
3
|
+
id: encrypted-communication
|
|
4
|
+
title: Encrypted Real-Time Communication
|
|
5
|
+
category: e2e-communication
|
|
6
|
+
description: Chat or video app with Tide-protected private keys and external crypto for runtime E2E encryption
|
|
7
|
+
|
|
8
|
+
frameworks:
|
|
9
|
+
- nextjs
|
|
10
|
+
- react-vite
|
|
11
|
+
- vanilla
|
|
12
|
+
|
|
13
|
+
core_patterns:
|
|
14
|
+
- self-encryption
|
|
15
|
+
- self-decryption
|
|
16
|
+
- oidc
|
|
17
|
+
- iga
|
|
18
|
+
- dpop
|
|
19
|
+
- e2e
|
|
20
|
+
- encrypted-chat
|
|
21
|
+
- encrypted-video
|
|
22
|
+
- encrypted-messaging
|
|
23
|
+
- key-storage
|
|
24
|
+
- key-exchange
|
|
25
|
+
- forward-secrecy
|
|
26
|
+
- zero-knowledge-server
|
|
27
|
+
- hybrid-encryption
|
|
28
|
+
- private-key-protection
|
|
29
|
+
- real-time
|
|
30
|
+
- websocket
|
|
31
|
+
- webrtc
|
|
32
|
+
|
|
33
|
+
match_keywords:
|
|
34
|
+
- encrypted chat
|
|
35
|
+
- encrypted messaging
|
|
36
|
+
- encrypted video
|
|
37
|
+
- encrypted calls
|
|
38
|
+
- encrypted communication
|
|
39
|
+
- e2e chat
|
|
40
|
+
- e2e messaging
|
|
41
|
+
- e2e video
|
|
42
|
+
- e2e communication
|
|
43
|
+
- end to end encrypted
|
|
44
|
+
- end-to-end encrypted
|
|
45
|
+
- secure chat
|
|
46
|
+
- secure messaging
|
|
47
|
+
- secure video
|
|
48
|
+
- private messaging
|
|
49
|
+
- private chat
|
|
50
|
+
- zero knowledge
|
|
51
|
+
- zero-knowledge
|
|
52
|
+
- real-time encryption
|
|
53
|
+
- encrypted group chat
|
|
54
|
+
- encrypted video call
|
|
55
|
+
- signal-like
|
|
56
|
+
- whatsapp-like
|
|
57
|
+
- chat app with encryption
|
|
58
|
+
- video app with encryption
|
|
59
|
+
- tidetalk
|
|
60
|
+
- encrypted collaboration
|
|
61
|
+
- forward secrecy
|
|
62
|
+
- key exchange
|
|
63
|
+
- encryption and decryption
|
|
64
|
+
- encrypt decrypt share
|
|
65
|
+
- encryption with sharing
|
|
66
|
+
- encrypt and share
|
|
67
|
+
- shared encryption
|
|
68
|
+
|
|
69
|
+
bootstrap_required: true
|
|
70
|
+
requires_admin_preapproval: true
|
|
71
|
+
requires_iga: true
|
|
72
|
+
requires_e2ee: true
|
|
73
|
+
encryption_mode: self-encryption
|
|
74
|
+
|
|
75
|
+
default_roles:
|
|
76
|
+
- _tide_enabled
|
|
77
|
+
- _tide_<tag>.selfencrypt
|
|
78
|
+
- _tide_<tag>.selfdecrypt
|
|
79
|
+
|
|
80
|
+
default_playbooks:
|
|
81
|
+
- start-tidecloak-dev
|
|
82
|
+
- bootstrap-realm-from-template
|
|
83
|
+
- initialize-admin-and-link-account
|
|
84
|
+
- configure-e2ee-roles-and-policies
|
|
85
|
+
- add-auth-nextjs-fresh
|
|
86
|
+
- protect-routes-nextjs
|
|
87
|
+
- protect-api-nextjs
|
|
88
|
+
- verify-jwt-server-side
|
|
89
|
+
- add-rbac-nextjs
|
|
90
|
+
|
|
91
|
+
default_skills:
|
|
92
|
+
- tide-setup
|
|
93
|
+
- tide-integration
|
|
94
|
+
- tide-route-and-api-protection
|
|
95
|
+
- tide-rbac-and-e2ee
|
|
96
|
+
|
|
97
|
+
discriminating_question: Does the app need real-time encrypted communication (chat, video, or audio) where the server never sees plaintext? If the app only stores encrypted data without real-time streaming, use organisation-password-manager instead.
|
|
98
|
+
valid_only_when: App requires runtime E2E encryption for real-time content (messages, media frames) using external crypto, with Tide protecting the private key material at rest.
|
|
99
|
+
|
|
100
|
+
notes: Two encryption layers. Layer 1 is Tide self-encryption for private key storage. Layer 2 is an external client-side crypto library for runtime E2E. Tide does not handle Layer 2 directly. Replace <tag> with app-specific tag name.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Encrypted Communication — Role & Policy Matrix
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Tide Bootstrap Roles
|
|
6
|
+
|
|
7
|
+
| Role | Type | Purpose | Created by | Approved by | Required before first use | Default / optional | Notes |
|
|
8
|
+
|------|------|---------|-----------|-------------|--------------------------|-------------------|-------|
|
|
9
|
+
| `_tide_enabled` | Realm | Enables Tide voucher system | Realm template | Auto (in default composite) | Yes | Default | Must be in `default-roles-{realm}` composite |
|
|
10
|
+
| `_tide_<tag>.selfencrypt` | Realm | Allows user to encrypt their private key with Tide | Realm template | IGA quorum (if added after import) | Yes | Default | Replace `<tag>` with app-specific name (e.g., `keys`, `chat`) |
|
|
11
|
+
| `_tide_<tag>.selfdecrypt` | Realm | Allows user to decrypt their private key with Tide | Realm template | IGA quorum (if added after import) | Yes | Default | Must pair with selfencrypt for round-trip |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Application Roles (optional, app-defined)
|
|
16
|
+
|
|
17
|
+
This scenario does not require app-specific roles beyond the Tide bootstrap roles. The app may add its own roles for business logic (e.g., `admin`, `moderator`, `premium-user`), but these are separate from the encryption mechanism.
|
|
18
|
+
|
|
19
|
+
| Example role | Type | Purpose | Notes |
|
|
20
|
+
|-------------|------|---------|-------|
|
|
21
|
+
| `admin` | Realm | App admin (not Tide admin) | Optional. For app-level moderation, not IGA governance. |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Key rules
|
|
26
|
+
|
|
27
|
+
1. **Self-encryption is identity-bound.** Only the user who called `doEncrypt` can call `doDecrypt` on the same ciphertext. This is the correct behavior for private key storage — no other user should be able to decrypt your private key.
|
|
28
|
+
|
|
29
|
+
2. **Both selfencrypt AND selfdecrypt roles are needed.** A user without `selfdecrypt` can store their encrypted key but cannot retrieve it on next login.
|
|
30
|
+
|
|
31
|
+
3. **Roles must be in the default composite.** New users get these roles automatically. If roles are missing from the composite, users must be assigned individually and the assignment must go through IGA.
|
|
32
|
+
|
|
33
|
+
4. **One tag is sufficient for this scenario.** Unlike the password manager scenario, this pattern does not need multiple tags or policy-governed encryption. A single tag (e.g., `keys`) covers the private key protection use case.
|
|
34
|
+
|
|
35
|
+
5. **No Forseti contracts needed.** Self-encryption does not require policy bytes, contracts, or VVK shared encryption. The Tide SDK handles it directly via `doEncrypt(data, tag)` / `doDecrypt(data, tag)`.
|
|
36
|
+
|
|
37
|
+
6. **No policy-governed encryption roles needed** (self-encryption variant only). The runtime E2E encryption uses an external crypto library, not Tide's policy-governed path.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Shared Encryption Variant
|
|
42
|
+
|
|
43
|
+
If the app requires **Tide-native shared encryption** (recipients decrypt via `IAMService.doDecrypt` with policy bytes, not external crypto), the self-encryption path above is insufficient. You need:
|
|
44
|
+
|
|
45
|
+
1. **Additional roles**: A Forseti contract role (e.g., `shared-data-access`) that the contract checks for authorization
|
|
46
|
+
2. **Voucher gate roles**: `_tide_<tag>.selfencrypt` / `_tide_<tag>.selfdecrypt` still needed for the voucher system
|
|
47
|
+
3. **Forseti contract**: Deployed via `PolicySignRequest.addForsetiContractToUpload()` during the admin signing ceremony
|
|
48
|
+
4. **Playbook**: Add `setup-forseti-e2ee` to the playbook sequence BEFORE `add-rbac-nextjs`
|
|
49
|
+
|
|
50
|
+
See `setup-forseti-e2ee` playbook for the complete Forseti policy signing flow. VERIFIED (LEARNINGS-batch-009 L-03, L-06).
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## What Tide does NOT handle in this scenario
|
|
55
|
+
|
|
56
|
+
| Concern | Handled by | Notes |
|
|
57
|
+
|---------|-----------|-------|
|
|
58
|
+
| Runtime message encryption | External crypto library (e.g., libsodium `crypto_secretbox`) | Tide has no involvement |
|
|
59
|
+
| Media frame encryption | External crypto library (e.g., libsodium with WebRTC Insertable Streams) | Tide has no involvement |
|
|
60
|
+
| Key exchange between users | External crypto library (e.g., libsodium `crypto_scalarmult`) | Uses public keys stored in DB |
|
|
61
|
+
| Conversation key generation | External crypto library | App generates symmetric keys for group chats |
|
|
62
|
+
| Key rotation / forward secrecy | App logic | Re-encrypt conversation key for remaining members when membership changes |
|
|
63
|
+
| Public key distribution | App server (stores public keys in database) | Server sees public keys, never private keys |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Database Schema (minimum required)
|
|
68
|
+
|
|
69
|
+
The app must store encrypted key material. Minimum columns:
|
|
70
|
+
|
|
71
|
+
| Column | Type | Purpose |
|
|
72
|
+
|--------|------|---------|
|
|
73
|
+
| `user_id` | string | TideCloak user ID (from JWT `sub` claim) |
|
|
74
|
+
| `public_key` | text | User's public key (plaintext, for key exchange) |
|
|
75
|
+
| `encrypted_private_key` | text | User's private key encrypted by Tide self-encryption |
|
|
76
|
+
| `created_at` | timestamp | When keys were generated |
|
|
77
|
+
|
|
78
|
+
The encrypted_private_key column stores the base64 output of `doEncrypt()`. Size depends on the key type. For Ed25519 (32-byte private key): approximately 400 characters base64 after Tide encryption overhead.
|
|
79
|
+
|
|
80
|
+
Optional extensions: key version, key history table (for forward secrecy), per-room conversation keys.
|