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.
- package/.env.example +13 -3
- package/.framework/cli/commands/bootstrap.ts +392 -0
- package/.framework/cli/commands/dev.ts +91 -218
- package/.framework/cli/commands/doctor.ts +132 -26
- package/.framework/cli/commands/generate.ts +38 -298
- package/.framework/cli/commands/init.ts +20 -1
- package/.framework/cli/commands/migrate.ts +36 -30
- package/.framework/cli/commands/migrations.ts +16 -56
- package/.framework/cli/commands/upgrade.ts +35 -0
- package/.framework/cli/commands/validate-schema.ts +147 -0
- package/.framework/cli/context.ts +223 -110
- package/.framework/cli/index.ts +6 -0
- package/.framework/cli/lib/project-files.ts +241 -1
- package/.framework/functions/_shared/agent-runner.ts +1000 -81
- package/.framework/functions/_shared/cil.ts +2 -1
- package/.framework/functions/_shared/db.ts +3 -6
- package/.framework/functions/_shared/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/kb.ts +61 -7
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/message-sequence.ts +53 -0
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/_shared/pipeline-runner.ts +150 -37
- package/.framework/functions/_shared/principal.ts +9 -6
- package/.framework/functions/_shared/trigger-engine.ts +149 -40
- package/.framework/functions/admin-data.ts +48 -31
- package/.framework/functions/ai-agents.ts +103 -33
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +43 -26
- package/.framework/functions/cil-ingest.ts +48 -15
- package/.framework/functions/debug-auth.ts +14 -8
- package/.framework/functions/deliveries.ts +39 -12
- package/.framework/functions/embeddings.ts +145 -76
- package/.framework/functions/integration-outbound.ts +67 -27
- package/.framework/functions/integrations.ts +254 -127
- package/.framework/functions/logs.ts +273 -133
- package/.framework/functions/observability.ts +277 -147
- package/.framework/functions/pipeline-executions.ts +1 -1
- package/.framework/functions/pipeline-runner-background.ts +120 -0
- package/.framework/functions/pipelines.ts +14 -9
- package/.framework/functions/prompt-configs.ts +7 -4
- package/.framework/functions/roles.ts +8 -5
- package/.framework/functions/security.ts +30 -17
- package/.framework/functions/system-cron.ts +250 -128
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/trigger-scheduler.ts +65 -5
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- package/.framework/migrations/064_episode_leases.sql +196 -0
- package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
- package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
- package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
- package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
- package/.framework/migrations/069_log_stats_window.sql +54 -0
- package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
- package/.framework/migrations/071_timer_interval_keys.sql +96 -0
- package/.framework/migrations/072_slug_autofill.sql +77 -0
- package/.framework/migrations/073_seed_member_roles.sql +26 -0
- package/.framework/schemas/spine.config.schema.json +62 -0
- package/.framework/src/App.tsx +3 -1
- package/.framework/src/apps/admin/index.tsx +1141 -775
- package/.framework/src/components/ErrorBoundary.tsx +136 -0
- package/.framework/src/components/admin/AdminListPage.tsx +81 -73
- package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
- package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
- package/.framework/src/components/layout/Layout.tsx +4 -2
- package/.framework/src/components/layout/Sidebar.tsx +304 -179
- package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
- package/.framework/src/components/runtime/DataHeader.tsx +96 -48
- package/.framework/src/components/runtime/DataListPage.tsx +3 -26
- package/.framework/src/components/runtime/DataStats.tsx +10 -10
- package/.framework/src/components/runtime/DataTable.tsx +12 -10
- package/.framework/src/components/ui/sheet.tsx +24 -26
- package/.framework/src/components/ui/textarea.tsx +74 -48
- package/.framework/src/hooks/useBranding.ts +47 -0
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +57 -0
- package/.framework/src/instructions/ai-agents.md +69 -0
- package/.framework/src/instructions/alerts.md +80 -0
- package/.framework/src/instructions/api-keys.md +87 -0
- package/.framework/src/instructions/apps.md +77 -0
- package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
- package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
- package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
- package/.framework/src/instructions/concepts/automation.md +186 -0
- package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
- package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
- package/.framework/src/instructions/concepts/design-schema.md +152 -0
- package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
- package/.framework/src/instructions/concepts/observability.md +116 -0
- package/.framework/src/instructions/concepts/permissions.md +150 -0
- package/.framework/src/instructions/concepts/runtime-records.md +111 -0
- package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
- package/.framework/src/instructions/concepts/spine-way.md +172 -0
- package/.framework/src/instructions/deliveries.md +59 -0
- package/.framework/src/instructions/guide/00-orientation.md +106 -0
- package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
- package/.framework/src/instructions/guide/02-project-map.md +147 -0
- package/.framework/src/instructions/guide/03-domain-model.md +223 -0
- package/.framework/src/instructions/guide/04-first-app.md +227 -0
- package/.framework/src/instructions/guide/05-glossary.md +183 -0
- package/.framework/src/instructions/integrations.md +96 -0
- package/.framework/src/instructions/internal/cil-markers.md +83 -0
- package/.framework/src/instructions/invites.md +60 -0
- package/.framework/src/instructions/kb.md +80 -0
- package/.framework/src/instructions/logs.md +77 -0
- package/.framework/src/instructions/observability.md +61 -0
- package/.framework/src/instructions/operations/deployment.md +115 -0
- package/.framework/src/instructions/operations/migrations.md +125 -0
- package/.framework/src/instructions/operations/security-checklist.md +148 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/pipeline-executions.md +62 -0
- package/.framework/src/instructions/pipelines.md +78 -0
- package/.framework/src/instructions/prompts.md +95 -0
- package/.framework/src/instructions/reference/api/admin-data.md +162 -0
- package/.framework/src/instructions/reference/api/auth.md +113 -0
- package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
- package/.framework/src/instructions/reference/app-manifest.md +150 -0
- package/.framework/src/instructions/reference/cli.md +124 -0
- package/.framework/src/instructions/reference/database.md +132 -0
- package/.framework/src/instructions/reference/design-schema.md +280 -0
- package/.framework/src/instructions/reference/env-vars.md +133 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
- package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
- package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
- package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
- package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
- package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
- package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
- package/.framework/src/instructions/reference/functions/apps.md +140 -0
- package/.framework/src/instructions/reference/functions/auth.md +99 -0
- package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
- package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
- package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
- package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
- package/.framework/src/instructions/reference/functions/index.md +103 -0
- package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
- package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
- package/.framework/src/instructions/reference/functions/integrations.md +130 -0
- package/.framework/src/instructions/reference/functions/invites.md +182 -0
- package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
- package/.framework/src/instructions/reference/functions/kb.md +166 -0
- package/.framework/src/instructions/reference/functions/logs.md +141 -0
- package/.framework/src/instructions/reference/functions/observability.md +105 -0
- package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
- package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
- package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
- package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
- package/.framework/src/instructions/reference/functions/roles.md +121 -0
- package/.framework/src/instructions/reference/functions/security.md +140 -0
- package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
- package/.framework/src/instructions/reference/functions/system.md +106 -0
- package/.framework/src/instructions/reference/functions/tests.md +97 -0
- package/.framework/src/instructions/reference/functions/timers.md +117 -0
- package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
- package/.framework/src/instructions/reference/functions/triggers.md +140 -0
- package/.framework/src/instructions/reference/functions/types.md +146 -0
- package/.framework/src/instructions/reference/seeds.md +126 -0
- package/.framework/src/instructions/reference/shared-modules.md +127 -0
- package/.framework/src/instructions/reference/spine-config.md +114 -0
- package/.framework/src/instructions/roles.md +80 -0
- package/.framework/src/instructions/runtime-detail.md +70 -0
- package/.framework/src/instructions/runtime-list.md +77 -0
- package/.framework/src/instructions/security.md +67 -0
- package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
- package/.framework/src/instructions/tasks/add-a-role.md +96 -0
- package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
- package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
- package/.framework/src/instructions/tasks/add-a-type.md +151 -0
- package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
- package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
- package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
- package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
- package/.framework/src/instructions/tasks/test-your-code.md +108 -0
- package/.framework/src/instructions/tasks/write-a-function.md +132 -0
- package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
- package/.framework/src/instructions/testing.md +59 -0
- package/.framework/src/instructions/timers.md +82 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/branding.ts +128 -0
- package/.framework/src/pages/NoAppPage.tsx +3 -1
- package/.framework/src/pages/NotFoundPage.tsx +62 -31
- package/.framework/src/pages/SetupPage.tsx +5 -2
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
- package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
- package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
- package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
- package/.framework/src/pages/admin/AppsPage.tsx +104 -66
- package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
- package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
- package/.framework/src/pages/admin/LogsPage.tsx +144 -76
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
- package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
- package/.framework/src/pages/admin/RolesPage.tsx +86 -43
- package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
- package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
- package/.framework/src/pages/admin/TimersPage.tsx +90 -62
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
- package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
- package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
- package/.framework/src/pages/admin/TypesPage.tsx +90 -48
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
- package/.framework/src/pages/auth/LoginPage.tsx +4 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
- package/.framework/templates/agent/AGENTS.md +150 -0
- package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
- package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
- package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
- package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
- package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
- package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
- package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
- package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
- package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
- package/.framework/templates/project.json +5 -1
- package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
- package/.framework/tests/unit/agent-files.test.ts +355 -0
- package/.framework/tests/unit/agent-runner.test.ts +496 -47
- package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
- package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
- package/.framework/tests/unit/bootstrap.test.ts +242 -0
- package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
- package/.framework/tests/unit/debug-auth.test.ts +38 -0
- package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
- package/.framework/tests/unit/episode-lease.test.ts +695 -0
- package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -0
- package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
- package/.framework/tests/unit/principal-cache.test.ts +15 -0
- package/.framework/tests/unit/system-context-denial.test.ts +145 -0
- package/.framework/tests/unit/system-cron.test.ts +240 -0
- package/.framework/tests/unit/timers.test.ts +159 -0
- package/.framework/tests/unit/trigger-engine.test.ts +278 -91
- package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
- package/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- package/netlify.toml +5 -0
- package/package.json +15 -4
- package/templates/project/package-lock.json +2 -2
- package/.framework/docs/admin-routes.md +0 -76
- package/.framework/docs/api-docs-progress.md +0 -38
- package/.framework/docs/api-governance.md +0 -146
- package/.framework/docs/api-testing-results.md +0 -212
- package/.framework/docs/assembly-launch-guide.md +0 -138
- package/.framework/docs/audit-results.md +0 -590
- package/.framework/docs/db-api-inventory.md +0 -95
- package/.framework/docs/gap-remediation-backlog.md +0 -103
- package/.framework/docs/ui-adoption-verification.md +0 -111
- package/.framework/docs/ui-api-coverage.md +0 -84
- package/.framework/docs/v2-compatibility-audit.md +0 -228
- package/.framework/mcp/index.ts +0 -133
- package/.framework/mcp/load-env.ts +0 -60
- package/.framework/mcp/shared/app-manifest.ts +0 -275
- package/.framework/mcp/shared/db.ts +0 -477
- package/.framework/mcp/shared/kb.ts +0 -118
- package/.framework/mcp/tools/architect.ts +0 -431
- package/.framework/mcp/tools/cil/context.ts +0 -345
- package/.framework/mcp/tools/cil/graph.ts +0 -242
- package/.framework/mcp/tools/cil/ingest.ts +0 -1217
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
- package/.framework/mcp/tools/cil/pipeline.ts +0 -767
- package/.framework/mcp/tools/cil/project.ts +0 -791
- package/.framework/mcp/tools/cil/retrieval.ts +0 -336
- package/.framework/mcp/tools/cil/search-core.ts +0 -454
- package/.framework/mcp/tools/cil/search.ts +0 -97
- package/.framework/mcp/tools/cil/suggest.ts +0 -186
- package/.framework/mcp/tools/cil/validate.ts +0 -1369
- package/.framework/mcp/tools/kb/delete.ts +0 -139
- package/.framework/mcp/tools/kb/index.ts +0 -52
- package/.framework/mcp/tools/kb/ingest.ts +0 -84
- package/.framework/mcp/tools/kb/list-types.ts +0 -102
- package/.framework/mcp/tools/kb/publish.ts +0 -100
- package/.framework/mcp/tools/kb/reindex.ts +0 -86
- package/.framework/mcp/tools/kb/revoke.ts +0 -85
- package/.framework/mcp/tools/kb/search.ts +0 -101
- package/.framework/mcp/tools/kb/upsert.ts +0 -125
- 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.
|