spine-framework 0.4.4 → 0.5.0-rc.4

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 (283) hide show
  1. package/.env.example +13 -3
  2. package/.framework/cli/commands/bootstrap.ts +392 -0
  3. package/.framework/cli/commands/dev.ts +91 -218
  4. package/.framework/cli/commands/doctor.ts +132 -26
  5. package/.framework/cli/commands/generate.ts +38 -298
  6. package/.framework/cli/commands/init.ts +20 -1
  7. package/.framework/cli/commands/migrate.ts +36 -30
  8. package/.framework/cli/commands/migrations.ts +16 -56
  9. package/.framework/cli/commands/upgrade.ts +35 -0
  10. package/.framework/cli/commands/validate-schema.ts +147 -0
  11. package/.framework/cli/context.ts +223 -110
  12. package/.framework/cli/index.ts +6 -0
  13. package/.framework/cli/lib/project-files.ts +241 -1
  14. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  15. package/.framework/functions/_shared/logging.ts +562 -224
  16. package/.framework/functions/_shared/middleware.ts +47 -44
  17. package/.framework/functions/admin-data.ts +39 -28
  18. package/.framework/functions/ai-agents.ts +10 -6
  19. package/.framework/functions/api-keys.ts +301 -122
  20. package/.framework/functions/apps.ts +43 -26
  21. package/.framework/functions/cil-ingest.ts +48 -15
  22. package/.framework/functions/debug-auth.ts +14 -8
  23. package/.framework/functions/deliveries.ts +39 -12
  24. package/.framework/functions/embeddings.ts +145 -76
  25. package/.framework/functions/integration-outbound.ts +67 -27
  26. package/.framework/functions/integrations.ts +254 -127
  27. package/.framework/functions/logs.ts +273 -133
  28. package/.framework/functions/observability.ts +277 -147
  29. package/.framework/functions/pipeline-executions.ts +1 -1
  30. package/.framework/functions/pipelines.ts +14 -9
  31. package/.framework/functions/prompt-configs.ts +7 -4
  32. package/.framework/functions/roles.ts +8 -5
  33. package/.framework/functions/security.ts +30 -17
  34. package/.framework/functions/system-cron.ts +240 -127
  35. package/.framework/functions/tests.ts +7 -6
  36. package/.framework/functions/timers.ts +310 -107
  37. package/.framework/functions/triggers.ts +28 -13
  38. package/.framework/functions/types.ts +13 -10
  39. package/.framework/migrations/069_log_stats_window.sql +54 -0
  40. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  41. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  42. package/.framework/migrations/072_slug_autofill.sql +77 -0
  43. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  44. package/.framework/src/apps/admin/index.tsx +1141 -775
  45. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  46. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  47. package/.framework/src/components/admin/AdminSidebar.tsx +88 -79
  48. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  49. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  50. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  51. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  52. package/.framework/src/components/layout/Sidebar.tsx +300 -177
  53. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  54. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  55. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  56. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  57. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  58. package/.framework/src/components/ui/sheet.tsx +24 -26
  59. package/.framework/src/components/ui/textarea.tsx +74 -48
  60. package/.framework/src/hooks/useEntityList.ts +12 -9
  61. package/.framework/src/hooks/useListSchema.ts +108 -58
  62. package/.framework/src/instructions/README.md +57 -0
  63. package/.framework/src/instructions/ai-agents.md +69 -0
  64. package/.framework/src/instructions/alerts.md +80 -0
  65. package/.framework/src/instructions/api-keys.md +87 -0
  66. package/.framework/src/instructions/apps.md +77 -0
  67. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  68. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  69. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  70. package/.framework/src/instructions/concepts/automation.md +186 -0
  71. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  72. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  73. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  74. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  75. package/.framework/src/instructions/concepts/observability.md +116 -0
  76. package/.framework/src/instructions/concepts/permissions.md +150 -0
  77. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  78. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  79. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  80. package/.framework/src/instructions/deliveries.md +59 -0
  81. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  82. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  83. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  84. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  85. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  86. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  87. package/.framework/src/instructions/integrations.md +96 -0
  88. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  89. package/.framework/src/instructions/invites.md +60 -0
  90. package/.framework/src/instructions/kb.md +80 -0
  91. package/.framework/src/instructions/logs.md +77 -0
  92. package/.framework/src/instructions/observability.md +61 -0
  93. package/.framework/src/instructions/operations/deployment.md +115 -0
  94. package/.framework/src/instructions/operations/migrations.md +125 -0
  95. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  96. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  97. package/.framework/src/instructions/operations/upgrade.md +116 -0
  98. package/.framework/src/instructions/pipeline-executions.md +62 -0
  99. package/.framework/src/instructions/pipelines.md +78 -0
  100. package/.framework/src/instructions/prompts.md +95 -0
  101. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  102. package/.framework/src/instructions/reference/api/auth.md +113 -0
  103. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  104. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  105. package/.framework/src/instructions/reference/cli.md +124 -0
  106. package/.framework/src/instructions/reference/database.md +132 -0
  107. package/.framework/src/instructions/reference/design-schema.md +280 -0
  108. package/.framework/src/instructions/reference/env-vars.md +133 -0
  109. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  110. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  111. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  112. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  113. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  114. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  115. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  116. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  117. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  118. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  119. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  120. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  121. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  122. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  123. package/.framework/src/instructions/reference/functions/index.md +103 -0
  124. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  125. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  126. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  127. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  128. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  129. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  130. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  131. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  132. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  133. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  134. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  135. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  136. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  137. package/.framework/src/instructions/reference/functions/security.md +140 -0
  138. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  139. package/.framework/src/instructions/reference/functions/system.md +106 -0
  140. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  141. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  142. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  143. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  144. package/.framework/src/instructions/reference/functions/types.md +146 -0
  145. package/.framework/src/instructions/reference/seeds.md +126 -0
  146. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  147. package/.framework/src/instructions/reference/spine-config.md +114 -0
  148. package/.framework/src/instructions/roles.md +80 -0
  149. package/.framework/src/instructions/runtime-detail.md +70 -0
  150. package/.framework/src/instructions/runtime-list.md +77 -0
  151. package/.framework/src/instructions/security.md +67 -0
  152. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  153. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  154. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  155. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  156. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  157. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  158. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  159. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  160. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  161. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  162. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  163. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  164. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  165. package/.framework/src/instructions/testing.md +59 -0
  166. package/.framework/src/instructions/timers.md +82 -0
  167. package/.framework/src/instructions/triggers.md +69 -0
  168. package/.framework/src/instructions/types.md +93 -0
  169. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  170. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  171. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  172. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  173. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  174. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  175. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  176. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  177. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  178. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  179. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  180. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  181. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  182. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  183. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  184. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  185. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  186. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  187. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  188. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  189. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  190. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  191. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  192. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  193. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  194. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  195. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  196. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  197. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  198. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  199. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  200. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  201. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  202. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  203. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  204. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  205. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  206. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  207. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  208. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  209. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  210. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  211. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  212. package/.framework/templates/agent/AGENTS.md +150 -0
  213. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  214. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  215. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  216. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  217. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  218. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  219. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  220. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  221. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  222. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  223. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  224. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  225. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  226. package/.framework/templates/project.json +4 -1
  227. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  228. package/.framework/tests/unit/agent-files.test.ts +355 -0
  229. package/.framework/tests/unit/agent-runner.test.ts +479 -44
  230. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  231. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  232. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  233. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  234. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  235. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  236. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  237. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  238. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  239. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  240. package/.framework/tests/unit/system-cron.test.ts +240 -0
  241. package/.framework/tests/unit/timers.test.ts +159 -0
  242. package/README.md +125 -166
  243. package/config/tsconfig.cli.json +2 -2
  244. package/package.json +15 -4
  245. package/templates/project/package-lock.json +2 -2
  246. package/.framework/docs/admin-routes.md +0 -76
  247. package/.framework/docs/api-docs-progress.md +0 -38
  248. package/.framework/docs/api-governance.md +0 -146
  249. package/.framework/docs/api-testing-results.md +0 -212
  250. package/.framework/docs/assembly-launch-guide.md +0 -138
  251. package/.framework/docs/audit-results.md +0 -590
  252. package/.framework/docs/db-api-inventory.md +0 -95
  253. package/.framework/docs/gap-remediation-backlog.md +0 -103
  254. package/.framework/docs/ui-adoption-verification.md +0 -111
  255. package/.framework/docs/ui-api-coverage.md +0 -84
  256. package/.framework/docs/v2-compatibility-audit.md +0 -228
  257. package/.framework/mcp/index.ts +0 -133
  258. package/.framework/mcp/load-env.ts +0 -60
  259. package/.framework/mcp/shared/app-manifest.ts +0 -275
  260. package/.framework/mcp/shared/db.ts +0 -477
  261. package/.framework/mcp/shared/kb.ts +0 -118
  262. package/.framework/mcp/tools/architect.ts +0 -431
  263. package/.framework/mcp/tools/cil/context.ts +0 -345
  264. package/.framework/mcp/tools/cil/graph.ts +0 -242
  265. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  266. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  267. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  268. package/.framework/mcp/tools/cil/project.ts +0 -791
  269. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  270. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  271. package/.framework/mcp/tools/cil/search.ts +0 -97
  272. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  273. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  274. package/.framework/mcp/tools/kb/delete.ts +0 -139
  275. package/.framework/mcp/tools/kb/index.ts +0 -52
  276. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  277. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  278. package/.framework/mcp/tools/kb/publish.ts +0 -100
  279. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  280. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  281. package/.framework/mcp/tools/kb/search.ts +0 -101
  282. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  283. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,280 @@
