@fabrika/builder 0.2.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 (112) hide show
  1. package/BUILD-CONTRACT.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.md +100 -0
  4. package/cli/src/bin.ts +66 -0
  5. package/cli/src/build-status.ts +109 -0
  6. package/cli/src/catalog.ts +42 -0
  7. package/cli/src/commands/advance.ts +73 -0
  8. package/cli/src/commands/build.ts +215 -0
  9. package/cli/src/commands/ensure-daemon.ts +25 -0
  10. package/cli/src/commands/gate.ts +165 -0
  11. package/cli/src/commands/induce.ts +61 -0
  12. package/cli/src/commands/init.ts +91 -0
  13. package/cli/src/commands/next.ts +31 -0
  14. package/cli/src/config.ts +47 -0
  15. package/cli/src/context.ts +28 -0
  16. package/cli/src/daemon-client.ts +280 -0
  17. package/cli/src/daemon-lifecycle.ts +171 -0
  18. package/cli/src/derive.ts +109 -0
  19. package/cli/src/index.ts +38 -0
  20. package/cli/src/induce.ts +151 -0
  21. package/cli/src/inject-skills.ts +313 -0
  22. package/cli/src/issues.ts +75 -0
  23. package/cli/src/layout.ts +124 -0
  24. package/cli/src/packet.ts +108 -0
  25. package/cli/src/phase-state.ts +19 -0
  26. package/cli/src/phases.ts +131 -0
  27. package/cli/src/skeletons.ts +104 -0
  28. package/daemon/src/bin.ts +70 -0
  29. package/daemon/src/build-stream.ts +38 -0
  30. package/daemon/src/handlers.ts +472 -0
  31. package/daemon/src/index.ts +123 -0
  32. package/daemon/src/paths.ts +30 -0
  33. package/daemon/src/phases.ts +221 -0
  34. package/daemon/src/static.ts +72 -0
  35. package/decisions/README.md +133 -0
  36. package/decisions/patterns/app-deploy.md +55 -0
  37. package/decisions/patterns/auth-password-kv.md +45 -0
  38. package/decisions/patterns/count-store.md +44 -0
  39. package/decisions/patterns/di-container.md +47 -0
  40. package/decisions/patterns/feature-architecture.md +48 -0
  41. package/decisions/patterns/lib-publish.md +46 -0
  42. package/decisions/patterns/monorepo.md +47 -0
  43. package/decisions/patterns/named-seeds.md +45 -0
  44. package/decisions/patterns/observability.md +45 -0
  45. package/decisions/patterns/single-worker-spa.md +74 -0
  46. package/decisions/patterns/structured-logging.md +49 -0
  47. package/decisions/patterns/typed-worker-rpc.md +87 -0
  48. package/decisions/patterns/vozka-app-deploy.md +74 -0
  49. package/decisions/patterns/write-split.md +47 -0
  50. package/decisions/technologies/analytics-engine.md +44 -0
  51. package/decisions/technologies/bindx.md +55 -0
  52. package/decisions/technologies/bun.md +51 -0
  53. package/decisions/technologies/buzola.md +80 -0
  54. package/decisions/technologies/cloudflare-access.md +50 -0
  55. package/decisions/technologies/cloudflare-vite-plugin.md +43 -0
  56. package/decisions/technologies/cloudflare.md +50 -0
  57. package/decisions/technologies/contember.md +65 -0
  58. package/decisions/technologies/cron-triggers.md +46 -0
  59. package/decisions/technologies/d1.md +51 -0
  60. package/decisions/technologies/durable-objects.md +44 -0
  61. package/decisions/technologies/kv.md +42 -0
  62. package/decisions/technologies/lopata.md +55 -0
  63. package/decisions/technologies/nuasite.md +50 -0
  64. package/decisions/technologies/oblaka.md +48 -0
  65. package/decisions/technologies/opice.md +40 -0
  66. package/decisions/technologies/pletivo.md +53 -0
  67. package/decisions/technologies/postgres.md +47 -0
  68. package/decisions/technologies/preact.md +44 -0
  69. package/decisions/technologies/propustka.md +137 -0
  70. package/decisions/technologies/queue.md +55 -0
  71. package/decisions/technologies/r2.md +41 -0
  72. package/decisions/technologies/react-router.md +47 -0
  73. package/decisions/technologies/react.md +37 -0
  74. package/decisions/technologies/roj.md +51 -0
  75. package/decisions/technologies/sentry.md +49 -0
  76. package/decisions/technologies/tailwind.md +45 -0
  77. package/decisions/technologies/vite.md +47 -0
  78. package/decisions/technologies/vozka.md +122 -0
  79. package/decisions/technologies/workers-ai.md +59 -0
  80. package/decisions/technologies/wrangler.md +41 -0
  81. package/decisions/tooling/deptective.md +45 -0
  82. package/decisions/tooling/edvabe.md +45 -0
  83. package/index.ts +2 -0
  84. package/package.json +51 -0
  85. package/skill/SKILL.md +76 -0
  86. package/skill/agents/build-orchestrator.md +170 -0
  87. package/skill/agents/flows-author.md +65 -0
  88. package/skill/agents/implementer.md +37 -0
  89. package/skill/agents/openai.yaml +4 -0
  90. package/skill/agents/opice-skeleton-author.md +68 -0
  91. package/skill/gates/brief.jsx +20 -0
  92. package/skill/gates/build.jsx +20 -0
  93. package/skill/gates/design.jsx +22 -0
  94. package/skill/gates/flows.jsx +14 -0
  95. package/skill/gates/review-grid.jsx +32 -0
  96. package/skill/gates/tech.jsx +17 -0
  97. package/skill/gates/vision.jsx +27 -0
  98. package/stacks/README.md +44 -0
  99. package/stacks/cf-fat.md +64 -0
  100. package/stacks/cf-internal-app.md +125 -0
  101. package/stacks/cf-lightweight.md +54 -0
  102. package/stacks/contember-app.md +53 -0
  103. package/stacks/content-site.md +45 -0
  104. package/ui/dist/assets/annotations-runtime.js +1 -0
  105. package/ui/dist/assets/client.css +2 -0
  106. package/ui/dist/assets/client.fhjppkvr.js +214 -0
  107. package/ui/dist/assets/components.js +63 -0
  108. package/ui/dist/assets/preact-compat.js +1 -0
  109. package/ui/dist/assets/react-jsx-dev-runtime.js +3 -0
  110. package/ui/dist/assets/react-jsx-runtime.js +3 -0
  111. package/ui/dist/assets/runtime.js +1 -0
  112. package/ui/dist/index.html +36 -0
