spine-framework 0.4.4 → 0.5.0-rc.5

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 (305) 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/install-app.ts +183 -13
  8. package/.framework/cli/commands/migrate.ts +36 -30
  9. package/.framework/cli/commands/migrations.ts +16 -56
  10. package/.framework/cli/commands/upgrade.ts +35 -0
  11. package/.framework/cli/commands/validate-app.ts +127 -1
  12. package/.framework/cli/commands/validate-schema.ts +147 -0
  13. package/.framework/cli/context.ts +223 -110
  14. package/.framework/cli/index.ts +6 -0
  15. package/.framework/cli/lib/project-files.ts +241 -1
  16. package/.framework/functions/_shared/app-manifest.ts +2 -0
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/logging.ts +562 -224
  19. package/.framework/functions/_shared/middleware.ts +47 -44
  20. package/.framework/functions/_shared/permissions.ts +4 -0
  21. package/.framework/functions/admin-data.ts +51 -28
  22. package/.framework/functions/ai-agents.ts +10 -6
  23. package/.framework/functions/api-keys.ts +301 -122
  24. package/.framework/functions/apps.ts +63 -29
  25. package/.framework/functions/cil-ingest.ts +48 -15
  26. package/.framework/functions/debug-auth.ts +14 -8
  27. package/.framework/functions/deliveries.ts +39 -12
  28. package/.framework/functions/embeddings.ts +149 -78
  29. package/.framework/functions/integration-outbound.ts +67 -27
  30. package/.framework/functions/integrations.ts +254 -127
  31. package/.framework/functions/logs.ts +273 -133
  32. package/.framework/functions/observability.ts +278 -148
  33. package/.framework/functions/pipeline-executions.ts +1 -1
  34. package/.framework/functions/pipelines.ts +14 -9
  35. package/.framework/functions/prompt-configs.ts +7 -4
  36. package/.framework/functions/roles.ts +8 -5
  37. package/.framework/functions/security.ts +30 -17
  38. package/.framework/functions/system-cron.ts +240 -127
  39. package/.framework/functions/tests.ts +7 -6
  40. package/.framework/functions/timers.ts +310 -107
  41. package/.framework/functions/triggers.ts +28 -13
  42. package/.framework/functions/types.ts +13 -10
  43. package/.framework/migrations/069_log_stats_window.sql +54 -0
  44. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  45. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  46. package/.framework/migrations/072_slug_autofill.sql +77 -0
  47. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  48. package/.framework/migrations/074_app_route_prefix_domains.sql +168 -0
  49. package/.framework/migrations/075_security_definer_grants.sql +157 -0
  50. package/.framework/src/apps/admin/index.tsx +1141 -775
  51. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  52. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  53. package/.framework/src/components/admin/AdminSidebar.tsx +88 -79
  54. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  55. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  56. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  57. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  58. package/.framework/src/components/layout/Sidebar.tsx +300 -177
  59. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  60. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  61. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  62. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  63. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  64. package/.framework/src/components/ui/sheet.tsx +24 -26
  65. package/.framework/src/components/ui/textarea.tsx +74 -48
  66. package/.framework/src/contexts/AppContext.tsx +5 -12
  67. package/.framework/src/contexts/AuthContext.tsx +17 -9
  68. package/.framework/src/hooks/useApps.ts +18 -22
  69. package/.framework/src/hooks/useEntityList.ts +12 -9
  70. package/.framework/src/hooks/useListSchema.ts +108 -58
  71. package/.framework/src/instructions/README.md +74 -0
  72. package/.framework/src/instructions/accounts.md +97 -0
  73. package/.framework/src/instructions/ai-agents.md +69 -0
  74. package/.framework/src/instructions/alerts.md +80 -0
  75. package/.framework/src/instructions/api-keys.md +87 -0
  76. package/.framework/src/instructions/apps.md +77 -0
  77. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  78. package/.framework/src/instructions/concepts/apps-and-renderers.md +228 -0
  79. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  80. package/.framework/src/instructions/concepts/automation.md +186 -0
  81. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  82. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  83. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  84. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  85. package/.framework/src/instructions/concepts/observability.md +116 -0
  86. package/.framework/src/instructions/concepts/permissions.md +150 -0
  87. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  88. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  89. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  90. package/.framework/src/instructions/deliveries.md +59 -0
  91. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  92. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  93. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  94. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  95. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  96. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  97. package/.framework/src/instructions/integrations.md +96 -0
  98. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  99. package/.framework/src/instructions/internal/docs-contract.md +158 -0
  100. package/.framework/src/instructions/invites.md +60 -0
  101. package/.framework/src/instructions/kb.md +80 -0
  102. package/.framework/src/instructions/logs.md +77 -0
  103. package/.framework/src/instructions/notifications.md +77 -0
  104. package/.framework/src/instructions/observability.md +61 -0
  105. package/.framework/src/instructions/operations/deployment.md +115 -0
  106. package/.framework/src/instructions/operations/migrations.md +125 -0
  107. package/.framework/src/instructions/operations/security-checklist.md +170 -0
  108. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  109. package/.framework/src/instructions/operations/upgrade.md +116 -0
  110. package/.framework/src/instructions/people.md +91 -0
  111. package/.framework/src/instructions/pipeline-executions.md +62 -0
  112. package/.framework/src/instructions/pipelines.md +78 -0
  113. package/.framework/src/instructions/prompts.md +95 -0
  114. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  115. package/.framework/src/instructions/reference/api/auth.md +113 -0
  116. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  117. package/.framework/src/instructions/reference/app-manifest.md +184 -0
  118. package/.framework/src/instructions/reference/cli.md +124 -0
  119. package/.framework/src/instructions/reference/database.md +132 -0
  120. package/.framework/src/instructions/reference/design-schema.md +280 -0
  121. package/.framework/src/instructions/reference/env-vars.md +133 -0
  122. package/.framework/src/instructions/reference/frontend-sdk.md +207 -0
  123. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  124. package/.framework/src/instructions/reference/functions/admin-data.md +197 -0
  125. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  126. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  127. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  128. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  129. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  130. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  131. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  132. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  133. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  134. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  135. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  136. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  137. package/.framework/src/instructions/reference/functions/index.md +103 -0
  138. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  139. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  140. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  141. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  142. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  143. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  144. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  145. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  146. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  147. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  148. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  149. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  150. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  151. package/.framework/src/instructions/reference/functions/security.md +140 -0
  152. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  153. package/.framework/src/instructions/reference/functions/system.md +106 -0
  154. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  155. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  156. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  157. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  158. package/.framework/src/instructions/reference/functions/types.md +146 -0
  159. package/.framework/src/instructions/reference/seeds.md +126 -0
  160. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  161. package/.framework/src/instructions/reference/spine-config.md +114 -0
  162. package/.framework/src/instructions/roles.md +80 -0
  163. package/.framework/src/instructions/runtime-collaboration.md +106 -0
  164. package/.framework/src/instructions/runtime-detail.md +70 -0
  165. package/.framework/src/instructions/runtime-list.md +77 -0
  166. package/.framework/src/instructions/security.md +67 -0
  167. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  168. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  169. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  170. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  171. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  172. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  173. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  174. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  175. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  176. package/.framework/src/instructions/tasks/document-your-app.md +117 -0
  177. package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
  178. package/.framework/src/instructions/tasks/register-an-app.md +137 -0
  179. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  180. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  181. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  182. package/.framework/src/instructions/testing.md +59 -0
  183. package/.framework/src/instructions/timers.md +84 -0
  184. package/.framework/src/instructions/triggers.md +69 -0
  185. package/.framework/src/instructions/types.md +93 -0
  186. package/.framework/src/lib/app-mount.ts +88 -0
  187. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  188. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  189. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  190. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  191. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  192. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  193. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  194. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  195. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  196. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  197. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  198. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  199. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  200. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  201. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  202. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  203. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  204. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  205. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  206. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  207. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  208. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  209. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  210. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  211. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  212. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  213. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  214. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  215. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  216. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  217. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  218. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  219. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  220. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  221. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  222. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  223. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  224. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  225. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  226. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  227. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  228. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  229. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  230. package/.framework/src/pages/auth/LoginPage.tsx +3 -2
  231. package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
  232. package/.framework/templates/agent/AGENTS.md +167 -0
  233. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  234. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  235. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  236. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  237. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +79 -0
  238. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  239. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
  240. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
  241. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -0
  242. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  243. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  244. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  245. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  246. package/.framework/templates/project.json +4 -1
  247. package/.framework/tests/unit/account-scoping-gates.test.ts +394 -0
  248. package/.framework/tests/unit/agent-files.test.ts +355 -0
  249. package/.framework/tests/unit/agent-runner.test.ts +479 -44
  250. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  251. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  252. package/.framework/tests/unit/app-mount.test.ts +80 -0
  253. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  254. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  255. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  256. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  257. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  258. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  259. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  260. package/.framework/tests/unit/security-definer-grants.test.ts +154 -0
  261. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  262. package/.framework/tests/unit/system-cron.test.ts +240 -0
  263. package/.framework/tests/unit/timers.test.ts +159 -0
  264. package/README.md +125 -166
  265. package/config/tsconfig.cli.json +2 -2
  266. package/package.json +15 -4
  267. package/templates/project/package-lock.json +2 -2
  268. package/.framework/docs/admin-routes.md +0 -76
  269. package/.framework/docs/api-docs-progress.md +0 -38
  270. package/.framework/docs/api-governance.md +0 -146
  271. package/.framework/docs/api-testing-results.md +0 -212
  272. package/.framework/docs/assembly-launch-guide.md +0 -138
  273. package/.framework/docs/audit-results.md +0 -590
  274. package/.framework/docs/db-api-inventory.md +0 -95
  275. package/.framework/docs/gap-remediation-backlog.md +0 -103
  276. package/.framework/docs/ui-adoption-verification.md +0 -111
  277. package/.framework/docs/ui-api-coverage.md +0 -84
  278. package/.framework/docs/v2-compatibility-audit.md +0 -228
  279. package/.framework/mcp/index.ts +0 -133
  280. package/.framework/mcp/load-env.ts +0 -60
  281. package/.framework/mcp/shared/app-manifest.ts +0 -275
  282. package/.framework/mcp/shared/db.ts +0 -477
  283. package/.framework/mcp/shared/kb.ts +0 -118
  284. package/.framework/mcp/tools/architect.ts +0 -431
  285. package/.framework/mcp/tools/cil/context.ts +0 -345
  286. package/.framework/mcp/tools/cil/graph.ts +0 -242
  287. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  288. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  289. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  290. package/.framework/mcp/tools/cil/project.ts +0 -791
  291. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  292. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  293. package/.framework/mcp/tools/cil/search.ts +0 -97
  294. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  295. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  296. package/.framework/mcp/tools/kb/delete.ts +0 -139
  297. package/.framework/mcp/tools/kb/index.ts +0 -52
  298. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  299. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  300. package/.framework/mcp/tools/kb/publish.ts +0 -100
  301. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  302. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  303. package/.framework/mcp/tools/kb/search.ts +0 -101
  304. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  305. 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,207 @@