1
+ ---
2
+ title: Design Schema Reference
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/src/types/types.ts
9
+ - .framework/functions/_shared/schema-utils.ts
10
+ - .framework/functions/_shared/permissions.ts
11
+ - .framework/functions/admin-data.ts
12
+ - .framework/functions/types.ts
13
+ - .framework/cli/commands/validate-schema.ts
14
+ - .framework/src/components/shared/FieldRenderer.tsx
15
+ - .framework/src/components/app-shell/GenericListPage.tsx
16
+ - .framework/src/components/app-shell/GenericDetailPage.tsx
17
+ - .framework/src/hooks/useForm.ts
18
+ ---
19
+
20
+ # Design Schema Reference
21
+
22
+ Exhaustive field-level spec for `public.types.design_schema` — the JSON every
23
+ type carries and every runtime record is stamped with at creation. The mental
24
+ model is in [Design Schema](../concepts/design-schema.md); this page is the
25
+ lookup table. The TypeScript source of truth is `DesignSchema`,
26
+ `FieldDefinition`, `ViewFieldConfig`, `ListView`, `DetailView`, and
27
+ `FunctionalityBindings` in `.framework/src/types/types.ts`.
28
+
29
+ ## Top-level shape
30
+
31
+ | Key | Type | Required | Purpose |
32
+ |---|---|---|---|
33
+ | `fields` | `Record<string, FieldDefinition>` | yes | Flat map of `snake_case` field names to definitions |
34
+ | `views` | `Record<string, View>` | yes for generic apps | `default_list` and `default_detail` drive the generic UI |
35
+ | `record_permissions` | `Record<string, string[]>` | yes | Base CRUD grant per role slug |
36
+ | `scope` | `"account" \| "platform"` | no (default `"account"`) | Which account owns records of this type at create |
37
+ | `functionality` | `FunctionalityBindings` or extensions | no | Automation bindings; `null` until a binding is confirmed live |
38
+ | `display_field` | `string` | no | Field used for record titles in detail pages |
39
+
40
+ `validate-schema` enforces: `fields` non-empty; every field has `data_type`,
41
+ `label`, `required`; `record_permissions` grants at least `system-admin` or
42
+ `system_admin`; `scope` is `account` or `platform`
43
+ (`.framework/cli/commands/validate-schema.ts`).
44
+
45
+ ## `fields.<name>` — FieldDefinition
46
+
47
+ | Key | Type | Notes |
48
+ |---|---|---|
49
+ | `data_type` | enum, see below | required |
50
+ | `label` | string | required |
51
+ | `required` | boolean | required; enforced client-side by `useForm` (`.framework/src/hooks/useForm.ts`), propagated into `validation_schema` |
52
+ | `system` | boolean | `true` → top-level record column; absent/`false` → `record.data.<name>` |
53
+ | `placeholder` | string | input hint |
54
+ | `description` | string | help text |
55
+ | `name` | string | optional field alias |
56
+ | `rows` | number | textarea height hint |
57
+ | `min`, `max`, `step` | number | range input bounds |
58
+ | `readonly`, `disabled` | boolean | form-state hints |
59
+ | `validation` | object | constraint keys, see below |
60
+ | `options` | `(string \| {value, label})[]` | required for `select`, `multiselect`, `radio` |
61
+ | `permissions` | `Record<string, string[]>` | per-role field overrides, `read`/`write` only |
62
+
63
+ ### `data_type` values
64
+
65
+ The authoritative dispatch is `sanitizeFieldData` in
66
+ `.framework/functions/_shared/schema-utils.ts`. Unknown types are **not
67
+ rejected** — the value passes through unsanitized and unvalidated.
68
+
69
+ **Where the sanitizer runs.** `sanitizeFieldData` executes only on the
70
+ `admin-data` **update** path, only for `data.*` (non-system) fields, and only
71
+ for **non-system-admin** principals. `create` performs no field sanitization —
72
+ the request body is inserted as-is — and system columns (`system: true`,
73
+ including `select` fields like `status`) are never option-validated
74
+ server-side. `required` is enforced client-side by `useForm` only. So
75
+ "sanitizer behavior throws" below means: on a `data.*` update by a non-admin
76
+ caller. The trust boundary is the client + field permissions, not
77
+ `validation_schema` — treat API writes as unvalidated input.
78
+
79
+ | `data_type` | Stored as | Sanitizer behavior |
80
+ |---|---|---|
81
+ | `text` | string | trim, strip control chars; `minLength` throws, `maxLength` truncates, `pattern` throws |
82
+ | `textarea` | string | trim, strip control chars; `minLength`/`maxLength` |
83
+ | `rich_text` | HTML string | tag allowlist (`p br strong em u ol ul li a h1–h6`), strips `<script>` and `on*=` handlers; `minLength`/`maxLength` |
84
+ | `email` | string | lowercase + trim; must match `^[^\s@]+@[^\s@]+\.[^\s@]+$` |
85
+ | `phone` | string | strips non-`0-9+` chars; optional `pattern` |
86
+ | `url` | string | parses URL; `http:`/`https:` only; normalized via `URL.toString()` |
87
+ | `number` | number | `Number()` coercion; `min`/`max` throw; `step` snaps down to step |
88
+ | `currency` | number | `Number()` coercion rounded to 2 decimals; `min`/`max` throw |
89
+ | `range` | number | same as `number` |
90
+ | `date` | `"YYYY-MM-DD"` | parses any `Date()` input, stores ISO date; `min`/`max` bounds |
91
+ | `datetime` | ISO string | parses any `Date()` input, stores `toISOString()`; `min`/`max` bounds |
92
+ | `boolean` | boolean | `true`/`false` pass through; strings `"true" "1" "yes" "on"` → `true`, `"false" "0" "no" "off"` → `false`; anything else throws |
93
+ | `checkbox` | boolean | alias of `boolean` |
94
+ | `select` | string | trim; when `options` are declared the value must match an option (`value` for `{label,value}` entries) or it throws |
95
+ | `multiselect` | string[] | accepts an array or comma-separated string; dedupes; each element must match an option; `validation.max` truncates the list |
96
+ | `radio` | string | same sanitizer as `select` |
97
+ | `color` | string | must match `#rgb`/`#rrggbb`; normalized to 6-digit uppercase hex |
98
+ | `file` | object | must be an object with `.name`; `maxSize` and `allowedTypes` (MIME) enforced; filename sanitized to `[a-zA-Z0-9.-]` |
99
+ | `image` | object | same sanitizer as `file` (`maxWidth`/`maxHeight` are declared but not enforced) |
100
+ | `json` | any JSON value | string input is `JSON.parse`d; rejects script tags, `javascript:`, `eval(`/`function(`, `on*=` handlers, and `__proto__` keys; arbitrary nesting allowed under `data.<name>` |
101
+ | `reference` | string (uuid) | must match the UUID regex; `validation.reference_kind`/`reference_type` describe the target |
102
+ | `address` | object | must be an object; string values are text-sanitized |
103
+
104
+ `formatFieldData` (display formatting) covers only `json`, `date`, `datetime`,
105
+ `currency`, `phone`, `url`, `reference`, `address`, `multiselect`, `boolean` —
106
+ all other types render raw.
107
+
108
+ ### `validation.*` keys
109
+
110
+ Declared on `FieldDefinition.validation`; copied verbatim into the generated
111
+ `validation_schema` by `generateValidationSchema`
112
+ (`.framework/functions/_shared/schema-utils.ts`).
113
+
114
+ | Key | Applies to | Effect |
115
+ |---|---|---|
116
+ | `pattern` | `text`, `phone` | regex must match or the write throws |
117
+ | `minLength`, `maxLength` | `text`, `textarea`, `rich_text` | min throws; max truncates |
118
+ | `min`, `max` | `number`, `currency`, `range`, `date`, `datetime` | bounds; out-of-range throws |
119
+ | `step` | `number`, `range` | value snapped down to step multiple |
120
+ | `integer` | `number` | declared; not enforced by the server sanitizer |
121
+ | `precision` | `number` | declared decimal precision; not enforced server-side |
122
+ | `maxSize` | `file`, `image` | byte-size limit, enforced server-side |
123
+ | `allowedTypes` | `file`, `image` | MIME allowlist, enforced server-side |
124
+ | `maxWidth`, `maxHeight` | `image` | declared; not enforced server-side |
125
+ | `max` | `multiselect` | truncates the list to `max` entries |
126
+ | `currency_code` | `currency` | display currency (e.g. `USD`) |
127
+ | `reference_kind` | `reference` | runtime table of the target (`item`, `person`, `account`, …) |
128
+ | `reference_type` | `reference` | type slug of the target record |
129
+
130
+ ### `options` formats
131
+
132
+ Required for `select`/`multiselect`/`radio`; validated non-empty by
133
+ `validate-schema`. Both forms accepted:
134
+
135
+ ```json
136
+ "options": ["todo", "done"]
137
+ "options": [{ "label": "Bug", "value": "bug" }]
138
+ ```
139
+
140
+ ### `permissions` (field-level overrides)
141
+
142
+ `record_permissions` is the **base, most restrictive** grant: when a role can
143
+ `read`/`update` a record, every field defaults to `read`/`write` unless the
144
+ field declares a `permissions` entry for that role. Field-level actions are
145
+ `read` and `write` only; `create`/`delete` are record-level
146
+ (`.framework/functions/_shared/permissions.ts`, `resolveFirstSurfacePermissions`
147
+ step 5–6). Use field permissions to **loosen** one field beyond the record
148
+ grant or to re-restrict a `system: true` field.
149
+
150
+ ```json
151
+ "fields": {
152
+ "cost": {
153
+ "data_type": "currency", "label": "Cost", "required": false,
154
+ "permissions": { "member": ["read"], "finance": ["read", "write"] }
155
+ }
156
+ }
157
+ ```
158
+
159
+ ### `system` flag semantics
160
+
161
+ `system: true` fields are stored as top-level record columns (`record.title`),
162
+ all others under `record.data.<name>` — see
163
+ [Runtime Records](../concepts/runtime-records.md). Writers split payloads the
164
+ same way (`useEntityRecord` / the update path). Conventionally `title` is
165
+ system; flag `system: true` fields that humans must not edit with a
166
+ `permissions` override restricting `write` to `system_admin`.
167
+
168
+ ## `views`
169
+
170
+ `views` is a map of view names to `ListView | DetailView`. Generic apps
171
+ (`apps.renderer = "generic"`) need `default_list` and `default_detail`; a
172
+ custom app may omit views but loses the generic admin/runtime fallback.
173
+
174
+ ### `default_list` — ListView
175
+
176
+ | Key | Type | Purpose |
177
+ |---|---|---|
178
+ | `type` | `"list"` | required discriminator |
179
+ | `display` | `"table" \| "card" \| "board"` | layout in `GenericListPage` |
180
+ | `label` | string | view title |
181
+ | `fields` | `Record<string, ViewFieldConfig>` | column map, keyed by field name (may include `created_at`/`updated_at`). Non-system fields resolve automatically — key `priority` reads `record.data.priority`; the explicit `data.priority` form also works and filters on `data.<field>` are translated server-side |
182
+ | `default_sort` | `{field, direction: "asc"\|"desc"}` | initial ordering |
183
+ | `filters` | `(string \| EntityFilter)[]` | filter affordances — a bare field-name string is normalized from the field def (`select` → enum with its option values, `boolean` → boolean, else search); or pass full `{key, label, type: "search"\|"enum"\|"boolean", options?: string[]}` objects |
184
+ | `stats` | `{title, type: "count"\|"filter_count", icon?, color?, filter?}[]` | stat cards above the list; `filter` is a `{<field>: <value>}` map matched per row (e.g. `{"status": "todo"}`) |
185
+ | `group_by` | string | field to group rows by (board display) |
186
+
187
+ ### `default_detail` — DetailView
188
+
189
+ | Key | Type | Purpose |
190
+ |---|---|---|
191
+ | `type` | `"detail"` | required discriminator |
192
+ | `label` | string | page title |
193
+ | `sections` | `DetailViewSection[]` | ordered sections of `{title, fields: Record<string, ViewFieldConfig>}` |
194
+
195
+ ### `ViewFieldConfig` — `display_type`
196
+
197
+ Each view field is `{display_type, sortable?, searchable?}`. Implemented
198
+ `display_type` values — enforced by `validate-schema` and rendered by
199
+ `.framework/src/components/shared/FieldRenderer.tsx`:
200
+
201
+ | `display_type` | Context |
202
+ |---|---|
203
+ | `text` | read-only text (list/detail) |
204
+ | `badge` | status pill (list) |
205
+ | `timestamp` | formatted date/time (list/detail) |
206
+ | `currency` | formatted amount |
207
+ | `number` | formatted number |
208
+ | `input` | single-line edit input (detail) |
209
+ | `textarea` | multi-line edit |
210
+ | `rich_text` | rich-text editor |
211
+ | `select`, `multiselect`, `radio` | option pickers |
212
+ | `checkbox`, `switch` | boolean toggles |
213
+ | `date_picker`, `datetime_picker` | date/time pickers |
214
+ | `color_picker` | color input |
215
+ | `file_upload`, `image_upload` | upload widgets |
216
+ | `range_slider` | numeric slider |
217
+
218
+ Reserved in `ViewFieldConfig` but not yet implemented by `FieldRenderer`:
219
+ `rating`, `autocomplete`, `address_form`, `reference_picker` (the type union
220
+ admits them in `.framework/src/types/types.ts`; treat as forward-compatible
221
+ placeholders).
222
+
223
+ ## `record_permissions`
224
+
225
+ ```json
226
+ "record_permissions": {
227
+ "system_admin": ["create", "read", "update", "delete"],
228
+ "admin": ["create", "read", "update"],
229
+ "member": ["read"]
230
+ }
231
+ ```
232
+
233
+ - Actions are `create`, `read`, `update`, `delete` — evaluated per request
234
+ against the caller's role slug.
235
+ - `"all"` is a wildcard role granted to every authenticated principal that
236
+ passed RLS; always evaluated when present
237
+ (`.framework/functions/_shared/permissions.ts`).
238
+ - Evaluation happens against the schema **stamped on the record**, not the
239
+ type row — records created before a schema edit keep the old grant.
240
+ - If a type's `design_schema` omits `record_permissions`, `admin-data` create
241
+ merges a permissive default `{all: [create,read,update,delete]}` before
242
+ stamping (`.framework/functions/admin-data.ts`, `PERMISSIONS_ALL`). An
243
+ explicit empty object denies everything downstream — always author
244
+ deliberately.
245
+
246
+ ## `scope`
247
+
248
+ Read at create time as `designSchema.scope ?? 'account'`
249
+ (`.framework/functions/admin-data.ts`). `"platform"` stamps new records with
250
+ the `spine-system` account id; records created against a parent
251
+ (`thread_id`, `target_id`, `parent_id`) inherit the parent's stamped scope.
252
+ See [Scope and Accounts](../concepts/scope-and-accounts.md).
253
+
254
+ ## `functionality`
255
+
256
+ `FunctionalityBindings` (`.framework/src/types/types.ts`) declares optional
257
+ automation:
258
+
259
+ | Binding | Shape |
260
+ |---|---|
261
+ | `pipelines[]` | `{pipeline_id, trigger: manual\|on_create\|on_update\|on_field_change\|on_delete\|scheduled, field?, condition?, roles[]}` |
262
+ | `ai_agents[]` | `{agent_id, capabilities: (read\|summarize\|suggest\|update)[], trigger, roles[]}` |
263
+ | `embeddings[]` | `{slug, fields[], model, vector_column, trigger}` |
264
+ | `integrations[]` | `{integration_id, sync: bidirectional\|inbound\|outbound, field_map, trigger}` |
265
+ | `constraints[]` | `{type: unique\|conditional_required\|immutable, fields?/field?, condition?, message, after?: create\|update}` |
266
+
267
+ `admin-data` also reads `functionality.lineage`
268
+ (`{parent_fields: string[], link_type: string, children: {[slug]: field}}`)
269
+ on `items` get/list to attach `parents`/`children` — declared per type in the
270
+ seed, interpreted by `attachLineage` in `.framework/functions/admin-data.ts`.
271
+ Set `functionality` to `null` until the binding you need is confirmed live.
272
+
273
+ ## `validation_schema` (generated, never authored)
274
+
275
+ `generateValidationSchema` derives `{fields: {name: {data_type, required,
276
+ ...validation, options, reference_kind?, reference_type?}}}` from
277
+ `design_schema.fields` on every type create and on every update that touches
278
+ `design_schema` (`.framework/functions/types.ts`,
279
+ `.framework/cli/commands/install-app.ts` seed path). Never hand-edit — change
280
+ `design_schema` and let the writer regenerate it.
@@ -0,0 +1,133 @@
1
+ ---
2
+ title: Environment Variables
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .env.example
9
+ - .framework/functions/_shared/principal.ts
10
+ - .framework/functions/_shared/db.ts
11
+ - .framework/functions/_shared/middleware.ts
12
+ - .framework/functions/_shared/trigger-engine.ts
13
+ - .framework/functions/_shared/resolve-credentials.ts
14
+ - .framework/functions/system-cron.ts
15
+ - .framework/functions/pipeline-runner-background.ts
16
+ - .framework/functions/security.ts
17
+ - .framework/functions/invites.ts
18
+ - .framework/functions/adapters/resend-outbound.ts
19
+ - .framework/cli/context.ts
20
+ - .framework/cli/commands/migrate.ts
21
+ - .framework/cli/commands/install-app.ts
22
+ - .framework/src/lib/supabase.ts
23
+ - .framework/src/App.tsx
24
+ ---
25
+
26
+ # Environment Variables
27
+
28
+ Every variable read across `.framework/` — where it's read, whether it's
29
+ required, and whether it is browser-exposed. `VITE_*` vars are compiled into
30
+ the frontend bundle and are **public by definition**; all others are
31
+ server-only (Netlify functions, CLI, MCP tooling). `.env` at the project root
32
+ is preloaded by the CLI and dev tooling.
33
+
34
+ > **Security rule:** never set `VITE_SUPABASE_SERVICE_ROLE_KEY` or put any
35
+ > secret behind a `VITE_*` name. The service-role key bypasses RLS entirely;
36
+ > shipping it to the browser hands full database access to every visitor.
37
+ > Only `VITE_SUPABASE_ANON_KEY` belongs in the client bundle.
38
+
39
+ ## Required — server
40
+
41
+ | Variable | Read by | Purpose |
42
+ |---|---|---|
43
+ | `SUPABASE_URL` | `.framework/functions/_shared/db.ts`, `.framework/functions/_shared/principal.ts`, CLI (`context.ts`, `install-app.ts`, `migrate.ts`, `status.ts`, `system.ts`) | Supabase project URL for service + user clients |
44
+ | `SUPABASE_SERVICE_ROLE_KEY` | `.framework/functions/_shared/db.ts` (`adminDb`), `.framework/cli/context.ts`, `install-app.ts`, `status.ts` | Service-role client; bypasses RLS. **Server-only** |
45
+ | `SUPABASE_ANON_KEY` | `.framework/functions/_shared/principal.ts`, `.framework/functions/_shared/db.ts` (`getUserDb`) | JWT verification and user-scoped PostgREST clients inside functions |
46
+
47
+ Missing `SUPABASE_URL`/`SUPABASE_SERVICE_ROLE_KEY` fails CLI commands that
48
+ touch the DB and leaves `adminDb` on placeholder credentials — function
49
+ calls then fail at query time.
50
+
51
+ > **Key naming:** all three values are under Supabase Dashboard → Project
52
+ > Settings → API Keys (older dashboards: Settings → API / Data API).
53
+ > Current dashboards issue the anon key as a **Publishable key**
54
+ > (`sb_publishable_…`) and the service-role key as a **Secret key**
55
+ > (`sb_secret_…`) — set `*_ANON_KEY` / `SUPABASE_SERVICE_ROLE_KEY` to them
56
+ > directly; they work everywhere the legacy JWTs did.
57
+
58
+ ## Required for specific features — server
59
+
60
+ | Variable | Read by | Required for |
61
+ |---|---|---|
62
+ | `SUPABASE_DB_PASSWORD` | `.framework/cli/commands/migrate.ts`, `install-app.ts` | Direct Postgres connection for `migrate`/`install-app` (from Supabase Dashboard → Database) |
63
+ | `SUPABASE_DB_URL` | `.framework/cli/commands/migrate.ts` | Optional full connection string overriding host/user/port — use the **Session Pooler** (`postgres://postgres.<ref>:<pw>@aws-0-<region>.pooler.supabase.com:5432/postgres`) when `db.<ref>.supabase.co:5432` is unreachable (direct connections are IPv6-only on most Supabase projects) |
64
+ | `INTERNAL_INVOKE_SECRET` | `.framework/functions/_shared/principal.ts` (`isInternalInvokeAuthorized`), `.framework/functions/_shared/trigger-engine.ts`, `.framework/functions/pipeline-runner-background.ts` | Authenticates function-to-function invocation via `x-internal-secret`. **Required for trigger dispatch** — without it, `dispatchTriggerPipeline` cannot invoke `pipeline-runner-background` and `x-cron-id`/`x-trigger-id`/`x-internal-secret` headers are never honored. Generate with `openssl rand -hex 32` |
65
+ | `SCHEDULER_API_KEY` | `.framework/functions/system-cron.ts` | `?api_key=`/`?scheduler_key=` on the scheduler endpoint; unset disables key-gated scheduling |
66
+ | `APP_BASE_URL` | `.framework/functions/invites.ts` | Base URL for invite links, auth redirects, and the allowlist for client-supplied `base_url` params |
67
+ | `RESEND_API_KEY` | resolved via `env_var` credentials on the `transactional-email`/`resend` integration (`.framework/functions/_shared/resolve-credentials.ts`) | Outbound email (invites, notification stages). Unset → invites fall back to copyable links, email stages skip |
68
+ | `EMAIL_FROM` | `.framework/functions/adapters/resend-outbound.ts` | Sender identity for outbound email |
69
+ | `OPENAI_API_KEY` | resolved via `env_var` credentials on the `openai` integration; `.framework/functions/_shared/agent-runner.ts` mocks responses when absent | LLM chat/embeddings for agents, pipelines, KB |
70
+ | `LLM_DEFAULT_MODEL` | `.framework/functions/_shared/agent-runner.ts`, `.framework/functions/embeddings.ts` | Default model slug when prompts don't pin one |
71
+ | `NETLIFY_ACCESS_TOKEN`, `NETLIFY_SITE_ID` | `.framework/functions/security.ts` | Push banned IPs to Netlify edge Firewall rules; unset skips the edge layer |
72
+
73
+ Integration credentials generally use `source: 'env_var'` +
74
+ `value: '<VAR_NAME>'` in `integrations.config.credentials` —
75
+ `resolveCredentials` reads `process.env[value]` server-side
76
+ (`.framework/functions/_shared/resolve-credentials.ts`). Any env var name is
77
+ permitted there.
78
+
79
+ ## Optional — server behavior tuning
80
+
81
+ | Variable | Read by | Default |
82
+ |---|---|---|
83
+ | `SITE_URL` | `.framework/functions/_shared/middleware.ts` | `Access-Control-Allow-Origin` header; falls back to Netlify's `URL`, then `*` |
84
+ | `URL` | `.framework/functions/_shared/middleware.ts`, `.framework/mcp/` | Netlify auto-set site URL; CORS and MCP base-URL fallback |
85
+ | `ALLOWED_ORIGINS` | `.framework/functions/invites.ts` | extra CORS origins for invite endpoints |
86
+ | `SPINE_IP_RATE_LIMIT` | `.framework/functions/_shared/principal.ts` | per-IP request ceiling for API-key auth; default `300`/60s |
87
+ | `REPO_PATH` | `.framework/functions/_shared/app-manifest.ts`, `.framework/mcp/` | project root for manifest discovery; defaults to `process.cwd()` |
88
+ | `DB_SCHEMA` | `.framework/functions/_shared/db.ts` | PostgREST schema; default `public` (multi-instance installs only) |
89
+ | `EMAIL_WEBHOOK_URL` | outbound adapters | optional inbound-email relay endpoint |
90
+
91
+ ## Client-exposed (`VITE_*`, browser bundle)
92
+
93
+ | Variable | Read by | Purpose |
94
+ |---|---|---|
95
+ | `VITE_SUPABASE_URL` | `.framework/src/lib/supabase.ts` | Supabase URL for the browser client — required |
96
+ | `VITE_SUPABASE_ANON_KEY` | `.framework/src/lib/supabase.ts` | anon key for the browser client — required, RLS-scoped, safe |
97
+ | `VITE_ENVIRONMENT` | `.framework/src/contexts/AppContext.tsx`, `.framework/src/hooks/useApps.ts` | environment badge (`dev`/`staging`/`prod`) |
98
+ | `VITE_SETUP_MODE` | `.framework/src/App.tsx` | setup-mode UI flag |
99
+ | `VITE_TURNSTILE_SITE_KEY` | `.framework/src/components/auth/TurnstileWidget.tsx` | Cloudflare Turnstile site key on auth pages — public by design |
100
+ | `VITE_APP_NAME` | `.env.example` (branding) | app name for UI/localStorage/webhook headers — prefer `branding.app_name` in `spine.config.json` |
101
+ | `VITE_DB_SCHEMA` | `.env.example` | deprecated multi-instance knob; leave unset |
102
+
103
+ ## CLI / local tooling
104
+
105
+ | Variable | Read by | Purpose |
106
+ |---|---|---|
107
+ | `SPINE_CLI_API_KEY` | `.framework/cli/context.ts` | API-key auth for remote CLI commands (`items`, `pipelines`, `agents`, `system`) |
108
+ | `SPINE_CLI_JWT` | `.framework/cli/context.ts` | JWT auth alternative for the same commands |
109
+ | `SPINE_CLI_ACCOUNT_ID` | `.framework/cli/context.ts` | default account scope; `--account` overrides |
110
+ | `SPINE_CLI_DEBUG` | `dev.ts`, `init.ts`, `install-app.ts`, `migrate.ts`, `upgrade.ts` | verbose CLI diagnostics |
111
+ | `SPINE_URL`, `SPINE_API_KEY`, `SPINE_FRAMEWORK_API_KEY` | `.framework/mcp/` | MCP server endpoint + key |
112
+ | `OPENAI_BASE_URL`, `LLM_API_KEY`, `OPENAI_API_KEY` | `.framework/mcp/` | MCP-side LLM provider config |
113
+
114
+ ## Consumer-side conventions
115
+
116
+ Not read by framework runtime code — a convention for agent/tooling
117
+ integrations so the same credential location works everywhere:
118
+
119
+ | Variable | Read by | Purpose |
120
+ |---|---|---|
121
+ | `SPINE_API_KEY` | agent tooling (`.framework/mcp/` honors it too) | the `sk_…` key an agent sends as `x-api-key` on `/api/*` requests; the user creates a scoped key in Admin → Configs → API Keys |
122
+
123
+ ## Test-only
124
+
125
+ `SPINE_DEV_URL`, `SPINE_DEV_JWT`, `SPINE_TEST_API_KEY`, `SPINE_TEST_ACCOUNT_ID`,
126
+ `ACCOUNT_ID`, `SERVICE_KEY` — used by `.framework/tests/` fixtures and
127
+ integration suites; never required at runtime.
128
+
129
+ ## Related
130
+
131
+ - `.env.example` — annotated starter file
132
+ - [Authentication](api/auth.md) — how `x-api-key`/`x-internal-secret`/Bearer resolve
133
+ - [Shared Modules](shared-modules.md)
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: account-nodes
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/account-nodes.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/public/_redirects
11
+ ---
12
+
13
+ # account-nodes
14
+
15
+ ## What it does
16
+
17
+ Returns one account ("node") plus its position in the account hierarchy:
18
+ ancestors, direct children, and full descendants. Used to render account
19
+ trees and breadcrumb navigation.
20
+
21
+ ## Route and methods
22
+
23
+ `/.netlify/functions/account-nodes`, reachable as
24
+ `GET /api/account-nodes?node_id=<uuid>` (`/api/*` →
25
+ `/.netlify/functions/:splat`). The handler performs no method check — any
26
+ HTTP method with the query parameter works.
27
+
28
+ ## Authentication
29
+
30
+ Standard `createHandler`: `x-api-key`, internal secret, or
31
+ `Bearer <jwt>` required; anonymous is rejected with 401. Queries run on
32
+ `ctx.db`, so human callers see only what row-level security allows; machine
33
+ and system principals use the service role.
34
+
35
+ ## Request contract
36
+
37
+ One query parameter:
38
+
39
+ - `node_id` — required, the `accounts.id` to center the lookup on.
40
+
41
+ ## Response contract
42
+
43
+ `{ node, ancestors, children, descendants }` inside the standard
44
+ `{ data, error, meta }` envelope:
45
+
46
+ - `node` — the account row's embedded `type` (`{ type: { id, slug, name,
47
+ icon, color } }`); the account's own columns are not selected.
48
+ - `ancestors` — rows returned by the `get_account_ancestors` RPC, else `[]`.
49
+ - `children` — active `accounts` rows with `parent_id = node_id`, each
50
+ shaped `{ type: {...} }`, ordered by `display_name`.
51
+ - `descendants` — rows returned by the `get_account_descendants` RPC, else `[]`.
52
+
53
+ ## Side effects
54
+
55
+ None — read-only. A `request.get` span is emitted by the middleware like
56
+ every `createHandler` call.
57
+
58
+ ## Errors
59
+
60
+ | Condition | HTTP | Message |
61
+ |---|---|---|
62
+ | Missing `node_id` | 500 | `node_id is required` |
63
+ | No active account with that id | 500 | `Account node not found` |
64
+ | RPC/query failure | 500 | underlying PostgREST message |
65
+
66
+ ## Agent notes
67
+
68
+ - The `select` on `node` and `children` contains only the embedded `type`
69
+ relation — do not expect `display_name`, `slug`, or other account
70
+ columns on those payloads; fetch the account itself via
71
+ `/api/admin-data?entity=accounts&id=…` when you need them.
72
+ - `ancestors`/`descendants` come from database functions; their row shape
73
+ is whatever the RPC returns, not the `{ type }` shape used for `node`.
74
+ - Inactive accounts are filtered from `node` and `children`
75
+ (`is_active = true`) but RPC results are whatever the functions return.