@@ -0,0 +1,48 @@
1
+ ---
2
+ id: oblaka
3
+ kind: technology
4
+ title: oblaka (Cloudflare IaC / bindings as code)
5
+ slot: iac
6
+ provides: [iac, binding-definitions, wrangler-generation]
7
+ requires: [cloudflare]
8
+ recommends: []
9
+ conflicts: []
10
+ npm: ["oblaka-iac@^0.0.18"]
11
+ status: wip
12
+ sources:
13
+ - where: "oblaka (repo) + `oblaka-iac` on npm (installed types)"
14
+ read: "The CURRENT binding API, programmatic deploy surface and how `bunx oblaka <file>` generates wrangler.jsonc. Read the live types — do not guess."
15
+ - where: "vozka packages/core + vozka-config"
16
+ read: "How Vozka materializes the same oblaka resource graph for production while an app keeps a thin local-dev shim."
17
+ ---
18
+
19
+ # oblaka
20
+
21
+ ## What it is
22
+
23
+ A TypeScript DSL for Cloudflare IaC: define the Worker + its bindings in
24
+ `oblaka.ts`, run `bunx oblaka oblaka.ts` to **generate `wrangler.jsonc`**. One typed
25
+ source of truth for Workers, D1, KV, R2, Queues, DO, Analytics Engine and cron.
26
+ Vozka uses oblaka programmatically for production provisioning; standalone
27
+ `oblaka.ts` remains useful as the local lopata config shim.
28
+
29
+ ## When & why to use
30
+
31
+ - Any CF stack — declare bindings as code instead of hand-writing `wrangler.jsonc`.
32
+ - A Vozka app — the resource builder in `vozka.config.ts` is still an oblaka
33
+ `Worker`; reuse it from the local shim rather than duplicating the graph.
34
+
35
+ ## Impacts & gotchas
36
+
37
+ - **`wrangler.jsonc` is GENERATED — never hand-edit it.** Change `oblaka.ts` and regenerate.
38
+ - Standalone/local shape: `define(({ env }) => buildWorker({ env, domain: undefined }))`,
39
+ where `buildWorker` is exported from `vozka.config.ts`. For a non-Vozka app,
40
+ build the `Worker` inline as before.
41
+ - **Verify every binding constructor against the installed `oblaka-iac` types** — it's WIP and a stack may use bindings (Queue consumer, AE, cron) the constructor surface for which moves. If a binding type is missing, generate the wrangler entry per Cloudflare docs.
42
+ - `vozka-config` and oblaka publish raw TypeScript in some versions; if the app's
43
+ main `tsc` excludes config files, validate them by actually materializing the
44
+ config and running a wrangler dry-run/config check.
45
+
46
+ ## Relationships — why
47
+
48
+ - **requires `cloudflare`** — it exists to configure CF. Recommended by every CF binding decision (`d1`/`kv`/`r2`/`queue`/`durable-objects`/`cron-triggers`).
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: opice
3
+ kind: technology
4
+ title: opice (AI-driven E2E browser tests)
5
+ slot: e2e-tests
6
+ provides: [e2e-browser-tests, acceptance]
7
+ requires: []
8
+ recommends: [bun]
9
+ conflicts: []
10
+ npm: ["@opice/harness@^0.6.1", "@opice/cli@^0.6.1"]
11
+ status: wip
12
+ sources:
13
+ - where: "opice (repo) + `@opice/harness` / `@opice/cli` on npm (installed types)"
14
+ read: "How browserTest/step/invariant work and how the harness runs under `bun test`. Read the installed types — do not guess."
15
+ - where: "The opice-* skills (opice-plan / opice-author / opice-batch / opice-reeval)"
16
+ read: "The authoring procedure: skeletons → authored steps → CI re-eval."
17
+ ---
18
+
19
+ # opice
20
+
21
+ ## What it is
22
+
23
+ The acceptance layer for fabrika: markdown scenarios → LLM-generated `*.test.ts`
24
+ under `tests/browser/` → deterministic `bun test` runs. `browserTest` + `step` +
25
+ `invariant`, with metadata (`url`, `seeds`, `roles`) that fabrika **induces issues
26
+ from**.
27
+
28
+ ## When & why to use
29
+
30
+ - Always, in a fabrika builder build — opice skeletons are the contract every issue satisfies; `url`/`seeds`/`roles` drive foundation-issue induction.
31
+
32
+ ## Impacts & gotchas
33
+
34
+ - **Skeleton metadata is a contract**: `url` → a route must exist; `seeds: ['x']` → `seeds/x.sql` must exist; `roles` → an auth foundation. fabrika `induce` unions these.
35
+ - Selector priority: **byRole → byLabel → getByText → el(testid)**. The app must be accessibility-first (every interactive element named, every section a heading) or opice-author has no stable selector.
36
+ - Runs against the **running** dev server (`APP_URL`); `bunx opice test <file>` for one, `bun test tests/browser` for all. WIP — read installed types.
37
+
38
+ ## Relationships — why
39
+
40
+ - Standalone (no hard tech deps) — it tests whatever stack the app is built on; runs under **`bun test`** (recommends `bun`) and pairs with `named-seeds` for the seed contract.
@@ -0,0 +1,53 @@
1
+ ---
2
+ id: pletivo
3
+ kind: technology
4
+ title: pletivo — Bun-native SSG (Astro-compatible) — fits the bun/non-vite preference
5
+ slot: site-framework
6
+ provides: [ssg, astro-compat, jsx-pages, content-collections, islands, file-routing]
7
+ requires: [bun]
8
+ recommends: [preact, tailwind]
9
+ conflicts: [nuasite]
10
+ npm: ["pletivo@^0.1", "@pletivo/astro-jsx-pages@^0.0.1"]
11
+ status: experimental
12
+ sources:
13
+ - where: "pletivo/README.md + CLAUDE.md"
14
+ read: "Compatibility matrix, project structure, and the critical invariants (island registry reset, JSON-serializable island props)."
15
+ - where: "pletivo/packages/pletivo/src/{index.ts,config.ts,build.ts}"
16
+ read: "Public exports (defineCollection/getCollection, PletivoConfig), the render loop, and page emission."
17
+ - where: "pletivo/packages/pletivo/src/runtime/{island.ts,hydration.ts}"
18
+ read: "Island registration/reset + client hydration strategies (load/idle/visible/media)."
19
+ ---
20
+
21
+ # pletivo
22
+
23
+ ## What it is
24
+
25
+ A **Bun-powered static site generator**, ~2× faster than Astro and a near drop-in for
26
+ it: compiles `.astro` pages + JSX/TSX to static HTML at build, file-based routing
27
+ (`src/pages/`), content collections (Zod), and **Preact islands** with multiple
28
+ hydration strategies. Runs on **Bun** (not Node) — a natural fit for our bun /
29
+ non-vite preference (builds with Bun's pipeline, no separate vite app server).
30
+
31
+ ## When & why to use
32
+
33
+ - A **content-focused static site** (blog, docs, marketing) where Astro builds are too slow.
34
+ - You want Astro compatibility (`.astro`, content collections) + lightweight Preact islands, on Bun.
35
+
36
+ ## When NOT to use
37
+
38
+ - You need **SSR / per-request dynamic routes** → use a Workers SSR app (`react-router`) instead; pletivo is SSG-only (pre-declare dynamic routes via `getStaticPaths`).
39
+ - A visual-CMS content site → `nuasite`.
40
+ - Very large sites (>5k pages) or Node-only deps.
41
+
42
+ ## Impacts & gotchas
43
+
44
+ - **Bun-locked** build/dev; CI needs Bun.
45
+ - **Islands must be JSON-serializable props** (embedded in `data-props`); functions/Dates/circular refs break.
46
+ - **Island registry must reset between pages** (`resetIslandRegistry()`) — the build does it; custom render paths must too, or island metadata bleeds across pages.
47
+ - Client is **Preact**, not React (no React-only island libs).
48
+
49
+ ## Relationships — why
50
+
51
+ - **requires `bun`** — it's a Bun CLI/runtime (no Node SSR).
52
+ - **recommends `preact`** (islands) + **`tailwind`** (styling).
53
+ - **conflicts `nuasite`** — same `site-framework` slot; pletivo = fast minimal SSG, nuasite = Astro + visual CMS. Pick one site approach.
@@ -0,0 +1,47 @@
1
+ ---
2
+ id: postgres
3
+ kind: technology
4
+ title: PostgreSQL (relational database)
5
+ slot: relational-db
6
+ provides: [relational-db, sql, mutable-state, transactions]
7
+ requires: []
8
+ recommends: []
9
+ conflicts: [d1]
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "postgresql.org/docs + the driver you use (e.g. `pg`, `postgres`)"
14
+ read: "Connection/pooling model, transactions, and row-level security if you use it directly."
15
+ - where: "contember-oss/docker-compose.yaml + packages/database"
16
+ read: "A real Postgres-backed setup (Contember Engine) and a fluent query/pool layer over `pg`."
17
+ ---
18
+
19
+ # PostgreSQL
20
+
21
+ ## What it is
22
+
23
+ The general-purpose relational database for stacks that need a **full SQL server**
24
+ (rich types, transactions, extensions, mature tooling) rather than edge SQLite.
25
+ Required by `contember`; the default store when you run a **Node backend** instead
26
+ of Workers + D1.
27
+
28
+ ## When & why to use
29
+
30
+ - You adopt `contember` (it mandates Postgres) or any Node backend needing a real RDBMS.
31
+ - You need Postgres-specific features (JSONB, full-text search, extensions, strong transactional guarantees) D1/SQLite lacks.
32
+
33
+ ## When NOT to use
34
+
35
+ - **Cloudflare Workers-first** apps → use `d1` (edge SQLite, local-first, no server). Workers can't open a raw Postgres TCP connection without Hyperdrive/a proxy.
36
+ - A small local-first app where SQLite is plenty.
37
+
38
+ ## Impacts & gotchas
39
+
40
+ - You **run and operate a Postgres server** (managed service, container, or self-hosted) — connection strings, pooling, backups.
41
+ - From Workers, direct connections need **Hyperdrive or an HTTP proxy**; the natural home is a long-lived Node process.
42
+ - Migrations are yours (or your framework's, e.g. Contember's diff-generated ones).
43
+
44
+ ## Relationships — why
45
+
46
+ - **conflicts `d1`** — same `relational-db` slot; one relational store per app. Postgres = server-side RDBMS, D1 = edge SQLite.
47
+ - **required by `contember`** — its schema/migration/ACL layer is Postgres-specific.
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: preact
3
+ kind: technology
4
+ title: Preact (small React-compatible UI framework)
5
+ slot: ui-framework
6
+ provides: [ui-framework, components]
7
+ requires: []
8
+ recommends: [tailwind]
9
+ conflicts: [react]
10
+ npm: ["preact@^10"]
11
+ status: stable
12
+ sources:
13
+ - where: "preactjs.com + installed preact types"
14
+ read: "The Preact 10 API, `preact/hooks`, and the differences from React (events, `jsxImportSource: preact`)."
15
+ - where: "pletivo islands + preactjs.com"
16
+ read: "Where Preact shows up in our stack: pletivo's islands run on Preact."
17
+ ---
18
+
19
+ # Preact
20
+
21
+ ## What it is
22
+
23
+ A ~4 KB React-compatible UI framework. Largely JSX/hooks-compatible with React but
24
+ smaller, with subtle differences. In our stack, `pletivo`'s islands run on Preact.
25
+
26
+ ## When & why to use
27
+
28
+ - You want a tiny client runtime (islands, embeddable widgets) — e.g. `pletivo` islands.
29
+ - Bundle size on the client matters more than the React ecosystem's breadth.
30
+
31
+ ## When NOT to use
32
+
33
+ - You need the full React ecosystem (`react-router`, `buzola`, `bindx`, Contember admin all assume React) → use `react`.
34
+
35
+ ## Impacts & gotchas
36
+
37
+ - `jsxImportSource: preact` in tsconfig; `preact/hooks` for hooks. **Don't mix React and Preact in one bundle.**
38
+ - **DOM-standard event names** (`onchange`, not React's `onChange`) — an easy bug when porting React code.
39
+ - React-only libs (Suspense-dependent, `bindx`, RRv7) won't run on Preact.
40
+
41
+ ## Relationships — why
42
+
43
+ - **conflicts `react`** — same `ui-framework` slot; one per app.
44
+ - **recommends `tailwind`** for styling; used by `pletivo`'s islands (no hard requirer — it's the Preact option for island-style UIs).
@@ -0,0 +1,137 @@
1
+ ---
2
+ id: propustka
3
+ kind: technology
4
+ title: Propustka — native login, authorization and audit
5
+ slot: authz
6
+ provides: [authentication, sso, authorization, audit, api-tokens]
7
+ requires: [cloudflare, d1]
8
+ recommends: [vozka, buzola]
9
+ conflicts: [cloudflare-access, auth-password-kv]
10
+ npm: ["@propustka/client@^0.0.6", "@propustka/core@^0.0.6"]
11
+ status: wip
12
+ sources:
13
+ - where: "propustka — README.md, architecture.md, packages/core/src/, packages/worker/src/"
14
+ read: "Native session/login architecture, app gates, token/JWKS contract, central human identity and D1 policy/audit model."
15
+ - where: "@propustka/client (installed packages/client/src/{session,client,fake,types}.ts)"
16
+ read: "The current PropustkaAuth, AuthContext, AppGates, setCookie/loginUrl and FakeIamClient surfaces. Version 0.x moves quickly; read the source."
17
+ - where: "propustka examples"
18
+ read: "Runtime gates, local operator context, SSO redirects, response cookies, schemas, and IAM bindings."
19
+ ---
20
+
21
+ # Propustka
22
+
23
+ ## What it is
24
+
25
+ The native identity, authorization and audit service for Cloudflare Workers apps.
26
+ There is **no Cloudflare Access edge** in front of a native Propustka app.
27
+ Propustka owns human SSO sessions, per-app permission tokens, `px_` service keys,
28
+ authorization policy and audit logs.
29
+
30
+ An app integrates over an `IAM` Worker-to-Worker service binding and uses
31
+ `PropustkaAuth` as its single request-auth seam. `PropustkaAuth` applies ordered
32
+ per-path gates, verifies cached permission JWTs locally against Propustka's JWKS,
33
+ and only calls the binding when it must exchange a session/key or write audit.
34
+
35
+ ## When & why to use
36
+
37
+ - An internal Cloudflare app needs human login plus role/scope-based permissions.
38
+ - The same API supports logged-in people and `px_` service credentials.
39
+ - You want a central people directory, app-defined roles/scopes, revocable keys
40
+ and an audit trail.
41
+ - The target Cloudflare account already runs Propustka (normally alongside Vozka).
42
+
43
+ ## When NOT to use
44
+
45
+ - The app is not a Cloudflare Worker; the integration is a service binding.
46
+ - There are no real authz/audit needs and a central IAM dependency is unjustified.
47
+ - The product requires app-owned signup/password recovery; use a product auth
48
+ system instead.
49
+ - Strict hierarchical scopes are fundamental. Propustka scope dimensions are
50
+ flat; containment must be resolved by the app.
51
+
52
+ ## Runtime integration
53
+
54
+ ```ts
55
+ import type { AppGates, IamRpc } from '@propustka/client'
56
+ import { PropustkaAuth } from '@propustka/client'
57
+
58
+ const gates: AppGates = {
59
+ rules: [
60
+ { path: '/rpc', kind: 'service' },
61
+ { path: '/rpc', kind: 'human' },
62
+ ],
63
+ }
64
+
65
+ const result = await new PropustkaAuth(env.IAM as IamRpc, APP_ID, {
66
+ issuer: env.PROPUSTKA_URL,
67
+ gates,
68
+ }).authenticate(request)
69
+
70
+ if (!result.ok) {
71
+ return Response.json(
72
+ { error: { message: result.reason, loginUrl: result.loginUrl } },
73
+ { status: result.status },
74
+ )
75
+ }
76
+
77
+ if (!result.context.can('site.write', { type: 'site', value: siteId })) {
78
+ return new Response('Forbidden', { status: 403 })
79
+ }
80
+
81
+ ctx.waitUntil(result.context.audit({
82
+ action: 'site.write',
83
+ resourceType: 'site',
84
+ resourceId: siteId,
85
+ }))
86
+ ```
87
+
88
+ When `result.setCookie` is present, append it to the outgoing response. It is the
89
+ short-lived per-app `px_token`; dropping it forces repeated session exchanges and
90
+ can make login appear broken.
91
+
92
+ ## Gates and login behavior
93
+
94
+ - `AppGates` are **runtime code**, not deploy-time schema. They replaced
95
+ Cloudflare Access policies and are passed directly to `PropustkaAuth`.
96
+ - Rules are ordered. A common protected path has `service` first and `human`
97
+ second, so a present service key is terminal while a missing key falls through
98
+ to the browser session.
99
+ - A `public` rule creates an anonymous context. A path matching no rule is denied
100
+ (`no_rule`) — native auth is fail-closed.
101
+ - A human miss includes `loginUrl`. The SPA should redirect there and set its
102
+ return URL to the current page, with a short bounce guard so an unprovisioned
103
+ identity does not loop forever.
104
+
105
+ ## Authorization model
106
+
107
+ - Keep action ids, scope dimensions and the app id as shared constants. Runtime
108
+ `can()` checks and Vozka's `AppSchema` must use the same vocabulary.
109
+ - `can(action, scope?)` and `scopedTo(action, dimension)` are local pure checks on
110
+ the minted permission set. `scopedTo` returns `null` (all), `[]` (none), or an
111
+ exact list.
112
+ - Audit is the operation that reaches the binding on the hot path; schedule it
113
+ with `ctx.waitUntil`.
114
+ - The app's `schema` (actions/scopes/roles) is reconciled by Vozka during deploy.
115
+
116
+ ## Local development and opice
117
+
118
+ Do not require a live IAM Worker locally. An explicit `DEV` var should select a
119
+ deterministic app-local `AuthContext` for a known operator persona. Current
120
+ `FakeIamClient` covers management methods such as principal listing and key
121
+ issue/revoke; it is **not** the replacement for `PropustkaAuth.authenticate()`.
122
+
123
+ Omit the external `IAM` service binding in the local resource graph when the
124
+ local runtime cannot resolve it. Off-local, fail loudly if `IAM` or
125
+ `PROPUSTKA_URL` is missing.
126
+
127
+ ## Relationships — why
128
+
129
+ - **requires `cloudflare`** — request auth and audit use a Worker service binding.
130
+ - **requires `d1`** — the separately deployed Propustka service stores identity,
131
+ policy, sessions and audit data in D1.
132
+ - **conflicts `cloudflare-access`** — native Propustka replaces the Access edge;
133
+ combining both creates two competing login/gate systems.
134
+ - **conflicts `auth-password-kv`** — Propustka owns login and sessions; do not add
135
+ a parallel app-owned password session.
136
+ - **recommends `vozka`** for schema reconciliation and **`buzola`** for internal
137
+ app SPAs.
@@ -0,0 +1,55 @@
1
+ ---
2
+ id: queue
3
+ kind: technology
4
+ title: Cloudflare Queues (async buffering)
5
+ slot: null
6
+ provides: [message-queue, async-buffering]
7
+ requires: [cloudflare]
8
+ recommends: [r2, oblaka]
9
+ conflicts: []
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/queues (producer send, consumer queue() handler, batching, retries)"
14
+ read: "Producer .send()/.sendBatch(), the queue(batch, env) consumer handler shape, batch size/retry config."
15
+ ---
16
+
17
+ # Cloudflare Queues
18
+
19
+ ## What it is
20
+
21
+ Managed async message queue to **decouple requests/schedules from slow or bursty
22
+ work**. A producer `env.QUEUE.send(msg)` returns fast; a consumer
23
+ `queue(batch, env)` processes messages with retries — e.g. write payloads to R2,
24
+ or run one bounded background audit while D1 tracks its lease/status.
25
+
26
+ ## When & why to use
27
+
28
+ - Bursty/spiky ingest (error storms, webhooks) where the request must return immediately (202) and work happens async.
29
+ - Batching expensive writes (R2 puts, D1 upserts) instead of doing them per-request.
30
+ - Long-running background jobs that need retry and bounded concurrency without
31
+ tying their lifetime to an HTTP `waitUntil`.
32
+
33
+ ## When NOT to use
34
+
35
+ - Work must complete before the response (synchronous result needed).
36
+ - Trivial, low-volume writes — the queue is overhead.
37
+
38
+ ## Impacts & gotchas
39
+
40
+ - Export a **`queue(batch, env)`** handler from the Worker alongside `fetch`. Check the lopata/RRv7 example for the exact export shape.
41
+ - Delivery is **at least once**. Make the consumer idempotent; for long jobs use a
42
+ D1 lease/attempt token and explicit terminal states so a retry cannot run two
43
+ copies or overwrite a newer attempt.
44
+ - Handle every message explicitly with `message.ack()` or `message.retry()` and
45
+ catch failures per message. An uncaught exception can retry the whole batch;
46
+ a message with no disposition is retried according to the consumer policy.
47
+ - Configure batch size, timeout, retry delay, max retries, concurrency and
48
+ visibility timeout from the real job envelope. A single expensive job often
49
+ wants `maxBatchSize: 1`.
50
+ - For ingest, return **202 fast** and let the consumer do R2 + D1. See
51
+ `write-split`. Do not force that batching shape onto one-job-per-message work.
52
+
53
+ ## Relationships — why
54
+
55
+ - **requires `cloudflare`** — CF binding. **recommends `r2`** (the usual payload sink) and **`oblaka`** (declare producer + consumer).
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: r2
3
+ kind: technology
4
+ title: Cloudflare R2 (object storage)
5
+ slot: null
6
+ provides: [object-storage, blobs]
7
+ requires: [cloudflare]
8
+ recommends: [oblaka, queue]
9
+ conflicts: []
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/r2 (Workers binding: get/put/delete, multipart)"
14
+ read: "The R2 binding API for storing/retrieving objects from a Worker."
15
+ ---
16
+
17
+ # Cloudflare R2
18
+
19
+ ## What it is
20
+
21
+ S3-compatible object storage with no egress fees. Used for **large/append-style
22
+ payloads** — full event bodies, uploads, source maps — keyed by id. Bound as
23
+ `env.R2`.
24
+
25
+ ## When & why to use
26
+
27
+ - Storing payloads too big or too numerous for D1 rows (event bodies, files, source maps).
28
+ - Write-heavy blob ingest, especially batched through a `queue`.
29
+
30
+ ## When NOT to use
31
+
32
+ - Small structured records you query by field → `d1`. Hot small keys → `kv`.
33
+
34
+ ## Impacts & gotchas
35
+
36
+ - Objects are addressed by key; design a stable key scheme (e.g. `events/<projectId>/<id>`).
37
+ - Pair with `queue` for bursty writes: enqueue → consumer batches puts to R2 (keeps the request path fast). See `write-split`.
38
+
39
+ ## Relationships — why
40
+
41
+ - **requires `cloudflare`** — CF binding. **recommends `oblaka`** (declare `R2Bucket`) and **`queue`** (buffer bursty payload writes).
@@ -0,0 +1,47 @@
1
+ ---
2
+ id: react-router
3
+ kind: technology
4
+ title: React Router v7 (SSR on Workers)
5
+ slot: router
6
+ provides: [router, ssr, loader-action-data]
7
+ requires: [react, vite]
8
+ recommends: [d1]
9
+ conflicts: [buzola]
10
+ npm: ["react-router@^7.13", "@react-router/dev@^7.13", "@react-router/node@^7.13", "isbot@^5"]
11
+ status: stable
12
+ sources:
13
+ - where: "reactrouter.com (RRv7 SSR/loader/action) + installed package types"
14
+ read: "Current SSR/loader/action API and the EXACT import paths — they move between minors. Verify against the installed version."
15
+ - where: "lopata/examples/react-router/ — the canonical RRv7-SSR-on-Workers example"
16
+ read: "Worker entry, entry.server, route table wired against CF bindings. The #1 ground truth for this decision."
17
+ ---
18
+
19
+ # React Router v7 (SSR on Workers)
20
+
21
+ ## What it is
22
+
23
+ Full-stack React Router v7 doing **server-side rendering on the Worker**:
24
+ `app/routes/*.tsx` with a server `loader`/`action` over CF bindings + a default
25
+ component. Self-contained, no RPC/Contember layer — lopata's own example path.
26
+
27
+ ## When & why to use
28
+
29
+ - Server-rendered app reading data directly in loaders from `context.env.DB` etc.
30
+ - fabrika's default app architecture when SSR and route loaders fit better than a client-rendered SPA.
31
+
32
+ ## When NOT to use
33
+
34
+ - A pure client SPA (no SSR), especially a Contember-style admin → `buzola`.
35
+
36
+ ## Impacts & gotchas — the load-bearing ones (anti-staleness)
37
+
38
+ - **Worker entry** (`workers/app.ts`): `createRequestHandler` from **`react-router`** (NOT `@react-router/dev/server`) over `virtual:react-router/server-build`; `fetch` passes `{ env, ctx }` as the load context so loaders read `context.env.DB`. **This is the #1 thing stale memory gets wrong** — check the lopata example.
39
+ - **SSR renderer** (`app/entry.server.tsx`): `renderToReadableStream` from **`react-dom/server.browser`** (NOT `react-dom/server`) + `<ServerRouter>`.
40
+ - **Route table** (`app/routes.ts`): from `@react-router/dev/routes` — `export default [ index('routes/home.tsx'), route('cart','routes/cart.tsx'), … ]`. **Each route URL must match the opice skeleton `url` (contract).**
41
+ - `react-router.config.ts`: `{ ssr: true }`. tsconfig: RRv7 typegen via `rootDirs` (`.react-router/types`).
42
+
43
+ ## Relationships — why
44
+
45
+ - **requires `react` + `vite`** (it's a React framework with a vite plugin).
46
+ - **recommends `d1`** — loaders/actions read/write D1 directly.
47
+ - **conflicts `buzola`** — same `router` slot. RRv7 = SSR; buzola = SPA. One router per stack.
@@ -0,0 +1,37 @@
1
+ ---
2
+ id: react
3
+ kind: technology
4
+ title: React (UI framework)
5
+ slot: ui-framework
6
+ provides: [ui-framework, components]
7
+ requires: []
8
+ recommends: [react-router, tailwind]
9
+ conflicts: [preact]
10
+ npm: ["react@^19", "react-dom@^19"]
11
+ status: stable
12
+ sources:
13
+ - where: "react.dev + installed react/react-dom types"
14
+ read: "Current React 19 APIs; for SSR-on-Workers note the server.browser render entry (see react-router)."
15
+ ---
16
+
17
+ # React
18
+
19
+ ## What it is
20
+
21
+ The UI framework for fabrika's generated apps (React 19). Paired with a router:
22
+ `react-router` renders SSR on the Worker, while `buzola` builds a client SPA that
23
+ the Worker can serve as static assets.
24
+
25
+ ## When & why to use
26
+
27
+ - Default UI layer — the routers, opice selector conventions, and design-token mockups all assume React.
28
+
29
+ ## Impacts & gotchas
30
+
31
+ - SSR on Workers renders via **`renderToReadableStream` from `react-dom/server.browser`** (NOT `react-dom/server`) — the Web Streams path. (Detail lives on `react-router`.)
32
+ - Build accessible markup: every interactive element needs an accessible name (`<label>`/text/`aria-label`) so opice has stable selectors (see `opice`).
33
+
34
+ ## Relationships — why
35
+
36
+ - **recommends `react-router`** (default SSR-on-Workers router; `buzola` is the SPA alternative, both require react) **+ `tailwind`** (styling).
37
+ - **conflicts `preact`** — same `ui-framework` slot; `preact` is the smaller alternative (pletivo's islands run on it).
@@ -0,0 +1,51 @@
1
+ ---
2
+ id: roj
3
+ kind: technology
4
+ title: roj — agent SDK (@roj-ai/*) for building AI agents
5
+ slot: agent-sdk
6
+ provides: [agent-runtime, llm-orchestration, streaming, sessions, event-sourcing, plugins]
7
+ requires: []
8
+ recommends: [cloudflare, durable-objects, d1, r2, react]
9
+ conflicts: []
10
+ npm: ["@roj-ai/sdk@^0.1.18", "@roj-ai/transport@^0.1.18", "@roj-ai/client@^0.1.18", "@roj-ai/client-react@^0.1.18"]
11
+ status: wip
12
+ sources:
13
+ - where: "roj/CLAUDE.md + packages/sdk/src/index.ts"
14
+ read: "The plugin DSL, event sourcing, preset/agent builders, session management, and LLM provider surface (Anthropic/OpenRouter/mock)."
15
+ - where: "roj/packages/demo/"
16
+ read: "A full e2e example: preset + standalone server + React SPA + snapshot tests."
17
+ ---
18
+
19
+ # roj
20
+
21
+ ## What it is
22
+
23
+ An **agent SDK** (`@roj-ai/*`, the OSS successor to buresh): a TypeScript framework
24
+ for multi-agent systems with persistent sessions, event sourcing, a plugin
25
+ architecture (filesystem/shell/chat/limits/skills/…), and streaming LLM inference.
26
+ Apps define **presets** (agents + plugins + system prompt), wire an LLM provider
27
+ (Anthropic native, OpenRouter, or mock), and expose a REST/WebSocket API. It is
28
+ LLM-provider-agnostic and host-agnostic. It can be composed with Workers, Durable
29
+ Objects, WebSockets, D1, and R2 for a production deployment.
30
+
31
+ ## When & why to use
32
+
33
+ - **Building AI agents/assistants** into an app (codegen, research, automation) with multi-turn, stateful tool calls.
34
+ - You want **event-sourced** sessions (auditable, replayable) and pluggable tool/skill libraries without provider lock-in.
35
+
36
+ ## When NOT to use
37
+
38
+ - **One-off inference** (single prompt→response) → use the Anthropic SDK / OpenRouter directly.
39
+ - No multi-turn/stateful needs.
40
+
41
+ ## Impacts & gotchas
42
+
43
+ - **Client/server split + async sessions** — agents run server-side; the client creates a session and streams updates over WebSocket. Plan UX for async + reconnection.
44
+ - **LLM key handling** — provide and encrypt the provider key; never log it. The default uses Anthropic/Claude.
45
+ - **Tool/sandbox security** — plugins execute shell/filesystem in a sandbox (E2B / `edvabe` locally); sanitize tool inputs.
46
+ - **DO hibernation** — on CF, the in-memory runtime is rebuilt from the event log via an alarm; don't delete session records.
47
+
48
+ ## Relationships — why
49
+
50
+ - **slot `agent-sdk`** — the agent layer; composes with any UI/backend.
51
+ - **recommends `cloudflare` + `durable-objects`** (one DO per agent instance), **`d1`/`r2`** (session/artifact storage), **`react`** (`@roj-ai/client-react` chat UI). The SDK itself requires none of these — it runs on Bun/Node too. Pairs with `edvabe` for local sandboxed tool execution.
@@ -0,0 +1,49 @@
1
+ ---
2
+ id: sentry
3
+ kind: technology
4
+ title: Sentry (error tracking)
5
+ slot: error-tracking
6
+ provides: [error-tracking, exception-capture, source-maps, session-replay]
7
+ requires: []
8
+ recommends: [observability]
9
+ conflicts: []
10
+ npm: ["@sentry/cloudflare", "@sentry/react", "@sentry/vite-plugin", "toucan-js"]
11
+ status: stable
12
+ sources:
13
+ - where: "Sentry Cloudflare Workers and React SDK documentation"
14
+ read: "SDK initialization, environment and release tags, trace propagation, logger integration, and error boundaries."
15
+ - where: "Sentry docs: Cloudflare Workers (@sentry/cloudflare or toucan-js), source-map upload (@sentry/vite-plugin / wrangler upload_source_maps), beforeSend + fingerprinting"
16
+ read: "Init options, source maps with SENTRY_AUTH_TOKEN, redaction, and fingerprinting for grouping."
17
+ ---
18
+
19
+ # Sentry
20
+
21
+ ## What it is
22
+
23
+ The error-tracking option: capture exceptions + stack traces (with source maps) and
24
+ group them. On Workers via **`@sentry/cloudflare`** (or `toucan-js` for a lighter
25
+ footprint), in the browser via **`@sentry/react`**, with source maps uploaded by
26
+ **`@sentry/vite-plugin`** or wrangler's `upload_source_maps` (gated on
27
+ `SENTRY_AUTH_TOKEN`). Plugs into `structured-logging` as a handler that forwards
28
+ `logger.error` to `captureException` at warn+.
29
+
30
+ ## When & why to use
31
+
32
+ - You want managed error aggregation + alerting (and optional browser session replay) rather than building it.
33
+ - Production apps where un-grouped, un-sourcemapped stack traces aren't enough.
34
+
35
+ ## When NOT to use
36
+
37
+ - You specifically want **self-hosted** error tracking (the `cf-fat`/error-tracker style — store issues in D1/DO, query via admin) for data residency/cost.
38
+ - Trivial apps where structured logs + AE metrics are enough.
39
+
40
+ ## Gotchas
41
+
42
+ - **Redact in `beforeSend`** — strip auth headers / PII before send; errors easily carry secrets.
43
+ - **`enableRpcTracePropagation: true`** — or Durable Object / service-binding errors appear isolated from the originating request.
44
+ - **Source maps must be uploaded in CI** (SENTRY_AUTH_TOKEN) or stacks are minified-useless.
45
+ - **Control cardinality with `fingerprint`** — don't put request ids in the error message or grouping explodes; use intent-based fingerprints. Use `replaysOnErrorSampleRate`, not full session replay, for cost.
46
+
47
+ ## Relationships — why
48
+
49
+ - **slot `error-tracking`** — the errors arm of `observability`; **recommends `observability`** (errors enrich traces/metrics). Bridged from `structured-logging` via a Sentry log handler.