anhedral 0.4.2 → 0.6.1

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 (89) hide show
  1. package/README.md +35 -265
  2. package/assets/anhedral-cli-init-technical.svg +322 -0
  3. package/assets/anhedral-cli-init.svg +257 -173
  4. package/dist/bin.js +28 -191
  5. package/dist/dependencies.d.ts +7 -57
  6. package/dist/dependencies.js +96 -308
  7. package/dist/index.d.ts +2 -7
  8. package/dist/index.js +1 -4
  9. package/dist/platforms/desktop.js +401 -0
  10. package/dist/platforms/extension.js +401 -0
  11. package/dist/platforms/mobile.js +232 -0
  12. package/dist/project.d.ts +0 -26
  13. package/dist/standard-products.d.ts +2 -0
  14. package/dist/standard-products.js +6 -0
  15. package/dist/standard.d.ts +18 -0
  16. package/dist/standard.js +595 -0
  17. package/dist/version.d.ts +1 -1
  18. package/dist/version.js +1 -1
  19. package/docs/application-stack-standard.md +700 -0
  20. package/package.json +29 -51
  21. package/CHANGELOG.md +0 -178
  22. package/CONTRIBUTING.md +0 -77
  23. package/SECURITY.md +0 -37
  24. package/SKILL.md +0 -339
  25. package/assets/images/svg/ably-symbol-color.svg +0 -8
  26. package/bin/anhedral.js +0 -2
  27. package/dist/architecture/contributions.d.ts +0 -28
  28. package/dist/architecture/contributions.js +0 -125
  29. package/dist/architecture/index.d.ts +0 -12
  30. package/dist/architecture/index.js +0 -6
  31. package/dist/architecture/manifest.d.ts +0 -53
  32. package/dist/architecture/manifest.js +0 -283
  33. package/dist/architecture/modules.d.ts +0 -41
  34. package/dist/architecture/modules.js +0 -187
  35. package/dist/architecture/plan.d.ts +0 -58
  36. package/dist/architecture/plan.js +0 -145
  37. package/dist/architecture/templates.d.ts +0 -10
  38. package/dist/architecture/templates.js +0 -23
  39. package/dist/cli.d.ts +0 -32
  40. package/dist/cli.js +0 -281
  41. package/dist/composers/root.d.ts +0 -6
  42. package/dist/composers/root.js +0 -35
  43. package/dist/print.d.ts +0 -8
  44. package/dist/print.js +0 -48
  45. package/dist/prompts.d.ts +0 -6
  46. package/dist/prompts.js +0 -41
  47. package/dist/scaffold.d.ts +0 -64
  48. package/dist/scaffold.js +0 -2102
  49. package/dist/template-source.d.ts +0 -20
  50. package/dist/template-source.js +0 -180
  51. package/dist/templates/api.d.ts +0 -2
  52. package/dist/templates/api.js +0 -3187
  53. package/dist/templates/assets-private-proxy.d.ts +0 -3
  54. package/dist/templates/assets-private-proxy.js +0 -277
  55. package/dist/templates/desktop.js +0 -792
  56. package/dist/templates/electron-updater.d.ts +0 -3
  57. package/dist/templates/electron-updater.js +0 -358
  58. package/dist/templates/extension.js +0 -856
  59. package/dist/templates/first-run.d.ts +0 -2
  60. package/dist/templates/first-run.js +0 -167
  61. package/dist/templates/mobile.js +0 -861
  62. package/dist/templates/shared.d.ts +0 -2
  63. package/dist/templates/shared.js +0 -577
  64. package/dist/templates/web.d.ts +0 -2
  65. package/dist/templates/web.js +0 -569
  66. package/dist/toolchain.d.ts +0 -3
  67. package/dist/toolchain.js +0 -8
  68. package/dist/ui.d.ts +0 -33
  69. package/dist/ui.js +0 -199
  70. package/dist/workspace-config.d.ts +0 -11
  71. package/dist/workspace-config.js +0 -372
  72. package/docs/NORTHSTAR.md +0 -114
  73. package/docs/conventions.md +0 -94
  74. package/docs/engineering-principles.md +0 -88
  75. package/docs/master-stack-map.md +0 -199
  76. package/docs/output-tree-contract.md +0 -493
  77. package/docs/references/manual-scaffolding.md +0 -421
  78. package/docs/references/provisioning.md +0 -577
  79. package/scripts/render-master-stack-map.mjs +0 -510
  80. package/templates/api-fastify/apps/api/tsconfig.json +0 -20
  81. package/templates/catalog.json +0 -23
  82. package/templates/db-drizzle/packages/db/tsconfig.json +0 -14
  83. package/templates/desktop-electron/apps/desktop/tsconfig.json +0 -37
  84. package/templates/extension-wxt/apps/extension/tsconfig.json +0 -41
  85. package/templates/mobile-expo/apps/mobile/expo-env.d.ts +0 -1
  86. package/templates/web-next/apps/web/next-env.d.ts +0 -6
  87. /package/dist/{templates → platforms}/desktop.d.ts +0 -0
  88. /package/dist/{templates → platforms}/extension.d.ts +0 -0
  89. /package/dist/{templates → platforms}/mobile.d.ts +0 -0