1
+ ---
2
+ title: Frontend SDK Reference (@core)
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: bc17374
6
+ verified_on: 2026-11-02
7
+ source_files:
8
+ - config/vite.config.ts
9
+ - config/tsconfig.json
10
+ - .framework/src/lib/api.ts
11
+ - .framework/src/lib/supabase.ts
12
+ - .framework/src/lib/utils.ts
13
+ - .framework/src/lib/registration.ts
14
+ - .framework/src/lib/branding.ts
15
+ - .framework/src/hooks/useAppPath.ts
16
+ - .framework/src/hooks/useApi.ts
17
+ - .framework/src/hooks/useApps.ts
18
+ - .framework/src/contexts/AuthContext.tsx
19
+ - .framework/src/contexts/AppContext.tsx
20
+ - .framework/src/components/ui/
21
+ - .framework/src/components/shared/
22
+ - .framework/src/components/kb/
23
+ - .framework/src/components/layout/
24
+ - .framework/src/types/types.ts
25
+ - .framework/src/types/auth.ts
26
+ ---
27
+
28
+ # Frontend SDK Reference (`@core`)
29
+
30
+ Custom apps write frontend code against the framework's `@core` import
31
+ surface — the same tree as `.framework/src`, resolved through the bundled
32
+ `.assembled/src`. This page is the contract: what each module exports, what
33
+ it is for, and when to reach for it. Ordered by how often real apps use it.
34
+
35
+ ## Import aliases
36
+
37
+ Defined in `config/vite.config.ts` (build/dev) and `config/tsconfig.json`
38
+ (types):
39
+
40
+ | Alias | Resolves to | Use |
41
+ |---|---|---|
42
+ | `@core/*` | `.assembled/src/*` (types: `.framework/src/*`) | **Frontend core.** All imports below. |
43
+ | `@/*` | `.assembled/src/*` | Same tree as `@core` — prefer `@core` in custom apps for clarity. |
44
+ | `@custom/*` | `custom/apps/*` | Cross-app imports; rare. |
45
+ | `@shared/*` | `.framework/functions/_shared/*` | **Server-side only** (Netlify/Deno functions). Never import from a React page — it pulls server-only deps into the frontend bundle. |
46
+
47
+ The loader mounts custom apps via `import.meta.glob('../../../custom/apps/*/index.tsx')`
48
+ — only `index.tsx` is the entry point; everything else is imported from it.
49
+
50
+ ## 1. `@core/lib/api` — HTTP client (use this for all API calls)
51
+
52
+ ```ts
53
+ import { apiFetch } from '@core/lib/api'
54
+ ```
55
+
56
+ | Export | Contract |
57
+ |---|---|
58
+ | `apiFetch(path, options?) → Promise<Response>` | Authenticated `fetch`. Auto-attaches `Authorization: Bearer <supabase session>` and `X-Account-Id` when set. Adds a `traceparent` header for request tracing. Derives an `x-app-id` hint from the URL's first segment (server validates it against installed apps). Returns the raw `Response` — check `.ok`, then `.json()`. Strips bogus `Bearer null/undefined` headers you pass. On `401` (except `/api/auth` and `/api/admin-data` paths) it fires the registered unauthorized callback. |
59
+ | `setAccountId(id \| null)` / `getAccountId()` | Set/read the account ID sent as `X-Account-Id` on subsequent calls. AuthContext sets this on login; apps rarely touch it. |
60
+ | `getAuthHeaders() → Promise<Record<string,string>>` | The Bearer + `X-Account-Id` header pair, if you need raw headers (e.g. non-fetch transport). |
61
+ | `onUnauthorized(cb) → unsubscribe` | Register a global 401 handler (framework registers the logout redirect). One callback at a time. |
62
+
63
+ Debug: `localStorage.spine_debug_api = '1'` logs `X-Request-ID` + traceparent per call.
64
+
65
+ **Do not** call `fetch()` directly against `/api/*` — you'll lose auth,
66
+ account scoping, and trace headers. For runtime record CRUD, `apiFetch` +
67
+ `/api/admin-data` is the only path (see [admin-data](api/admin-data.md)).
68
+
69
+ ## 2. `@core/hooks/useAppPath` — mount-agnostic navigation (required)
70
+
71
+ ```ts
72
+ import { useAppPath } from '@core/hooks/useAppPath'
73
+ const appPath = useAppPath()
74
+ appPath('/tickets') // → '/my-app/tickets' (or '/tickets' when route_prefix === '/')
75
+ appPath('/') // → '/my-app' (or '/')
76
+ ```
77
+
78
+ Returns a function that prepends the current app's `route_prefix` (trailing
79
+ slash stripped; `'/'` treated as empty). **All app-internal links and
80
+ `navigate()` calls must go through it** — hardcoding `/dashboard` escapes the
81
+ app when it is directory-mounted, and breaks differently when it is
82
+ root-mounted. Alternatively use relative `path`/`to` inside the app's own
83
+ `<Routes>`.
84
+
85
+ ## 3. `@core/components/ui/*` — the component kit
86
+
87
+ A shadcn-style kit (~60 components) plus framework-level widgets. Import
88
+ per component: `import { Button } from '@core/components/ui/button'`.
89
+
90
+ **Framework widgets** (higher-level, Spine-aware):
91
+
92
+ | Component | Purpose |
93
+ |---|---|
94
+ | `DataTable` | Sortable/paginated table |
95
+ | `ItemListView`, `ItemCard` | Record list/card rendering |
96
+ | `Form`, `field`, `input-group` | Form scaffolding (react-hook-form aligned) |
97
+ | `Modal`, `dialog`, `alert-dialog`, `drawer`, `sheet` | Overlays |
98
+ | `RichTextEditor` | Rich text input |
99
+ | `LoadingSpinner`, `spinner`, `skeleton` | Loading states |
100
+ | `chart` | Chart wrappers (recharts) |
101
+ | `sonner` | Toast notifications |
102
+
103
+ **Plain kit** (pick what you need): `accordion`, `alert`, `aspect-ratio`,
104
+ `avatar`, `badge`, `breadcrumb`, `button`, `button-group`, `calendar`,
105
+ `card`, `carousel`, `checkbox`, `collapsible`, `combobox`, `command`,
106
+ `context-menu`, `direction`, `dropdown-menu`, `empty`, `hover-card`,
107
+ `input`, `input-otp`, `item`, `kbd`, `label`, `menubar`, `native-select`,
108
+ `navigation-menu`, `pagination`, `popover`, `progress`, `radio-group`,
109
+ `resizable`, `scroll-area`, `select`, `separator`, `sidebar`, `slider`,
110
+ `switch`, `table`, `table-primitives`, `tabs`, `textarea`, `toggle`,
111
+ `toggle-group`, `tooltip`.
112
+
113
+ ## 4. `@core/contexts/AuthContext` — who is signed in
114
+
115
+ ```ts
116
+ import { useAuth } from '@core/contexts/AuthContext'
117
+ const { user, isLoading } = useAuth()
118
+ ```
119
+
120
+ `user` is `null` or a `User` (`@core/types/auth`):
121
+
122
+ | Field | Notes |
123
+ |---|---|
124
+ | `id`, `email`, `full_name` | Person record identity |
125
+ | `account_id` | Currently active account (drives `X-Account-Id`) |
126
+ | `account?` | The Account row |
127
+ | `roles: string[]` | Role slugs — check `includes('system_admin')` etc. |
128
+ | `permissions: string[]` | Resolved permission strings |
129
+ | `is_system_admin?: boolean` | Shortcut for the system-admin role |
130
+ | `status?: string` | `'pending'` users are gated to the pending-approval page |
131
+ | `accessible_accounts?: Account[]` | Accounts the user may switch between |
132
+
133
+ ## 5. `@core/lib/utils` — `cn` + formatters
134
+
135
+ ```ts
136
+ import { cn, formatDateTime, formatCurrency } from '@core/lib/utils'
137
+ ```
138
+
139
+ `cn(...)` = `twMerge(clsx(...))` for conditional Tailwind classes.
140
+ Formatters: `formatDate`, `formatTime`, `formatDateTime`,
141
+ `formatRelativeTime`, `formatFileSize`, `formatCurrency`, `formatNumber`,
142
+ `truncateText`. Use them instead of hand-rolling `toLocaleString` calls so
143
+ dates/money render consistently with the rest of the shell.
144
+
145
+ ## 6. `@core/components/shared` — instant account/entity UI
146
+
147
+ | Component | Purpose |
148
+ |---|---|
149
+ | `MemberList` | Account members with roles |
150
+ | `InviteManager` | Invite creation/management UI |
151
+ | `AgentView` | AI-agent interaction surface |
152
+ | `FieldRenderer` | Render a `design_schema` field for display/input |
153
+ | `SchemaFields` | Render a whole `design_schema.fields` section |
154
+
155
+ `FieldRenderer`/`SchemaFields` are how generic pages render schema-driven
156
+ forms — reuse them in custom apps instead of reimplementing field widgets.
157
+
158
+ ## 7. `@core/hooks/useApi` — request state wrappers
159
+
160
+ Thin React state over `apiFetch`-style calls. All three return
161
+ `{ data, loading, error, ... }` shapes with cancellation and callbacks.
162
+
163
+ | Hook | Signature | Use |
164
+ |---|---|---|
165
+ | `useApi<T>` | `(apiFn: (params?) => Promise<T>, { immediate?, onSuccess?, onError?, initialData? })` → `{ data, loading, error, execute, reset, refetch }` | One-shot fetch with cancellation |
166
+ | `usePaginatedApi<T>` | `(apiFn: ({page, itemsPerPage, ...}) => Promise<{data, pagination}>, { itemsPerPage? })` → adds `pagination`, `nextPage`/`prevPage` | Server-paginated lists |
167
+ | `useMutation<T, P>` | `(mutateFn: (params: P) => Promise<T>, { onSuccess?, onError?, onSettled? })` → `{ data, loading, error, mutate, reset }` | Create/update/delete calls |
168
+
169
+ ## 8. `@core/components/kb` — knowledge-base widgets
170
+
171
+ `KBArticleList`, `KBArticleEditor` — list and editing UI for `kb` records.
172
+ Use when an app surfaces framework knowledge rather than its own KB types.
173
+
174
+ ## 9. Layout, app context, client, types
175
+
176
+ | Import | Exports | Use |
177
+ |---|---|---|
178
+ | `@core/components/layout` | `AppShell`, `Header`, `Sidebar`, `Layout` | Wrap custom pages in the app chrome |
179
+ | `@core/contexts/AppContext` | `useCurrentApp() → AppRecord`, `useAppsRegistry() → { apps, routableApps, loading, error, refetch }`, `AppProvider`, `AppsRegistryProvider` | Current app's manifest row (slug, route_prefix, config, nav_items); the whole registry |
180
+ | `@core/lib/supabase` | `supabase` (browser client), `getCurrentSession`, `getCurrentUser`, `refreshSession` | Direct Supabase access — prefer `apiFetch`/endpoints for record data |
181
+ | `@core/types` | `DesignSchema`, `FieldDefinition`, `View`/`ListView`/`DetailView`, `ItemType`, `Item`, `PaginationParams`, `SortParams`, `FilterParams`, `FormState` | Shared type defs for schema-driven work |
182
+
183
+ ## Other exports — available, unproven
184
+
185
+ `useEntityList`, `useEntityRecord`, `useSchemaRecord`, `useListSchema`,
186
+ `useForm`, `useBranding`, `useApps` exist in `hooks/`/`lib/` but shipped
187
+ apps do not use them today (they predate or duplicate the surfaces above).
188
+ Treat as internal scaffolding; prefer the documented tiers.
189
+
190
+ ## Boundaries
191
+
192
+ - **Never import `@core/pages/*` or `@core/apps/*`** from a custom app —
193
+ admin/internal pages are not SDK surface (one app importing
194
+ `pages/admin/LogsPage` is a known violation, not a pattern).
195
+ - **Never import `@shared/*` in frontend code** — server-only modules.
196
+ - Framework code never imports from `custom/`; do not rely on your app
197
+ being reachable from core.
198
+ - For runtime record CRUD, always `/api/admin-data` via `apiFetch` — never
199
+ bespoke server endpoints (see [admin-data](api/admin-data.md)).
200
+
201
+ ## Related
202
+
203
+ - [Build a custom page](../tasks/build-a-custom-page.md) — the task doc
204
+ that uses this surface end to end
205
+ - [Apps and renderers](../concepts/apps-and-renderers.md) — mounting model
206
+ and `useAppPath`'s role in it
207
+ - [admin-data API](api/admin-data.md) · [errors and envelopes](api/errors-and-envelopes.md)