spine-framework 0.4.3 → 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 (320) 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/agent-runner.ts +1000 -81
  15. package/.framework/functions/_shared/cil.ts +2 -1
  16. package/.framework/functions/_shared/db.ts +3 -6
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/kb.ts +61 -7
  19. package/.framework/functions/_shared/logging.ts +562 -224
  20. package/.framework/functions/_shared/message-sequence.ts +53 -0
  21. package/.framework/functions/_shared/middleware.ts +47 -44
  22. package/.framework/functions/_shared/pipeline-runner.ts +150 -37
  23. package/.framework/functions/_shared/principal.ts +9 -6
  24. package/.framework/functions/_shared/trigger-engine.ts +149 -40
  25. package/.framework/functions/admin-data.ts +48 -31
  26. package/.framework/functions/ai-agents.ts +103 -33
  27. package/.framework/functions/api-keys.ts +301 -122
  28. package/.framework/functions/apps.ts +43 -26
  29. package/.framework/functions/cil-ingest.ts +48 -15
  30. package/.framework/functions/debug-auth.ts +14 -8
  31. package/.framework/functions/deliveries.ts +39 -12
  32. package/.framework/functions/embeddings.ts +145 -76
  33. package/.framework/functions/integration-outbound.ts +67 -27
  34. package/.framework/functions/integrations.ts +254 -127
  35. package/.framework/functions/logs.ts +273 -133
  36. package/.framework/functions/observability.ts +277 -147
  37. package/.framework/functions/pipeline-executions.ts +1 -1
  38. package/.framework/functions/pipeline-runner-background.ts +120 -0
  39. package/.framework/functions/pipelines.ts +14 -9
  40. package/.framework/functions/prompt-configs.ts +7 -4
  41. package/.framework/functions/roles.ts +8 -5
  42. package/.framework/functions/security.ts +30 -17
  43. package/.framework/functions/system-cron.ts +250 -128
  44. package/.framework/functions/tests.ts +7 -6
  45. package/.framework/functions/timers.ts +310 -107
  46. package/.framework/functions/trigger-scheduler.ts +65 -5
  47. package/.framework/functions/triggers.ts +28 -13
  48. package/.framework/functions/types.ts +13 -10
  49. package/.framework/migrations/064_episode_leases.sql +196 -0
  50. package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
  51. package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
  52. package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
  53. package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
  54. package/.framework/migrations/069_log_stats_window.sql +54 -0
  55. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  56. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  57. package/.framework/migrations/072_slug_autofill.sql +77 -0
  58. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  59. package/.framework/schemas/spine.config.schema.json +62 -0
  60. package/.framework/src/App.tsx +3 -1
  61. package/.framework/src/apps/admin/index.tsx +1141 -775
  62. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  63. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  64. package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
  65. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  66. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  67. package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
  68. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  69. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  70. package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
  71. package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
  72. package/.framework/src/components/layout/Layout.tsx +4 -2
  73. package/.framework/src/components/layout/Sidebar.tsx +304 -179
  74. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  75. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  76. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  77. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  78. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  79. package/.framework/src/components/ui/sheet.tsx +24 -26
  80. package/.framework/src/components/ui/textarea.tsx +74 -48
  81. package/.framework/src/hooks/useBranding.ts +47 -0
  82. package/.framework/src/hooks/useEntityList.ts +12 -9
  83. package/.framework/src/hooks/useListSchema.ts +108 -58
  84. package/.framework/src/instructions/README.md +57 -0
  85. package/.framework/src/instructions/ai-agents.md +69 -0
  86. package/.framework/src/instructions/alerts.md +80 -0
  87. package/.framework/src/instructions/api-keys.md +87 -0
  88. package/.framework/src/instructions/apps.md +77 -0
  89. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  90. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  91. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  92. package/.framework/src/instructions/concepts/automation.md +186 -0
  93. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  94. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  95. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  96. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  97. package/.framework/src/instructions/concepts/observability.md +116 -0
  98. package/.framework/src/instructions/concepts/permissions.md +150 -0
  99. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  100. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  101. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  102. package/.framework/src/instructions/deliveries.md +59 -0
  103. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  104. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  105. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  106. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  107. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  108. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  109. package/.framework/src/instructions/integrations.md +96 -0
  110. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  111. package/.framework/src/instructions/invites.md +60 -0
  112. package/.framework/src/instructions/kb.md +80 -0
  113. package/.framework/src/instructions/logs.md +77 -0
  114. package/.framework/src/instructions/observability.md +61 -0
  115. package/.framework/src/instructions/operations/deployment.md +115 -0
  116. package/.framework/src/instructions/operations/migrations.md +125 -0
  117. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  118. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  119. package/.framework/src/instructions/operations/upgrade.md +116 -0
  120. package/.framework/src/instructions/pipeline-executions.md +62 -0
  121. package/.framework/src/instructions/pipelines.md +78 -0
  122. package/.framework/src/instructions/prompts.md +95 -0
  123. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  124. package/.framework/src/instructions/reference/api/auth.md +113 -0
  125. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  126. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  127. package/.framework/src/instructions/reference/cli.md +124 -0
  128. package/.framework/src/instructions/reference/database.md +132 -0
  129. package/.framework/src/instructions/reference/design-schema.md +280 -0
  130. package/.framework/src/instructions/reference/env-vars.md +133 -0
  131. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  132. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  133. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  134. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  135. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  136. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  137. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  138. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  139. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  140. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  141. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  142. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  143. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  144. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  145. package/.framework/src/instructions/reference/functions/index.md +103 -0
  146. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  147. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  148. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  149. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  150. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  151. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  152. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  153. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  154. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  155. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  156. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  157. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  158. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  159. package/.framework/src/instructions/reference/functions/security.md +140 -0
  160. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  161. package/.framework/src/instructions/reference/functions/system.md +106 -0
  162. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  163. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  164. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  165. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  166. package/.framework/src/instructions/reference/functions/types.md +146 -0
  167. package/.framework/src/instructions/reference/seeds.md +126 -0
  168. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  169. package/.framework/src/instructions/reference/spine-config.md +114 -0
  170. package/.framework/src/instructions/roles.md +80 -0
  171. package/.framework/src/instructions/runtime-detail.md +70 -0
  172. package/.framework/src/instructions/runtime-list.md +77 -0
  173. package/.framework/src/instructions/security.md +67 -0
  174. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  175. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  176. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  177. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  178. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  179. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  180. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  181. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  182. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  183. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  184. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  185. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  186. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  187. package/.framework/src/instructions/testing.md +59 -0
  188. package/.framework/src/instructions/timers.md +82 -0
  189. package/.framework/src/instructions/triggers.md +69 -0
  190. package/.framework/src/instructions/types.md +93 -0
  191. package/.framework/src/lib/branding.ts +128 -0
  192. package/.framework/src/pages/NoAppPage.tsx +3 -1
  193. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  194. package/.framework/src/pages/SetupPage.tsx +5 -2
  195. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  196. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  197. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  198. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  199. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  200. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  201. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  202. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  203. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  204. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  205. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  206. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  207. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  208. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  209. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  210. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  211. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  212. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  213. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  214. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  215. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  216. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  217. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  218. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  219. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  220. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  221. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  222. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  223. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  224. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  225. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  226. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  227. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  228. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  229. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  230. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  231. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  232. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  233. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  234. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  235. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  236. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  237. package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
  238. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
  239. package/.framework/src/pages/auth/LoginPage.tsx +4 -2
  240. package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
  241. package/.framework/templates/agent/AGENTS.md +150 -0
  242. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  243. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  244. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  245. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  246. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  247. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  248. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  249. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  250. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  251. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  252. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  253. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  254. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  255. package/.framework/templates/project.json +5 -1
  256. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  257. package/.framework/tests/unit/agent-files.test.ts +355 -0
  258. package/.framework/tests/unit/agent-runner.test.ts +496 -47
  259. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  260. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  261. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  262. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  263. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  264. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  265. package/.framework/tests/unit/episode-lease.test.ts +695 -0
  266. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  267. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  268. package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
  269. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  270. package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
  271. package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
  272. package/.framework/tests/unit/principal-cache.test.ts +15 -0
  273. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  274. package/.framework/tests/unit/system-cron.test.ts +240 -0
  275. package/.framework/tests/unit/timers.test.ts +159 -0
  276. package/.framework/tests/unit/trigger-engine.test.ts +278 -91
  277. package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
  278. package/README.md +125 -166
  279. package/config/tsconfig.cli.json +2 -2
  280. package/netlify.toml +5 -0
  281. package/package.json +15 -4
  282. package/templates/project/package-lock.json +2 -2
  283. package/.framework/docs/admin-routes.md +0 -76
  284. package/.framework/docs/api-docs-progress.md +0 -38
  285. package/.framework/docs/api-governance.md +0 -146
  286. package/.framework/docs/api-testing-results.md +0 -212
  287. package/.framework/docs/assembly-launch-guide.md +0 -138
  288. package/.framework/docs/audit-results.md +0 -590
  289. package/.framework/docs/db-api-inventory.md +0 -95
  290. package/.framework/docs/gap-remediation-backlog.md +0 -103
  291. package/.framework/docs/ui-adoption-verification.md +0 -111
  292. package/.framework/docs/ui-api-coverage.md +0 -84
  293. package/.framework/docs/v2-compatibility-audit.md +0 -228
  294. package/.framework/mcp/index.ts +0 -133
  295. package/.framework/mcp/load-env.ts +0 -60
  296. package/.framework/mcp/shared/app-manifest.ts +0 -275
  297. package/.framework/mcp/shared/db.ts +0 -477
  298. package/.framework/mcp/shared/kb.ts +0 -118
  299. package/.framework/mcp/tools/architect.ts +0 -431
  300. package/.framework/mcp/tools/cil/context.ts +0 -345
  301. package/.framework/mcp/tools/cil/graph.ts +0 -242
  302. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  303. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  304. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  305. package/.framework/mcp/tools/cil/project.ts +0 -791
  306. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  307. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  308. package/.framework/mcp/tools/cil/search.ts +0 -97
  309. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  310. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  311. package/.framework/mcp/tools/kb/delete.ts +0 -139
  312. package/.framework/mcp/tools/kb/index.ts +0 -52
  313. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  314. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  315. package/.framework/mcp/tools/kb/publish.ts +0 -100
  316. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  317. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  318. package/.framework/mcp/tools/kb/search.ts +0 -101
  319. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  320. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: ai-interactions
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/ai-interactions.ts
9
+ - .framework/functions/ai-agents.ts
10
+ - .framework/functions/_shared/agent-runner.ts
11
+ - .framework/functions/_shared/pipeline-runner.ts
12
+ - .framework/functions/_shared/middleware.ts
13
+ - .framework/functions/_shared/funnel.ts
14
+ - .framework/functions/_shared/resolve-ids.ts
15
+ - public/_redirects
16
+ ---
17
+
18
+ # ai-interactions
19
+
20
+ ## What it does
21
+
22
+ `ai-interactions` (`.framework/functions/ai-interactions.ts`) writes the
23
+ feedback and observability side of the AI loop as `items` rows. It is *not*
24
+ the conversation engine:
25
+
26
+ - Interactive turns run through `POST /api/ai-agents?action=run` — `runAgent`
27
+ in `.framework/functions/_shared/agent-runner.ts` drives a `threads` row,
28
+ persists `messages`, and manages the episode lease.
29
+ - Stateless pipeline inference runs through the `agent_inference` stage
30
+ (`.framework/functions/_shared/pipeline-runner.ts`) — it POSTs `{ context,
31
+ model, temperature, max_tokens }` to `config.webhook_url` via
32
+ `integration-outbound` (`generic-http`), and returns a marked mock response
33
+ when `webhook_url` is unset.
34
+ - `ai-interactions` records what happened around those paths: customer
35
+ thumbs feedback, per-interaction telemetry rows, and draft learning
36
+ proposals.
37
+
38
+ Actions (dispatched on `?action=`):
39
+
40
+ | Action | Method | Purpose |
41
+ |---|---|---|
42
+ | `submit_feedback` | POST | Record `helpful`/`not_helpful` on an AI message; escalate or resolve the ticket. |
43
+ | `create_interaction` | POST | Insert a telemetry item (default type `ai_feedback_record`, overridable via `type_slug`). The pipeline `create_interaction` stage lazy-imports this handler. |
44
+ | `update_interaction` | PATCH or POST | Merge allow-listed fields into an existing interaction item. |
45
+ | `create_cil_proposal` | POST | Insert a `status: 'draft'` learning-proposal item (fixed `cil_*` type slug) from ticket context and source references. |
46
+ | `list` | GET | List interaction items for the caller's account (default action). |
47
+
48
+ ## Route and methods
49
+
50
+ - `/api/ai-interactions?action=<action>` — `public/_redirects` maps `/api/*`
51
+ to `/.netlify/functions/:splat`; the export `handler` is a
52
+ `createHandler`-wrapped dispatcher reading `ctx.query.action` and
53
+ `ctx.query.method` (populated from the HTTP method).
54
+ - `GET` with no `action` (or an unrecognized one) falls through to `list`;
55
+ other invalid action/method pairs throw `Invalid action or method`.
56
+
57
+ ## Authentication
58
+
59
+ `createHandler` resolves a principal and rejects anonymous requests with 401.
60
+ There is no role gate — any authenticated human or machine principal may call
61
+ the actions. `list` reads through `ctx.db` scoped to `ctx.accountId`;
62
+ `create_interaction`/`create_cil_proposal`/`update_interaction` write through
63
+ admin-data `create`/`update`, so the caller's permissions, audit, and
64
+ triggers apply. `submit_feedback` writes `messages`/`items` via `adminDb`
65
+ directly (service role) — it does not check that the caller owns the ticket.
66
+
67
+ ## Request contract
68
+
69
+ `submit_feedback` (POST):
70
+
71
+ | Field | Required | Notes |
72
+ |---|---|---|
73
+ | `message_id` | yes | `messages.id` of the AI reply. |
74
+ | `ticket_id` | yes | `items.id` of the ticket under discussion. |
75
+ | `feedback` | yes | `'helpful'` or `'not_helpful'`. |
76
+ | `reason` | no | Free text, truncated to 2000 chars. |
77
+
78
+ `create_interaction` (POST): `thread_id` *or* `ai_message_id` required.
79
+ Optional: `type_slug` (default `ai_feedback_record`; skipped silently when
80
+ the type isn't installed), `retrieved_sources`, `per_source_feedback`,
81
+ `ai_confidence`, `retrieval_confidence`, `kb_candidate`, `answer_quality`,
82
+ `assistance_outcome`, `eval_case_id`, the learning-proposal linkage id,
83
+ `review_notes`, `reviewed_by`, `reviewed_at`. The record is titled
84
+ `AI Interaction — <date>`, `status: 'active'`.
85
+
86
+ `update_interaction` (PATCH/POST): `id` required plus ≥1 allow-listed field:
87
+ `kb_article_id`, `human_message_id`, `human_edits_made`, `customer_feedback`,
88
+ `kb_candidate`, `reuse_count`, `per_source_feedback`, `answer_quality`,
89
+ `assistance_outcome`, `eval_case_id`, the learning-proposal linkage id,
90
+ `review_notes`, `reviewed_by`, `reviewed_at`.
91
+
92
+ `create_cil_proposal` (POST): `ticket_id` and `proposed_summary` required;
93
+ optional `thread_id`, `ai_message_id`, `source_message_ids`,
94
+ `source_chunk_ids`, `proposed_tags`. Returns `{ skipped: true }` when the
95
+ proposal type isn't installed.
96
+
97
+ `list` (GET): query params `feedback`, `kb_candidate`, `limit` (default 50),
98
+ `offset` (default 0).
99
+
100
+ ## Response contract
101
+
102
+ - `submit_feedback` → `{ message_id, ticket_id, feedback, escalated }` where
103
+ `escalated` is `feedback === 'not_helpful'`.
104
+ - `create_interaction` / `create_cil_proposal` → the created `items` record,
105
+ or `{ skipped: true, reason: '<type> type not found' }` when the type slug
106
+ can't be resolved — **not** an error.
107
+ - `update_interaction` → the admin-data `update` result.
108
+ - `list` → an array of `items` rows (filtered to the `ai_feedback_record`
109
+ type when that type exists).
110
+
111
+ ## Side effects
112
+
113
+ - `submit_feedback` stamps `messages.data.feedback` (+ `feedback_reason`,
114
+ and for `not_helpful` a `feedback_diagnosis` + `needs_review: true`).
115
+ Diagnosis is heuristic over the message's own evidence trail:
116
+ `missing_knowledge` (no retrieved sources), `retrieval_miss` (mean
117
+ similarity < 0.35), `reasoning_error` (sources retrieved but none cited),
118
+ else `wrong_answer`.
119
+ - Ticket lifecycle side effects on the `items` row: `not_helpful` sets
120
+ `status: 'human_assigned'` from any state except `human_assigned`/
121
+ `in_progress`, reopening `resolved`/`closed` tickets (`reopened_at`,
122
+ `reopen_reason: 'thumbs_down'`, resolution stamps cleared). `helpful` on a
123
+ `pending_resolution` ticket resolves it with `resolved_via:
124
+ ai_confirmed|human_confirmed` (chosen from `pending_proposed_by`) and
125
+ `aim_resolved_by_ai`.
126
+ - The first `items` row whose `data.ai_message_id` matches gets
127
+ `customer_feedback` (+reason/diagnosis) stamped.
128
+ - `ai_interaction.feedback_submitted` audit event plus a `funnel_signal`
129
+ `feedback_submitted` journey signal (fire-and-forget).
130
+
131
+ ## Errors
132
+
133
+ | Condition | Error |
134
+ |---|---|
135
+ | Missing inputs | `message_id is required` · `ticket_id is required` · `id is required` · `thread_id or ai_message_id is required` · `proposed_summary is required` |
136
+ | Bad enum | `feedback must be "helpful" or "not_helpful"` |
137
+ | Empty patch | `No updatable fields provided` |
138
+ | Unknown interaction id | `Interaction record not found` |
139
+ | Bad dispatch | `Invalid action or method` |
140
+
141
+ ## Agent notes
142
+
143
+ - Use `type_slug` on `create_interaction` to record under an app-specific
144
+ item type; the `{ skipped: true }` return makes the call safe to fire even
145
+ when the type isn't installed — check for `skipped` instead of treating it
146
+ as failure.
147
+ - `submit_feedback` is a lifecycle accelerator, not just a thumbs counter:
148
+ thumbs-up is the explicit customer confirmation that closes a
149
+ `pending_resolution` ticket, thumbs-down escalates and can reopen. Calling
150
+ it outside the support-ticket statuses is a no-op for the ticket fields.
151
+ - The pipeline `create_interaction` stage resolves `ai_message_id`/
152
+ `thread_id` from trigger data and passes `config.type_slug` through.
153
+ - `list` queries `ctx.db` — a machine principal sees account-scoped rows via
154
+ `adminDb`, a human sees RLS-filtered rows.
155
+ - See [AI agents, prompt configs, and embeddings](../../concepts/ai-and-prompts.md)
156
+ for how the run path, inference, and these records fit together.
@@ -0,0 +1,130 @@
1
+ ---
2
+ title: api-keys
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/api-keys.ts
9
+ - .framework/functions/_shared/hash-api-key.ts
10
+ - .framework/functions/_shared/middleware.ts
11
+ - .framework/public/_redirects
12
+ ---
13
+
14
+ # api-keys
15
+
16
+ ## What it does
17
+
18
+ Mints, validates, revokes, and lists API keys — the `sk_…` credentials
19
+ that resolve to machine principals on every other endpoint. Also exposes
20
+ per-key usage logs.
21
+
22
+ ## Route and methods
23
+
24
+ `/.netlify/functions/api-keys`, reachable as `/api/api-keys` (`/api/*` →
25
+ `/.netlify/functions/:splat`). Dispatch is by `?action=` plus method
26
+ (`?method=` can override the HTTP method):
27
+
28
+ | Call | Action |
29
+ |---|---|
30
+ | `GET /api/api-keys` | `list` |
31
+ | `GET /api/api-keys?id=<uuid>` | `get` |
32
+ | `POST /api/api-keys` | `create` |
33
+ | `POST /api/api-keys?action=validate` | `validate` |
34
+ | `POST /api/api-keys?action=revoke` | `revoke` |
35
+ | `GET /api/api-keys?action=usage-logs` | `listUsageLogs` |
36
+
37
+ Unrecognized `action` values fall through to the method-based default
38
+ branch, so `GET`/`POST` still dispatch; only methods other than `GET`,
39
+ `POST` (or a matched action with the wrong method) reach
40
+ `Invalid action or method`.
41
+
42
+ ## Authentication
43
+
44
+ Standard `createHandler`: `x-api-key`, internal secret, or
45
+ `Bearer <jwt>`; anonymous → 401. `list`, `create`, and `usage-logs`
46
+ additionally require `ctx.accountId` ("Account context required").
47
+
48
+ - System admins read via `adminDb` (all accounts); other principals use
49
+ `ctx.db` — JWT-scoped (RLS) for humans, service role for machines.
50
+ - There is **no machine-scope check** in this file. A machine principal
51
+ with an `account_id` can create/revoke/read keys within what its
52
+ service-role client reaches — treat key management as a trusted-surface
53
+ operation.
54
+
55
+ ## Request contract
56
+
57
+ **list** (query): `integration_id`, `key_type`, `is_active` (`'true'`/
58
+ `'false'`), `expires_before`, `expires_after`, `limit` (default 100),
59
+ `offset` (default 0). Ordered `created_at` desc.
60
+
61
+ **get** (query): `id`.
62
+
63
+ **create** (body): `name` required. Optional: `slug` (defaults to a
64
+ slugified `name`), `key_type` (default `'inbound'`), `permissions`,
65
+ `scopes`, `rate_limit` (default `100`), `expires_at`, `metadata`,
66
+ `integration_id`, `app_id` (resolved via `resolveAppUuid`, falling back
67
+ to the `x-app-id` header's app), `machine_type` (default `'integration'`),
68
+ `is_internal` (default `false`). `account_id` and `is_active: true` are
69
+ stamped from context; `created_by` is the principal id.
70
+
71
+ **validate** (body): `key_value` required; `required_permissions`
72
+ optional array.
73
+
74
+ **revoke** (body): `id` required.
75
+
76
+ **usage-logs** (query): `api_key_id`, `response_status`, `success`,
77
+ `date_from`, `date_to`, `limit` (default 100), `offset` (default 0).
78
+
79
+ ## Response contract
80
+
81
+ Standard `{ data, error, meta }` envelope.
82
+
83
+ - `create` → the inserted row (`id, name, key_type, rate_limit,
84
+ expires_at, is_active, created_at`) **plus `raw_key` — the only time
85
+ the secret is returned** — and `warning: 'Store this key now. It will
86
+ not be shown again.'` Raw key format: `sk_<32 hex chars>`.
87
+ - `validate` → `{ is_valid: true, api_key_id, integration_id, app_id,
88
+ account_id, scopes, permissions }`, or `{ is_valid: false, error }`
89
+ (HTTP 200 in both cases; `Insufficient permissions` carries
90
+ `api_key_id`).
91
+ - `list`/`get`/`revoke`/`usage-logs` → rows passed through
92
+ `sanitizeRecordData` + `omitSecretFields`, so `key_hash`/`key_value`-type
93
+ secret columns never leave the function. Only `key_prefix` (first 8
94
+ chars of the raw key) is stored in plaintext as the lookup index.
95
+
96
+ ## Side effects
97
+
98
+ - `create` inserts an `api_keys` row storing `key_prefix` and a
99
+ PBKDF2-SHA256 `key_hash` (the raw key is never stored).
100
+ - `revoke` sets `is_active = false` (keys are never hard-deleted here).
101
+ - `emitLog` writes `api_key.created` / `api_key.revoked` audit entries.
102
+ - Note: `api_key_usage_logs` rows are written by `resolvePrincipal` on
103
+ every authenticated request that uses a key, not by this endpoint.
104
+
105
+ ## Errors
106
+
107
+ | Condition | HTTP | Message |
108
+ |---|---|---|
109
+ | Missing `name` / `id` / `key_value` | 500 | `name is required` / `API key ID is required` / `key_value is required` |
110
+ | No account context | 500 | `Account context required` |
111
+ | Unknown action/method | 500 | `Invalid action or method` |
112
+ | DB failures | 500 | PostgREST message |
113
+
114
+ `validate` failures are payload-level (`is_valid: false`), not HTTP
115
+ errors.
116
+
117
+ ## Agent notes
118
+
119
+ - `validate` checks permissions as **any-match**: a key passes if at least
120
+ one of its `permissions` entries appears in `required_permissions`.
121
+ Wildcard `*:*` in `scopes` is honored at principal resolution, not here.
122
+ - Key verification happens on prefix-matched candidates (`key_prefix` =
123
+ first 8 chars), then PBKDF2 verify — `validate` mirrors exactly what
124
+ `resolvePrincipal` does on a live request.
125
+ - To call `validate`/`revoke`/`get` a caller needs only to be
126
+ authenticated (no `ctx.accountId` check on those paths); `revoke` on a
127
+ machine principal runs with the service role — pin management calls to
128
+ humans or `*:*` keys in practice.
129
+ - `expires_at` is nullable; expiry is enforced at resolution time
130
+ (`expires_at.is.null OR expires_at >= now`).
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: apps
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/apps.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/permissions.ts
11
+ - .framework/migrations/013_get_account_apps_role_filter.sql
12
+ - .framework/public/_redirects
13
+ ---
14
+
15
+ # apps
16
+
17
+ ## What it does
18
+
19
+ CRUD over `public.apps` — the app registry that controls routing
20
+ (`route_prefix`, `renderer`), visibility (`min_role`, `required_roles`),
21
+ and install metadata (`version`, `app_type`, `source`,
22
+ `integration_deps`). Also exposes schema, availability, and version-bump
23
+ RPC pass-throughs.
24
+
25
+ ## Route and methods
26
+
27
+ `/.netlify/functions/apps`, reachable as `/api/apps` (`/api/*` →
28
+ `/.netlify/functions/:splat`, `.framework/public/_redirects`). Dispatch
29
+ is by method plus `?action=`/`?id`/`?slug`:
30
+
31
+ | Call | Handler |
32
+ |---|---|
33
+ | `GET /api/apps` | `list` (also `?action=list`) |
34
+ | `GET /api/apps?id=<uuid>` or `?slug=<s>` or `?action=get` | `get` |
35
+ | `GET /api/apps?action=schema&slug=<s>` | `getSchema` → RPC `get_app_schema` |
36
+ | `GET /api/apps?action=available&slug=<s>` | `checkAvailability` → RPC `is_app_available` |
37
+ | `POST /api/apps` | `create` |
38
+ | `POST /api/apps?action=version` | `updateVersion` → RPC `update_app_version` |
39
+ | `PATCH /api/apps?id=<uuid>` (or `id` in body) | `update` |
40
+ | `DELETE /api/apps?id=<uuid>` (or `id` in body) | `remove` |
41
+
42
+ ## Authentication
43
+
44
+ Standard `createHandler` auth: `x-api-key` → machine principal,
45
+ `x-internal-secret` → internal principal, `Bearer <jwt>` → human;
46
+ anonymous → 401.
47
+
48
+ - `list` requires an account context (`ctx.accountId` or an explicit
49
+ `account_id` query param).
50
+ - `create` allows non-system-admins only when
51
+ `resolveFirstSurfacePermissions(principal.id, accountId, 'app',
52
+ 'create')` grants `canCreate`.
53
+ - `update`/`remove` have no role gate in this file — field-level control
54
+ comes from `validateUpdatePermissions` (update only) and RLS on
55
+ `ctx.db` for human callers. Machine principals run `ctx.db` as the
56
+ service role.
57
+
58
+ ## Request contract
59
+
60
+ **list** (query): `account_id` (defaults to `ctx.accountId`),
61
+ `include_system` (default true; `'false'` excludes), `include_inactive`
62
+ (`'true'` to include). Backed by the `get_account_apps` RPC, which
63
+ filters by ownership, active state, `required_roles`/`min_role` against
64
+ the caller's role slugs unless the caller is a system admin
65
+ (`.framework/migrations/013_get_account_apps_role_filter.sql`).
66
+
67
+ **get** (query): `id` or `slug` (active rows only).
68
+
69
+ **getSchema** / **checkAvailability** (query): `slug`.
70
+
71
+ **create** (body): `slug`, `name` required. Optional with defaults:
72
+ `version` `'1.0.0'`, `app_type` `'custom'`, `source` `'custom'`,
73
+ `owner_account_id` → caller's account, `config` `{}`, `nav_items` `[]`,
74
+ `min_role` `'member'`, `required_roles` `[]`, `integration_deps` `[]`, `metadata` `{}`,
75
+ `route_prefix` `'/' + slug`, `renderer` `'generic'`; `is_active: true`,
76
+ `is_system: false` are forced.
77
+
78
+ **update** (`id` in body or query + body): **no field allowlist** —
79
+ every body key except `id` is written verbatim plus `updated_at`, after
80
+ `validateUpdatePermissions(ctx, updates, current, 'apps')` passes.
81
+
82
+ **remove** (`id` in body or query): sets `is_active = false`.
83
+
84
+ **updateVersion** (body): `id`, `version` required.
85
+
86
+ ## Response contract
87
+
88
+ Standard `{ data, error, meta }` envelope.
89
+
90
+ - `list` → rows from `get_account_apps`, each passed through
91
+ `sanitizeRecordData`. Non-system-admins receive only the routing
92
+ whitelist (`id, slug, name, description, route_prefix, min_role,
93
+ required_roles, preview_domain, prod_domain, is_active, is_system,
94
+ renderer, created_at, updated_at`); admins get the full row.
95
+ - `get` → sanitized row. `getSchema` → raw RPC result.
96
+ `checkAvailability` → `{ available: boolean }`. `updateVersion` →
97
+ `{ success: true }`.
98
+
99
+ ## Side effects
100
+
101
+ - `create` inserts into `public.apps`; a duplicate `slug` is rejected by
102
+ a pre-check before insert (slug has a global `UNIQUE` constraint in
103
+ `.framework/migrations/001_core_schema.sql`).
104
+ - `update`/`remove` write `updated_at`; remove is a soft delete.
105
+ - `emitLog` writes `app.created`, `app.updated`, `app.deleted`, and
106
+ `app.version_updated` audit rows to `public.logs`.
107
+
108
+ ## Errors
109
+
110
+ | Condition | HTTP | Message |
111
+ |---|---|---|
112
+ | No account context (list, available) | 500 | `Account context required` |
113
+ | Missing slug/id | 500 | `App ID or slug is required` / `App slug is required` / `App ID is required` |
114
+ | Missing create fields | 500 | `slug and name are required` |
115
+ | Non-admin without create grant | 500 | `Insufficient permissions to create apps` |
116
+ | Duplicate slug | 500 | `App slug already exists` |
117
+ | Missing row (update/remove) | 500 | `App not found` |
118
+ | Missing version args | 500 | `App ID and version are required` |
119
+ | Unknown method | 500 | `Unsupported method: <METHOD>` |
120
+
121
+ Validation failures surface as HTTP 500 — thrown `Error`s without a
122
+ `statusCode` map to 500 in the shared error path.
123
+
124
+ ## Agent notes
125
+
126
+ - The four RPCs called here — `get_app_schema`, `is_app_available`,
127
+ `update_app_version` (plus `get_type_schema` for types) — are provided
128
+ by `.framework/migrations/070_app_type_schema_rpcs.sql`;
129
+ `get_account_apps` exists via
130
+ `.framework/migrations/013_get_account_apps_role_filter.sql`. Installs
131
+ missing migration 070 will see `function does not exist` errors.
132
+ - `update` will write **any** column present in the body (including
133
+ `slug`, `is_system`, `owner_account_id`) — the only guard is
134
+ `validateUpdatePermissions`, which is config-object (second-surface)
135
+ logic. Treat it as an admin-trusted endpoint.
136
+ - `remove` does not check `is_system` or permissions at all beyond
137
+ authentication + RLS for human callers.
138
+ - `renderer` defaults to `'generic'` on create — custom-renderer apps
139
+ must pass `renderer: 'custom'` explicitly (see
140
+ [Apps and renderers](../../concepts/apps-and-renderers.md)).
@@ -0,0 +1,99 @@
1
+ ---
2
+ title: auth
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/auth.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/principal.ts
11
+ - .framework/public/_redirects
12
+ ---
13
+
14
+ # auth
15
+
16
+ ## What it does
17
+
18
+ Returns the caller's resolved user context: the `people` row bound to the
19
+ JWT, its role and permissions, and the set of accounts the person can
20
+ reach through the account hierarchy. This is the endpoint the frontend
21
+ calls after login to learn "who am I".
22
+
23
+ ## Route and methods
24
+
25
+ `/.netlify/functions/auth`, reachable as `/api/auth` (`/api/*` →
26
+ `/.netlify/functions/:splat`). Dispatch is on `ctx.query.method`, which
27
+ the middleware fills from the HTTP method but a `?method=` query
28
+ parameter can override:
29
+
30
+ | Call | Result |
31
+ |---|---|
32
+ | `GET /api/auth` | `context` — the user-context payload below |
33
+ | `GET /api/auth?method=HEALTH` | `health` — `{ status: 'healthy', timestamp, service: 'auth' }` |
34
+ | any other method | error `Unsupported method: <method>` |
35
+
36
+ `HEALTH` is a query-param convention, not an HTTP method — there is no
37
+ `?action=` dispatch.
38
+
39
+ ## Authentication
40
+
41
+ Standard `createHandler`: `x-api-key`, internal secret, or
42
+ `Bearer <jwt>`; anonymous is rejected with 401, and `context` re-checks
43
+ `principal.id !== 'anonymous'`. All queries run on `ctx.db` — JWT-scoped
44
+ for humans (RLS applies), service role for machines/system. In practice
45
+ `context` is human-only: a machine principal's id is an API-key id, not a
46
+ `people.id`, so the person lookup returns "User not found".
47
+
48
+ ## Request contract
49
+
50
+ No parameters for `context` — identity comes entirely from the resolved
51
+ principal. `?method=HEALTH` selects the health check.
52
+
53
+ ## Response contract
54
+
55
+ `context` returns inside the standard `{ data, error, meta }` envelope:
56
+
57
+ ```json
58
+ {
59
+ "id": "<people.id>",
60
+ "email": "...",
61
+ "full_name": "...",
62
+ "account_id": "<uuid>",
63
+ "account": { "id", "slug", "display_name", "parent_id" },
64
+ "roles": ["<role slug>"],
65
+ "permissions": ["read", "write", ...],
66
+ "is_system_admin": false,
67
+ "status": "active",
68
+ "accessible_accounts": [{ "id", "slug", "display_name", "parent_id" }]
69
+ }
70
+ ```
71
+
72
+ - `roles` is a single-element array of the person's role slug.
73
+ - `permissions` is `['read', 'write', 'admin', 'system']` for
74
+ `system_admin`, else the role row's `permissions` array (possibly `[]`).
75
+ - `accessible_accounts` comes from the `get_accessible_accounts` RPC,
76
+ raced against a 5-second timeout; on failure or timeout it degrades to
77
+ `[]` rather than erroring.
78
+
79
+ ## Side effects
80
+
81
+ None — read-only (the middleware still emits request spans/logs).
82
+
83
+ ## Errors
84
+
85
+ | Condition | HTTP | Message |
86
+ |---|---|---|
87
+ | Anonymous | 401 | `Authentication required` |
88
+ | `people` row missing/inactive | 500 | `User not found: <principal.id>` |
89
+ | Unsupported method | 500 | `Unsupported method: <method>` |
90
+
91
+ ## Agent notes
92
+
93
+ - `is_system_admin` and `permissions` are derived here, not authoritative
94
+ checks — server-side endpoints re-evaluate roles from the principal.
95
+ - The 5-second `get_accessible_accounts` timeout means a slow hierarchy
96
+ query yields `accessible_accounts: []`, not an error; clients should not
97
+ treat an empty list as proof of a single-account deployment.
98
+ - Role/account changes can lag up to ~60 seconds because human principals
99
+ are cached per warm function instance.
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: debug-auth
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/debug-auth.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/public/_redirects
11
+ ---
12
+
13
+ # debug-auth
14
+
15
+ ## What it does
16
+
17
+ Diagnostic endpoint for troubleshooting authentication. Reports whether a
18
+ principal was resolved from the request credentials and whether the
19
+ function's Supabase environment variables are configured. **This is a
20
+ debug/diagnostic endpoint, not part of the product API.**
21
+
22
+ ## Route and methods
23
+
24
+ `/.netlify/functions/debug-auth`, reachable as
25
+ `/api/debug-auth` (`/api/*` → `/.netlify/functions/:splat`). There is no
26
+ HTTP-method or `action` dispatch — the only selector is the `?method=`
27
+ **query parameter**, read directly off the request:
28
+
29
+ | Call | Result |
30
+ |---|---|
31
+ | `GET /api/debug-auth` (or `?method=debug`, the default) | env + principal summary |
32
+ | `GET /api/debug-auth?method=testJwt` | principal echo for a Bearer token |
33
+ | any other `?method=` value | falls through to `debug` |
34
+
35
+ ## Authentication
36
+
37
+ Standard `createHandler` — so a valid `x-api-key`, internal secret, or
38
+ `Bearer <jwt>` is required and anonymous gets 401. As of the S-006 fix the
39
+ handler **requires a `system_admin` principal** — non-admin principals are
40
+ rejected with `statusCode` 403. That is the
41
+ point of a diagnostic — it answers "did my credentials resolve, and as
42
+ what?".
43
+
44
+ ## Request contract
45
+
46
+ `?method=debug|testJwt` only. No body.
47
+
48
+ ## Response contract
49
+
50
+ Both branches return an object with a top-level `data` key, so the payload
51
+ is nested one level deep inside the standard envelope —
52
+ `{ "data": { "data": { ... } }, "error": null, "meta": {...} }`.
53
+
54
+ `debug` returns:
55
+
56
+ ```json
57
+ {
58
+ "message": "Debug auth endpoint",
59
+ "hasPrincipal": true,
60
+ "principalType": "human",
61
+ "requestId": "<uuid>",
62
+ "envVars": {
63
+ "supabaseUrl": "SET",
64
+ "supabaseAnonKey": "SET"
65
+ }
66
+ }
67
+ ```
68
+
69
+ `testJwt` returns `{ tokenValid: true, principal: { type, id, accountId } }`,
70
+ or `{ data: null, error: 'No principal resolved from token' }` when the
71
+ principal is missing.
72
+
73
+ ## Side effects
74
+
75
+ None — read-only. Env-var values are never returned, only `SET`/`NOT_SET`
76
+ flags for `SUPABASE_URL` and `SUPABASE_ANON_KEY`.
77
+
78
+ ## Errors
79
+
80
+ | Condition | HTTP | Message |
81
+ |---|---|---|
82
+ | Anonymous | 401 | `Authentication required` |
83
+
84
+ ## Agent notes
85
+
86
+ - Use `?method=testJwt` to verify a `Bearer` token resolves to the
87
+ expected person + account, and `debug` to sanity-check a deployment's
88
+ env wiring.
89
+ - Because it is an admin-only diagnostic surface, do not build
90
+ product logic on it and expect it to behave like a hardened endpoint.
91
+ - The double-nested `data.data` shape is real — unwrap twice.