@@ -1,421 +0,0 @@
1
- # Manual Anhedral scaffolding
2
-
3
- Use this procedure to build an Anhedral-style workspace without invoking the Anhedral CLI or importing its generator. Complete the phases in order. Do not stop after framework initialization: shared contracts, integration wiring, tests, root scripts, environment examples, CI, and deployment files are part of the scaffold.
4
-
5
- ## Contents
6
-
7
- 1. Record inputs and resolve modules
8
- 2. Establish the workspace contract
9
- 3. Create root configuration
10
- 4. Create shared packages
11
- 5. Create the API
12
- 6. Create client surfaces
13
- 7. Wire optional features
14
- 8. Configure environments and providers
15
- 9. Install, migrate, and verify
16
- 10. Audit completeness
17
- 11. Work from the Anhedral source repository
18
-
19
- ## 1. Record inputs and resolve modules
20
-
21
- Record these values before editing:
22
-
23
- - `projectName`: a valid npm package name; use it for the root package and child package prefixes.
24
- - `displayName`: human-readable product name; escape it for every JSON, JavaScript, HTML, and Markdown context.
25
- - requested app surfaces: `web`, `mobile`, `api`, `desktop`, `extension`.
26
- - requested features: `db`, `auth`, `billing`, `storage`, `native-subscriptions`, `electron-updater`.
27
- - toolchain policy: exact tested versions for reproducibility, or current compatible versions when the user explicitly wants upgrades.
28
-
29
- Resolve the transitive closure before writing files:
30
-
31
- ```text
32
- auth -> api + db
33
- billing -> auth -> api + db
34
- storage -> auth -> api + db
35
- native-subscriptions -> mobile + billing -> auth + api + db
36
- electron-updater -> desktop
37
- ```
38
-
39
- If no modules were specified, select all five surfaces and all six features. Never create a feature without its prerequisites. Keep normalized order `web, mobile, api, desktop, extension, db, auth, billing, storage, native-subscriptions, electron-updater` when displaying or recording a plan.
40
-
41
- Choose Node `^20.19.0 || >=22.12.0` unless mobile is selected. Expo mobile requires `^22.13.0 || ^24.3.0 || >=25`. Use one root pnpm workspace and one root lockfile; never leave nested lockfiles, workspaces, `.git` directories, or `node_modules` inside apps.
42
-
43
- Use this stable baseline when current source is unavailable. It was verified on 2026-07-15; keep each version exact and update the baseline as one tested set rather than upgrading packages independently:
44
-
45
- ```text
46
- pnpm 10.34.5; turbo 2.9.14; TypeScript 5.9.3; @types/node 20.19.43
47
- contracts: zod 4.2.1
48
- db: @neondatabase/serverless 1.0.2, drizzle-orm 0.45.2,
49
- drizzle-kit 0.31.7, dotenv 17.2.3, tsx 4.20.6
50
- api: fastify 5.8.5, @fastify/cors 11.1.0, @fastify/compress 8.3.0,
51
- @fastify/helmet 13.0.2, @fastify/rate-limit 10.3.0,
52
- @clerk/fastify 3.1.51, @aws-sdk/client-s3 3.1047.0,
53
- @aws-sdk/s3-request-presigner 3.1047.0, ably 2.24.0, vitest 4.1.0
54
- web: next 16.2.11, react/react-dom 19.2.3, @clerk/nextjs 7.5.18,
55
- @clerk/ui 1.25.3, tailwindcss and @tailwindcss/postcss 4.1.18
56
- mobile: expo 56.0.16, expo-router 56.2.15, react-native 0.85.3,
57
- @clerk/expo 3.7.5, expo-secure-store 56.0.4,
58
- react-native-purchases/react-native-purchases-ui 10.4.2
59
- desktop: electron 43.1.1, electron-builder 26.15.3, vite 7.3.6,
60
- @vitejs/plugin-react 5.2.0, @clerk/clerk-js 6.25.3,
61
- electron-updater 6.8.9 when electron-updater is selected
62
- extension: wxt 0.20.27, @wxt-dev/module-react 1.2.2,
63
- @clerk/chrome-extension 3.1.52, tailwindcss 3.4.19
64
- shared UI: clsx 2.1.1, tailwind-merge 3.4.0
65
- ```
66
-
67
- Expo's package uses TypeScript 6.0.3 and its SDK-compatible React Native support packages from `src/dependencies.ts`; do not force the root TypeScript version into mobile.
68
-
69
- ## 2. Establish the workspace contract
70
-
71
- Create only selected branches of this tree. A bracketed condition makes the file required exactly when true.
72
-
73
- ```text
74
- <root>/
75
- .env.example
76
- .github/workflows/anhedral-ci.yml
77
- .gitignore
78
- .vercelignore
79
- README.md
80
- PRODUCTION.md
81
- package.json
82
- pnpm-workspace.yaml
83
- turbo.json
84
- vercel.json
85
- scripts/verify-db-migrations.mjs [db]
86
- packages/contracts/{package.json,tsconfig.json,src/index.ts} [api]
87
- packages/api-client/{package.json,tsconfig.json,src/index.ts} [api + any client]
88
- packages/realtime/{package.json,tsconfig.json,src/index.ts} [billing + any client]
89
- packages/db/{package.json,tsconfig.json,drizzle.config.ts,.env.example} [db]
90
- packages/db/src/{index.ts,migrate.ts,schema.ts} [db]
91
- packages/db/migrations/.gitkeep [db]
92
- apps/api/{package.json,tsconfig.json,vitest.config.ts,.env.example,.gitignore} [api]
93
- apps/api/src/{application.ts,env.ts,index.ts,routes.ts} [api]
94
- apps/api/src/auth.ts [auth]
95
- apps/api/src/billing.ts [billing]
96
- apps/api/src/realtime.ts [billing]
97
- apps/api/src/storage.ts [storage]
98
- apps/api/tests/{health.test.ts,env.test.ts} [api]
99
- apps/api/tests/revenuecat-webhook.test.ts [billing]
100
- apps/api/tests/storage.test.ts [storage]
101
- apps/assets-private-proxy/{package.json,wrangler.jsonc} [storage]
102
- apps/assets-private-proxy/src/index.js [storage]
103
- cloudflare/{README.md,r2-cors.template.json} [storage]
104
- apps/web/{package.json,tsconfig.json,next-env.d.ts,next.config.ts,postcss.config.mjs,components.json,.env.example} [web]
105
- apps/web/app/{layout.tsx,page.tsx,globals.css} [web]
106
- apps/web/components/ui/{button.tsx,card.tsx} [web]
107
- apps/web/lib/utils.ts [web]
108
- apps/web/{lib/api.ts,hooks/use-api-client.ts} [web + api]
109
- apps/web/components/item-list.tsx [web + api + db]
110
- apps/web/components/account-actions.tsx [web + auth]
111
- apps/web/{hooks/use-entitlement.ts,components/subscription-status.tsx} [web + billing]
112
- apps/mobile/{package.json,app.json,tsconfig.json,expo-env.d.ts,eas.json,.env.example,.gitignore} [mobile]
113
- apps/mobile/app/{_layout.tsx,index.tsx} [mobile]
114
- apps/mobile/{lib/api.ts,hooks/use-api-client.ts} [mobile + api]
115
- apps/mobile/components/item-list.tsx [mobile + api + db]
116
- apps/mobile/components/account-controls.tsx [mobile + auth]
117
- apps/mobile/hooks/use-entitlement.ts [mobile + billing]
118
- apps/mobile/lib/subscriptions.ts [native-subscriptions]
119
- apps/desktop/{package.json,tsconfig.json,tsconfig.main.json,vite.config.ts,postcss.config.mjs,components.json,index.html,.env.example} [desktop]
120
- apps/desktop/scripts/dev.mjs [desktop]
121
- apps/desktop/src/main/{main.ts,preload.cts} [desktop]
122
- apps/desktop/src/renderer/{main.tsx,styles.css,lib/utils.ts} [desktop]
123
- apps/desktop/src/renderer/components/ui/button.tsx [desktop]
124
- apps/desktop/src/renderer/lib/api.ts [desktop + api]
125
- apps/desktop/src/renderer/components/item-list.tsx [desktop + api + db]
126
- apps/desktop/src/renderer/lib/auth.ts [desktop + auth]
127
- apps/desktop/src/renderer/hooks/use-entitlement.ts [desktop + billing]
128
- apps/desktop/electron-builder.env.example [electron-updater]
129
- apps/desktop/scripts/publish-updates.mjs [electron-updater]
130
- apps/desktop-updater-worker/{package.json,wrangler.jsonc} [electron-updater]
131
- apps/desktop-updater-worker/src/index.js [electron-updater]
132
- cloudflare/desktop-updates.md [electron-updater]
133
- apps/extension/{package.json,tsconfig.json,wxt.config.ts,postcss.config.cjs,tailwind.config.cjs,components.json,.env.example,README.md} [extension]
134
- apps/extension/src/{styles/main.css,lib/utils.ts} [extension]
135
- apps/extension/src/components/ui/button.tsx [extension]
136
- apps/extension/src/entrypoints/{background.ts,sidepanel/index.html,sidepanel/main.tsx,sidepanel/app.tsx} [extension]
137
- apps/extension/src/contexts/auth-context.tsx [extension + auth]
138
- apps/extension/src/lib/api.ts [extension + api]
139
- apps/extension/src/components/item-list.tsx [extension + api + db]
140
- apps/extension/src/hooks/use-entitlement.ts [extension + billing]
141
- ```
142
-
143
- Do not create `anhedral.json` manually. Its schema-v5 hashes, modes, ownership classes, template and UI-provider provenance, module resolution, generator version, and toolchain channel form a trust boundary for `add` and `doctor`. Invented records are worse than no manifest. Also omit `ANHEDRAL.md`, whose CLI-management claims would be false for a manual workspace.
144
-
145
- ## 3. Create root configuration
146
-
147
- ### Root package
148
-
149
- Create a private `package.json` with version `0.1.0`, the selected Node engine, the pnpm version being used, `turbo` as an exact dev dependency, and `workspaces` containing `apps/*` when any app exists and `packages/*` when API or database exists.
150
-
151
- Always add `build: turbo build` and `typecheck: turbo typecheck`. Set `dev` to
152
- the primary product loop: run the first selected client in canonical module
153
- order plus API when present, or API alone for a backend-only project. When the
154
- project has additional app surfaces, add `dev:all` to run every selected app in
155
- parallel. Each command uses `turbo run dev` with explicit
156
- `--filter=./apps/<surface>` arguments. Add only selected scripts:
157
-
158
- ```text
159
- web: dev:web, verify:web = typecheck + build
160
- mobile: dev:mobile, verify:mobile = typecheck + build:web
161
- api: dev:api, verify:api = test:coverage + build
162
- desktop: dev:desktop, desktop:build, verify:desktop = typecheck + build
163
- electron-updater: desktop:updates:cloudflare:login,
164
- desktop:updates:bucket:create, desktop:updates:first-provision,
165
- desktop:updates:worker:{check,dev,deploy,types},
166
- desktop:updates:build:{mac,win,linux},
167
- desktop:updates:publish, verify:desktop-updates
168
- extension: dev:extension, extension:zip, verify:extension = typecheck + zip
169
- db: db:generate, db:migrate, db:check, db:studio, verify:db
170
- storage: r2:login, r2:bucket:create, r2:cors:list, r2:cors:set,
171
- assets:proxy:check, assets:proxy:dev, assets:proxy:deploy,
172
- verify:assets-proxy
173
- ```
174
-
175
- Set root `verify` to every selected `verify:<module>` joined with `&&`; include `verify:db`. If no app exists, begin it with `pnpm typecheck`.
176
-
177
- ### pnpm workspace
178
-
179
- Create `pnpm-workspace.yaml` with the same workspace globs as the root package. Set `autoInstallPeers: false`, then add:
180
-
181
- ```yaml
182
- onlyBuiltDependencies:
183
- - electron
184
- - esbuild
185
- - sharp
186
- ignoredBuiltDependencies:
187
- - browser-tabs-lock
188
- - bufferutil
189
- - core-js
190
- - electron-winstaller
191
- - spawn-sync
192
- - utf-8-validate
193
- peerDependencyRules:
194
- ignoreMissing:
195
- - '@solana/web3.js'
196
- - bs58
197
- - react-native
198
- allowedVersions:
199
- esbuild: '>=0.25.0'
200
- utf-8-validate: '>=5.0.2'
201
- ```
202
-
203
- Pin current security overrides for `@vitejs/plugin-react`, `postcss`, vulnerable `esbuild` ranges, `shell-quote`, `tmp`, and `uuid`. Inside the Anhedral repository, copy exact override keys and values from `SECURITY_OVERRIDES` in `src/dependencies.ts`.
204
-
205
- ### Turbo, ignore files, Vercel, and CI
206
-
207
- Create `turbo.json` using `https://turborepo.dev/schema.json` with:
208
-
209
- - `build.dependsOn = ["^build"]` and outputs `.next/**`, `!.next/cache/**`, `.output/**`, `dist/**`.
210
- - `typecheck.dependsOn = ["^typecheck"]`.
211
- - `dev.cache = false` and `dev.persistent = true`.
212
-
213
- Ignore `node_modules`, framework/build caches, coverage, release output, every `.env` variant except `.env.example`, and `*.tsbuildinfo`. Put extension output, mobile web export, desktop release output, `apps/assets-private-proxy`, and `apps/desktop-updater-worker` in `.vercelignore`.
214
-
215
- Create the single root `vercel.json` using `https://openapi.vercel.sh/vercel.json`. Add service `api` rooted at `apps/api` and service `web` rooted at `apps/web` as selected. Put the `/api/(.*)` rewrite before the web `/(.*)` catch-all. If billing is selected, add `*/5 * * * *` for `/api/internal/realtime/flush`; if storage is selected, add `0 3 * * *` for `/api/internal/storage/cleanup`. Protect both internal routes with the same high-entropy `CRON_SECRET` bearer token.
216
-
217
- Create `.github/workflows/anhedral-ci.yml` with read-only contents permission, concurrency cancellation, pinned action commit SHAs, pnpm setup, Node `22.13.0` for mobile or `20.19.0` otherwise, and frozen installation. Run `pnpm typecheck`, API coverage tests when API exists, and `pnpm build`. For database workspaces also run `pnpm verify:db`, run `pnpm db:generate`, fail on any migration diff, and fail on untracked migration artifacts.
218
-
219
- Create `scripts/first-run.mjs` and root `first-run`/`ready` scripts. `first-run`
220
- must copy only missing package-local environment files and never overwrite
221
- existing values. `ready` must identify missing files and required blank or
222
- placeholder variables without printing values, return nonzero when blocked,
223
- and treat explicitly documented optional variables as optional.
224
-
225
- Create `README.md` containing selected modules and first-run commands. Create `PRODUCTION.md` containing provider and deployment requirements from sections 8 and 9. These two files become user-owned; do not rewrite them during later manual module additions.
226
-
227
- ## 4. Create shared packages
228
-
229
- Use strict TypeScript with `moduleResolution: Bundler`, `noEmit`, `skipLibCheck`, and `noUncheckedIndexedAccess`. Export source TypeScript directly inside the workspace.
230
-
231
- ### Contracts
232
-
233
- Create `@shared/contracts` whenever API is selected. Depend on Zod. Define and export schemas plus inferred types for health and readiness; authenticated user when auth is selected; revisioned entitlement, realtime token request, and `subscription.changed` invalidation when billing is selected; and strict create-upload, confirm-upload, upload-record, and route-param data when storage is selected.
234
-
235
- Keep the upload content-type allowlist and maximum upload size in contracts so API and clients share one source of truth. Validate API response bodies in the client package before returning them.
236
-
237
- ### API client
238
-
239
- Create `@shared/api-client` when API and at least one client surface are selected. Depend on contracts and Zod. Implement:
240
-
241
- - strict absolute `http:`/`https:` base-URL normalization with no credentials, query, or fragment;
242
- - an `ApiError` carrying HTTP status, stable code, details, and cause;
243
- - optional async token retrieval;
244
- - timeout and caller-abort composition with listener/timer cleanup;
245
- - JSON parsing and Zod response validation;
246
- - typed methods for health/readiness and only selected auth, billing, and storage routes.
247
-
248
- When database is selected, also create user-owned item request/response
249
- contracts and `listItems`/`createItem` methods. Render that same feature with
250
- idiomatic, accessible UI in every selected client so the first end-to-end proof
251
- is cross-platform rather than web-only.
252
-
253
- Do not make server-only provider SDKs dependencies of clients.
254
-
255
- ### Realtime client
256
-
257
- Create `@shared/realtime` only when billing and at least one client are selected. Depend on Ably and contracts. Accept an async API-token callback, connect with `authCallback`, subscribe only to `private:users/<userId>/subscriptions`, validate every `subscription.changed` payload, expose connection errors, and return an idempotent cleanup function that unsubscribes and closes the client. Never put `ABLY_API_KEY` or a broad Ably capability in a client package.
258
-
259
- ### Database
260
-
261
- Create `@shared/db` when `db` is selected. Use Neon serverless, Drizzle ORM, dotenv, Drizzle Kit, and tsx. Provide `db:generate`, `db:migrate`, `db:check`, and `db:studio`. Configure PostgreSQL with `src/schema.ts` and `migrations/`.
262
-
263
- Always define an `items` table. For billing, add `subscriptions` with a monotonic integer `revision`, idempotent `webhookEvents`, and `realtimeOutbox` with user, topic, revision, attempt/error, delivery, and creation fields. Add `uploads` for storage, including user ownership, staging/final keys, declared and actual metadata, status/rejection fields, expiry, cleanup, confirmation, and creation timestamps. Export the Drizzle client and schema, and fail immediately when `DATABASE_URL` is absent.
264
-
265
- Create `scripts/verify-db-migrations.mjs`. It must recursively find committed SQL files, fail when none exist, fail when SQL is untracked inside a Git worktree, and require a Git worktree in CI.
266
-
267
- ## 5. Create the API
268
-
269
- Create a Fastify TypeScript package with `dev`, `build`, `typecheck`, `test`, and `test:coverage`. Use Fastify, CORS, compression, Helmet, rate limiting, Zod, and only provider/database dependencies selected by the module plan.
270
-
271
- Implement these layers:
272
-
273
- - `env.ts`: validate selected values, exact CORS origins, port, proxy hops, demo mode, production key prefixes, URL forms, secret strength, and provider identifier formats. Never silently fall back to demo behavior in production.
274
- - `application.ts`: construct the app, set proxy trust, register security plugins, expose shutdown state, centralize safe error serialization, and register routes under `/api`.
275
- - `routes.ts`: register health/readiness unconditionally and provider routes conditionally.
276
- - `index.ts`: listen on `0.0.0.0`, handle `SIGINT`/`SIGTERM` once, begin draining, close gracefully, and enforce a shutdown deadline.
277
-
278
- Always provide `GET /api/health` and `GET /api/ready`. Readiness returns 503 for dependency failure or shutdown. Do not leak internal errors, stack traces, credentials, or arbitrary details in 5xx responses. Allow safe details only for intentional 4xx errors.
279
-
280
- When auth is selected, verify Clerk requests server-side and expose protected `/api/me`. When billing is selected, implement a RevenueCat webhook with constant-time secret comparison, shape validation, durable idempotency, claim/retry handling, monotonic event timestamps, and server-side subscriber reconciliation. In the same database transaction as every accepted subscription mutation, increment its revision and insert an outbox record. Expose authenticated `GET /api/subscriptions/me`, `POST /api/subscriptions/refresh`, and `POST /api/realtime/token`; issue a short-lived Ably token request limited to the authenticated Clerk user's subscribe-only channel. Publish only `{ type, revision }`, mark delivery after publish, retain failures for retry, and expose `GET /api/internal/realtime/flush` behind `CRON_SECRET`. When storage is selected, issue short-lived R2 presigned PUTs bound to content type and length, persist pending uploads, verify object metadata before confirmation, isolate every key below `R2_PREFIX`, expose an owner-authorized read-URL endpoint with a bounded presigned-GET TTL, and provide an authenticated cleanup endpoint for the scheduled cron.
281
-
282
- Test health, readiness, shutdown state, safe errors, and exact CORS behavior. Test environment validation. Add replay/out-of-order/retry tests for billing and size/type/ownership/cleanup tests for storage.
283
-
284
- ## 6. Create client surfaces
285
-
286
- Every client with API access must use `@shared/api-client`; do not duplicate fetch/error logic. Every authenticated client supplies its Clerk session token to that client. Require HTTPS for configured production API URLs. Billing clients load the authoritative entitlement first, subscribe through `@shared/realtime`, ignore invalidation revisions they already hold, and refetch rather than trusting event payloads. They must also refetch after visibility/foreground recovery so missed or delayed events cannot leave stale access.
287
-
288
- ### Web
289
-
290
- Create a Next.js App Router app with Tailwind and shadcn-style `Button` and `Card` primitives. Use strict TypeScript, the `@/*` alias, a development rewrite from `/api/:path*` to `http://localhost:8787/api/:path*`, and same-origin `/api` in production. Wrap the layout in Clerk only for auth. Add account controls for auth and typed API status UI for API. For billing, render a subscription-status component backed by a realtime entitlement hook and refetch on document visibility.
291
-
292
- ### Mobile
293
-
294
- Create an Expo Router app with static web export, a normalized URL scheme, strict TypeScript, and EAS production profiles. Add Clerk plus secure token caching only for auth. Add the typed API hook only for API. For billing, subscribe to realtime invalidations and refetch when `AppState` returns active. For native subscriptions, configure RevenueCat independently for iOS and Android public SDK keys, synchronize the RevenueCat app-user ID after Clerk loads, log RevenueCat out when Clerk signs out, expose retryable synchronization errors, register one global customer-info update listener, and present the configured entitlement paywall. After purchase/restore success or a RevenueCat customer-info callback, call the authenticated refresh endpoint so every other surface updates without waiting for webhook delivery.
295
-
296
- ### Desktop
297
-
298
- Create Electron main/preload and a Vite React renderer. Keep `contextIsolation: true`, `nodeIntegration: false`, sandboxing enabled, and navigation/window-open restricted to trusted targets. Use a preload bridge rather than exposing Node. Add Tailwind/shadcn-style UI. Add typed API and Clerk browser auth only when selected. For billing, expose the current Clerk user ID to a realtime entitlement hook and refetch when the renderer becomes visible. Provide development orchestration plus current-host and platform-specific electron-builder scripts.
299
-
300
- ### Extension
301
-
302
- Create a WXT React side-panel extension with Tailwind/shadcn-style UI. Generate permissions from selected features and host permissions only from validated API/Clerk origins. Require HTTPS in production. Add Clerk's extension provider/context only for auth and typed API only for API. For billing, consume the authenticated user ID/API client from that context, subscribe to invalidations, and refetch when the side panel becomes visible. Include `storage` and `cookies` permissions only when Clerk needs them. Provide `dev`, `build`, `zip`, `postinstall`, and `typecheck`.
303
-
304
- ## 7. Wire optional features
305
-
306
- After selected surfaces exist, perform a cross-surface pass:
307
-
308
- - `auth`: Clerk server verification; protected API route; provider/account UI in every selected client; token-aware API hooks; client publishable keys and server secret separated.
309
- - `billing`: revisioned database state and transactional outbox; RevenueCat webhook/direct refresh; entitlement, scoped-token, and retry routes; Ably publisher and shared subscriber; contracts/API-client methods; live hooks in every client. Never expose RevenueCat or Ably secret keys.
310
- - `storage`: upload tables; R2 presign/confirm/cleanup; owner-authorized presigned GET URLs; contracts and client methods; Vercel cron; generated R2 CORS/lifecycle guidance; and `apps/assets-private-proxy` with Worker name `assets-private-proxy`, private bucket binding `ASSETS`, `workers.dev` disabled, a user-owned custom-domain config, confirmed-prefix-only streaming GET/HEAD delivery, ranges, conditionals, cache, method/host validation, and no bucket listing.
311
- - `native-subscriptions`: mobile RevenueCat SDKs, platform keys, Clerk identity synchronization, entitlement/paywall behavior, and error recovery.
312
-
313
- Search the finished workspace for imports of unselected providers. Remove both source branches and dependencies when a feature is absent.
314
-
315
- ## 8. Configure environments and providers
316
-
317
- The root `.env.example` is an inventory, not a runtime file. Create package-local examples and copy them to ignored runtime files:
318
-
319
- ```text
320
- apps/api/.env.example -> apps/api/.env
321
- packages/db/.env.example -> packages/db/.env
322
- apps/web/.env.example -> apps/web/.env.local
323
- apps/mobile/.env.example -> apps/mobile/.env
324
- apps/desktop/.env.example -> apps/desktop/.env
325
- apps/extension/.env.example -> apps/extension/.env
326
- ```
327
-
328
- Include only selected values:
329
-
330
- ```text
331
- API: NODE_ENV, PORT, TRUST_PROXY_HOPS, CORS_ORIGINS, ANHEDRAL_DEMO
332
- db: DATABASE_URL
333
- auth: CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY
334
- web: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, NEXT_PUBLIC_API_URL
335
- mobile: EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY, EXPO_PUBLIC_API_URL
336
- desktop: VITE_CLERK_PUBLISHABLE_KEY, VITE_API_URL
337
- extension: VITE_CLERK_PUBLISHABLE_KEY, VITE_CLERK_FRONTEND_API_URL,
338
- VITE_CLERK_SYNC_HOST, VITE_API_URL, optional VITE_CRX_PUBLIC_KEY
339
- billing: RC_WEBHOOK_SECRET, RC_SECRET_API_KEY, RC_ENTITLEMENT_ID,
340
- ABLY_API_KEY, CRON_SECRET
341
- native: EXPO_PUBLIC_RC_API_KEY_IOS, EXPO_PUBLIC_RC_API_KEY_ANDROID,
342
- EXPO_PUBLIC_RC_ENTITLEMENT_ID
343
- storage: BASE_URL, R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY,
344
- R2_BUCKET_NAME, R2_PREFIX=storage,
345
- R2_PROXY_READ_URL_TTL_SECONDS=600, CLOUDFLARE_API_TOKEN (ops/CI only),
346
- CRON_SECRET
347
- ```
348
-
349
- Use explicit local CORS origins for selected browser clients. Include `null` only for Electron's file origin. Production uses exact HTTPS origins. Keep `ANHEDRAL_DEMO=false`, server secrets server-only, and real environment files out of Git.
350
-
351
- For R2, generate a user-owned `cloudflare/r2-cors.template.json`, list the live policy before replacing it, and retain every exact upload origin. Add a lifecycle rule for `storage/staging/` whose age exceeds the application cleanup grace period. Keep `r2.dev` and R2 bucket custom domains disabled. Use the generated Worker Custom Domain at `assets.<domain>` for public-by-key `storage/confirmed/` objects only. Use the authenticated upload-owner read-URL route for private delivery; it returns a short-lived presigned GET governed by `R2_PROXY_READ_URL_TTL_SECONDS`.
352
-
353
- ## 9. Install, migrate, and verify
354
-
355
- Run from the root:
356
-
357
- ```sh
358
- pnpm install
359
- pnpm first-run
360
- pnpm ready
361
- ```
362
-
363
- For a database workspace, generate and review the initial migration, and add it
364
- to Git before the gate:
365
-
366
- ```sh
367
- pnpm db:generate
368
- git add packages/db/migrations
369
- pnpm verify:db
370
- pnpm db:migrate
371
- ```
372
-
373
- Then run every selected package verification, root verification, and build:
374
-
375
- ```sh
376
- pnpm typecheck
377
- pnpm verify
378
- pnpm build
379
- ```
380
-
381
- Run API coverage when API exists, Expo compatibility checks when mobile exists, a packaged Electron smoke test when desktop exists, and load unpacked WXT output when extension exists. Exercise one authenticated API call per selected client. For billing, replay a webhook, send an older event, verify an outbox record is committed atomically, verify the scoped token cannot publish or subscribe to another user, and observe one native purchase/refresh update web, Android, extension, and desktop sessions. Disconnect one client, mutate the entitlement, reconnect/foreground it, and confirm authoritative recovery. For storage, perform browser preflight, presigned upload, confirmation, cross-user private-read rejection, read-URL TTL verification, cleanup, Worker syntax/Wrangler dry-run validation, GET/HEAD/range/conditional/cache behavior, forbidden methods, unexpected hosts, missing keys, staging/private-prefix rejection, and direct-bucket inaccessibility.
382
-
383
- Do not weaken types, tests, provider validation, security headers, or migration checks to make verification green. Report the failing package and command, fix the owning layer, and rerun the narrow check before root verification.
384
-
385
- ## 10. Audit completeness
386
-
387
- Answer yes to every applicable item:
388
-
389
- - Were prerequisites resolved before creation?
390
- - Is there exactly one pnpm workspace and lockfile?
391
- - Do root scripts reference only selected modules?
392
- - Does every selected conditional file exist and every unselected one stay absent?
393
- - Are contracts shared by API and clients?
394
- - Are provider dependencies and environment keys gated by selection?
395
- - Are secrets absent from clients and committed files?
396
- - Are optional flows wired end to end rather than represented by placeholders?
397
- - For billing, are revisions monotonic, outbox writes atomic, Ably capabilities user-scoped, and all clients recovery-safe?
398
- - Is a reviewed, committed migration present for `db`?
399
- - Do CI, Vercel services/rewrites, cron, CORS, and local ports agree?
400
- - For storage, is the bucket private, is the hostname attached to the Worker rather than R2, and does the documented public-key or authenticated download policy match the code?
401
- - Did `pnpm verify` and `pnpm build` pass?
402
- - Is `anhedral.json` absent unless produced by the matching CLI?
403
-
404
- ## 11. Work from the Anhedral source repository
405
-
406
- Inside the Anhedral repository, treat current source as higher authority than copied versions or prose. Read before exact reconstruction:
407
-
408
- ```text
409
- src/architecture/modules.ts dependency closure and canonical module IDs
410
- src/dependencies.ts exact versions, engines, and overrides
411
- src/scaffold.ts root config, CI, docs, env inventory, manifest boundary
412
- src/templates/shared.ts contracts, API client, and database
413
- src/templates/api.ts Fastify source, providers, and tests
414
- src/templates/web.ts Next.js source and conditional wiring
415
- src/templates/mobile.ts Expo source and RevenueCat integration
416
- src/templates/desktop.ts Electron/Vite source and security settings
417
- src/templates/extension.ts WXT source, permissions, auth, and side panel
418
- docs/output-tree-contract.md expected paths for representative plans
419
- ```
420
-
421
- Render selected branches manually and replace project/display-name literals safely. Do not execute `dist/bin.js`, `bin/anhedral.js`, `scaffoldProject`, or template scaffold functions. Compare against the relevant output-tree contract, but never copy `demo/`: demos can contain installed artifacts or lag current templates. If source and this guide disagree, follow source and update this guide in the same change.