spine-framework 0.4.4 → 0.5.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/hash-api-key.ts +183 -79
- package/.framework/functions/_shared/logging.ts +562 -224
- package/.framework/functions/_shared/middleware.ts +47 -44
- package/.framework/functions/admin-data.ts +39 -28
- package/.framework/functions/ai-agents.ts +10 -6
- 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/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 +240 -127
- package/.framework/functions/tests.ts +7 -6
- package/.framework/functions/timers.ts +310 -107
- package/.framework/functions/triggers.ts +28 -13
- package/.framework/functions/types.ts +13 -10
- 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/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 +88 -79
- package/.framework/src/components/admin/EntitySelect.tsx +181 -0
- package/.framework/src/components/admin/PageInstructions.tsx +86 -127
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
- package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
- package/.framework/src/components/layout/Sidebar.tsx +300 -177
- 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/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/pages/NotFoundPage.tsx +62 -31
- 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/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 +4 -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 +479 -44
- 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/integration-outbound-credentials.test.ts +167 -0
- package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
- package/.framework/tests/unit/logging-app-id.test.ts +75 -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/README.md +125 -166
- package/config/tsconfig.cli.json +2 -2
- 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,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pipelines
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/pipelines.ts
|
|
9
|
+
- .framework/functions/_shared/pipeline-runner.ts
|
|
10
|
+
- .framework/functions/pipeline-executions.ts
|
|
11
|
+
- .framework/src/pages/admin/PipelinesPage.tsx
|
|
12
|
+
- .framework/src/pages/admin/PipelineDetailPage.tsx
|
|
13
|
+
- .framework/migrations/053_builtin_action_seeds.sql
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## What this is for
|
|
17
|
+
|
|
18
|
+
A **pipeline** is a multi-step workflow run by the pipeline runner. It can be triggered manually, on a schedule (via a timer), by a webhook, or by an event (via a trigger). This page controls pipeline metadata, trigger type, and the JSON `stages` the runner executes.
|
|
19
|
+
|
|
20
|
+
## Fields and settings
|
|
21
|
+
|
|
22
|
+
- **Name** — string. Display name; missing name prevents saving.
|
|
23
|
+
- **Trigger Type** — select: `manual` (run by button), `scheduled` (invoked by a timer), `webhook` (HTTP call), `event` (Spine trigger event). Determines what causes a run.
|
|
24
|
+
- **Active** — checkbox. Inactive pipelines never run, regardless of trigger.
|
|
25
|
+
- **App ID** — select, optional. Owning app; blank = account-scoped.
|
|
26
|
+
- **Description** — string, optional.
|
|
27
|
+
- **Config (JSON)** — free-form runtime configuration passed to the runner.
|
|
28
|
+
- **Stages (JSON)** — required **array** of step definitions executed in order by the runner. Each stage is `{ "stage_type": "<action slug>", "config": {...}, "continue_on_error": false }` — `stage_type` is looked up in `public.actions` (`is_active`) at run time; an unknown slug fails the run with "Action not found for stage type".
|
|
29
|
+
- **Metadata (JSON)** — optional metadata.
|
|
30
|
+
|
|
31
|
+
**Downstream effects**
|
|
32
|
+
|
|
33
|
+
- The runner executes `stages` in array order; the JSON is the source of truth — there is no visual step editor. Stage config can embed `{{trigger.*}}` and `{{record.*}}` placeholders, resolved recursively at run time (`record` = the entity row that fired the event).
|
|
34
|
+
- A failed stage aborts the run unless the stage sets `continue_on_error: true`.
|
|
35
|
+
- Triggers and timers reference pipelines by ID. Changing `trigger_type` does not rewire existing triggers/timers — relink them separately.
|
|
36
|
+
- `scheduled` without a linked timer, or `event` without a matching trigger, means the pipeline never fires.
|
|
37
|
+
|
|
38
|
+
## Minimal configuration
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"name": "Notify on create",
|
|
43
|
+
"trigger_type": "event",
|
|
44
|
+
"is_active": true,
|
|
45
|
+
"stages": [
|
|
46
|
+
{
|
|
47
|
+
"stage_type": "send_notification",
|
|
48
|
+
"config": {
|
|
49
|
+
"recipients": ["role:admin"],
|
|
50
|
+
"message": "Record {{record.id}} created"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Full configuration
|
|
58
|
+
|
|
59
|
+
Multi-stage pipelines compose the seeded built-in action slugs — `update_item`, `create_record`, `http_request`, `send_notification`, `run_pipeline`, `search_knowledge`, `query_items`, `agent_inference`, `generate_embeddings`, `revoke_embeddings`, `create_interaction` — with `config` carrying shared settings (batch sizes, model names, endpoints) and `metadata` carrying ownership/version notes. Each stage's own `config` is merged over the action row's defaults.
|
|
60
|
+
|
|
61
|
+
## Example
|
|
62
|
+
|
|
63
|
+
A support app ships an `auto_embed_on_publish` pipeline (`trigger_type: "event"`): stage 1 fetches the published record via `admin-data`, stage 2 calls an AI agent to generate the embedding, stage 3 writes it back. Two apps can each own a same-named pipeline — the **App** column disambiguates them in the list.
|
|
64
|
+
|
|
65
|
+
## Core files
|
|
66
|
+
|
|
67
|
+
- `.framework/functions/pipelines.ts` — API
|
|
68
|
+
- `.framework/functions/pipeline-runner-background.ts` — background executor
|
|
69
|
+
- `.framework/functions/pipeline-executions.ts` — run history (`observability/executions`)
|
|
70
|
+
- `.framework/src/pages/admin/PipelinesPage.tsx` / `PipelineDetailPage.tsx` — admin UI
|
|
71
|
+
- Tables: `public.pipelines`, `public.pipeline_executions`
|
|
72
|
+
|
|
73
|
+
## Agent notes
|
|
74
|
+
|
|
75
|
+
- "Runs today" and "last run" come from `pipeline_executions` — a pipeline with no executions shows `Never`.
|
|
76
|
+
- Stages must be a JSON **array**; a pasted object or string is the most common save-time mistake.
|
|
77
|
+
- The runner also implements `send_email`, `kb_ingest`, and `kb_revoke` handlers, but no `actions` rows are seeded for them — a stage using those slugs throws "Action not found" until you insert a matching `public.actions` row yourself.
|
|
78
|
+
- Activating/pausing is `is_active` — a paused pipeline keeps its triggers but they no-op.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prompt Configs
|
|
3
|
+
audience: both
|
|
4
|
+
kind: entity
|
|
5
|
+
verified_against: e13127d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/prompt-configs.ts
|
|
9
|
+
- .framework/functions/_shared/agent-runner.ts
|
|
10
|
+
- .framework/src/pages/admin/PromptConfigsPage.tsx
|
|
11
|
+
- .framework/src/pages/admin/PromptConfigDetailPage.tsx
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What this is for
|
|
15
|
+
|
|
16
|
+
A **prompt config** stores the prompt body, model settings, escalation rules, knowledge sources, available tools, and metadata that AI agents and chat interfaces use at runtime. Callers select a config by slug.
|
|
17
|
+
|
|
18
|
+
## Fields and settings
|
|
19
|
+
|
|
20
|
+
- **Name** *(required)* — display and lookup name.
|
|
21
|
+
- **Slug** *(required)* — unique key callers use to select this config.
|
|
22
|
+
- **Description** — optional purpose note.
|
|
23
|
+
- **App ID** — optional owning app.
|
|
24
|
+
- **System Prompt** — base system instructions (composes with the agent's own `system_prompt` at run time).
|
|
25
|
+
- **Context Template** — the prompt body. The agent runner substitutes only `{{user_message}}` and `{{conversation_history}}`; any other `{{placeholder}}` is left literal in the rendered prompt.
|
|
26
|
+
- **Model** — default `gpt-4o`. The model used for this prompt.
|
|
27
|
+
- **Temperature** — default `0.7`. `0` = deterministic; higher = more random.
|
|
28
|
+
- **Max Tokens** — default `4000`. Response token limit.
|
|
29
|
+
- **Is Multi Turn** — default `true`. Whether conversation history is included.
|
|
30
|
+
- **Max History Messages** — default `20`. Prior messages to include when multi-turn.
|
|
31
|
+
- **Confidence Threshold** — optional minimum confidence before acting.
|
|
32
|
+
- **Escalation Action / Target** — where to route low-confidence results.
|
|
33
|
+
- **Output Mode / Field** — how and where the model output is emitted.
|
|
34
|
+
- **Requires Review** — default `false`. Outputs need human review before use.
|
|
35
|
+
- **Knowledge Sources (JSON array)** — data sources the prompt can draw from.
|
|
36
|
+
- **Available Tools (JSON array)** — tool definitions the prompt may invoke.
|
|
37
|
+
- **Tool Constraints (JSON object)** — rules restricting tool use.
|
|
38
|
+
- **Metadata (JSON object)** — extra structured data.
|
|
39
|
+
- **Active** — default `true`. Inactive configs are not returned to callers.
|
|
40
|
+
|
|
41
|
+
**Downstream effects**
|
|
42
|
+
|
|
43
|
+
- Agents and pipelines resolve prompt configs by slug or by ID (`thread.data.prompt_config_id` → `agent.metadata.default_prompt_config_id`) — a missing or inactive config breaks those calls.
|
|
44
|
+
- `requires_review` + `escalation_*` define the human-in-the-loop path; without an escalation target, low-confidence output is just flagged.
|
|
45
|
+
- JSON fields must be valid and correctly shaped (arrays for sources/tools, objects for constraints/metadata).
|
|
46
|
+
|
|
47
|
+
## Minimal configuration
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"name": "Ticket reply draft",
|
|
52
|
+
"slug": "ticket-reply-draft",
|
|
53
|
+
"context_template": "Draft a reply to this message: {{user_message}}",
|
|
54
|
+
"model": "gpt-4o",
|
|
55
|
+
"is_active": true
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Full configuration
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"name": "Ticket triage",
|
|
64
|
+
"slug": "ticket-triage",
|
|
65
|
+
"system_prompt": "You classify support tickets.",
|
|
66
|
+
"context_template": "Message: {{user_message}}\nHistory: {{conversation_history}}",
|
|
67
|
+
"model": "gpt-4o",
|
|
68
|
+
"temperature": 0.2,
|
|
69
|
+
"max_tokens": 1500,
|
|
70
|
+
"confidence_threshold": 0.75,
|
|
71
|
+
"escalation_action": "assign",
|
|
72
|
+
"escalation_target": "support-manager",
|
|
73
|
+
"requires_review": true,
|
|
74
|
+
"knowledge_sources": [{ "type": "kb", "slug": "support-kb" }],
|
|
75
|
+
"available_tools": ["search_kb", "set_field"],
|
|
76
|
+
"tool_constraints": { "max_calls": 3 },
|
|
77
|
+
"is_active": true
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Example
|
|
82
|
+
|
|
83
|
+
A support app ships `ticket-triage` (low temperature, tools constrained to `search_kb` + `set_field`, `requires_review`) and `customer-chat` (multi-turn, `max_history_messages: 30`, `output_mode` streaming). The triage pipeline resolves `ticket-triage` by slug for every new ticket.
|
|
84
|
+
|
|
85
|
+
## Core files
|
|
86
|
+
|
|
87
|
+
- `.framework/functions/prompt-configs.ts` — API
|
|
88
|
+
- `.framework/src/pages/admin/PromptConfigsPage.tsx` / `PromptConfigDetailPage.tsx` — admin UI
|
|
89
|
+
- Table: `public.prompt_configs`
|
|
90
|
+
|
|
91
|
+
## Agent notes
|
|
92
|
+
|
|
93
|
+
- The `is_default`/`prompt_type`/`category` columns do **not** exist — the dead UI for them was removed (F-013); use `is_system` and `output_mode`.
|
|
94
|
+
- Pair with an `ai_agents` record: the agent holds model/behavior, this config holds the prompt text.
|
|
95
|
+
- Keep templates small and pass data through placeholders — large embedded blobs defeat versioning.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: admin-data API
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/admin-data.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/permissions.ts
|
|
11
|
+
- .framework/functions/_shared/resolve-ids.ts
|
|
12
|
+
- .framework/functions/_shared/trigger-engine.ts
|
|
13
|
+
- .framework/src/lib/api.ts
|
|
14
|
+
- .framework/src/hooks/useEntityRecord.ts
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# `admin-data` API
|
|
18
|
+
|
|
19
|
+
`POST|GET|PATCH|DELETE /api/admin-data` is the single generic CRUD endpoint
|
|
20
|
+
custom apps use for all runtime records. It resolves types, evaluates the
|
|
21
|
+
record's stamped `design_schema` permissions, stamps schemas on write,
|
|
22
|
+
soft-deletes, and fires triggers. Custom apps must route record CRUD through
|
|
23
|
+
it rather than shipping their own record functions — the shared permission,
|
|
24
|
+
type-resolution, audit, and trigger logic lives here.
|
|
25
|
+
|
|
26
|
+
Handler: `.framework/functions/admin-data.ts` via `createHandler`, so every
|
|
27
|
+
request is authenticated and wrapped in the standard envelope — see
|
|
28
|
+
[Authentication](auth.md) and [Errors and Envelopes](errors-and-envelopes.md).
|
|
29
|
+
|
|
30
|
+
## Request model
|
|
31
|
+
|
|
32
|
+
Action and entity come from the **query string**; record fields come from the
|
|
33
|
+
JSON body (writes) or additional query params (reads). With no `action`, the
|
|
34
|
+
HTTP method picks the verb (`GET`+`id` → get, `GET` → list, `POST` → create,
|
|
35
|
+
`PATCH` → update, `DELETE` → remove).
|
|
36
|
+
|
|
37
|
+
| Action | Method | Params |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `list` | GET | `entity` (required), `search`, `type_slug`, `type_slugs` (csv), `sort_field` (default `created_at`), `sort_direction` (`asc`/`desc`, default `desc`), `limit` (default 50), `offset`, `view`, `flag_only`, `exclude_flag`, plus any other `key=value` as `eq` filters (`is_active`/`is_verified`/`is_primary` coerce `'true'` → boolean) |
|
|
40
|
+
| `get` | GET | `entity`, `id`, `view`, `lineage=0` (items only — skips lineage attach) |
|
|
41
|
+
| `stats` | GET | `entity` → `{entity, count}` |
|
|
42
|
+
| `create` | POST | `entity` (query or body), `type_slug` **or** `type_id` (required for non-raw entities), record fields |
|
|
43
|
+
| `update` | PATCH | `entity`, `id`, record fields (partial) |
|
|
44
|
+
| `remove` | DELETE | `entity`, `id`, `soft` (default `'true'`) |
|
|
45
|
+
|
|
46
|
+
Valid entities: `accounts`, `people`, `items`, `threads`, `messages`,
|
|
47
|
+
`links`, `attachments`, `watchers`, `item_progress`, `logs`, `test_runs`,
|
|
48
|
+
`observability`, `test_cases`, `types`, `link_types`, `ai_agents`,
|
|
49
|
+
`prompt_configs`.
|
|
50
|
+
|
|
51
|
+
`logs`, `test_runs`, `observability`, `test_cases` are **raw entities**:
|
|
52
|
+
create takes a per-entity field allowlist, requires specific fields
|
|
53
|
+
(`logs`: `level`+`message`; `test_runs`: `suite`; `observability`:
|
|
54
|
+
`metric_name`+`metric_value`; `test_cases`: `slug`+`name`), and needs no
|
|
55
|
+
`type_id`.
|
|
56
|
+
|
|
57
|
+
## Response shapes
|
|
58
|
+
|
|
59
|
+
All responses use the standard envelope `{data, error, meta:{requestId,
|
|
60
|
+
duration}}`. `data` is:
|
|
61
|
+
|
|
62
|
+
- `list` → array of sanitized records, or `{data, schema, view}` when
|
|
63
|
+
`view`+`type_slug` are passed and the type's `design_schema` resolves
|
|
64
|
+
- `get` → the sanitized record (+`parents`/`children`/`type_slug` lineage on
|
|
65
|
+
`items` unless `lineage=0`), or `{data, schema, view}` when `view` is
|
|
66
|
+
passed and the record carries `design_schema`
|
|
67
|
+
- `create`/`update` → the sanitized record
|
|
68
|
+
- `remove` → `{deleted: true, soft: true, data: <record>}` on soft delete;
|
|
69
|
+
`{deleted: true, soft: false}` on hard delete
|
|
70
|
+
- `stats` → `{entity, count}`
|
|
71
|
+
|
|
72
|
+
Sanitization applies `design_schema.fields.*.permissions`: fields the
|
|
73
|
+
principal cannot read are stripped before the row is returned.
|
|
74
|
+
|
|
75
|
+
## Permission evaluation order
|
|
76
|
+
|
|
77
|
+
1. `createHandler` resolves the principal (401 if anonymous).
|
|
78
|
+
2. `create` → `resolveFirstSurfacePermissions(personId, accountId,
|
|
79
|
+
typeSlug, 'create', designSchema)`; machine principals with `*:*` scope
|
|
80
|
+
bypass. Denied → 403.
|
|
81
|
+
3. `update` → `validateFirstSurfaceUpdatePermissions` on the **existing
|
|
82
|
+
record's** stamped `design_schema` (no `record_permissions` → deny); each
|
|
83
|
+
`data.<field>` written requires field `write`; sanitizes values through
|
|
84
|
+
`validation_schema`. `system_admin` bypasses.
|
|
85
|
+
4. `remove` → `resolveFirstSurfacePermissions(..., 'delete',
|
|
86
|
+
existing.design_schema)`; `system_admin` bypasses.
|
|
87
|
+
5. `list`/`get` run under `ctx.db` (RLS-scoped for humans, `adminDb` for
|
|
88
|
+
internal/system-admin), then per-record sanitization.
|
|
89
|
+
|
|
90
|
+
## Stamping & account resolution on create
|
|
91
|
+
|
|
92
|
+
- `type_slug` resolves via `resolveTypeRecord(kind, slug)` (kind from
|
|
93
|
+
`ENTITY_TO_KIND`); `type_id` fetches the row and requires `is_active`.
|
|
94
|
+
One of the two is mandatory — "every runtime record must reference a type".
|
|
95
|
+
- The record is stamped with the type's `design_schema` and
|
|
96
|
+
`validation_schema` at insert; a missing `record_permissions` merges
|
|
97
|
+
`{all: [create,read,update,delete]}`.
|
|
98
|
+
- `account_id` in the body is a **request hint**, not a field — it sets the
|
|
99
|
+
target account when permitted; otherwise `ctx.accountId`.
|
|
100
|
+
- `scope: 'platform'` types are stamped with the `spine-system` account;
|
|
101
|
+
children created via `thread_id`/`target_id`/`parent_id` inherit the
|
|
102
|
+
parent's account and scope (missing parent → 403).
|
|
103
|
+
- `created_by`/`updated_by` stamp `people.id` for human principals;
|
|
104
|
+
`types`/`link_types` have no audit columns and skip the stamp.
|
|
105
|
+
- `checkTypeSlugRateLimit` enforces per-type create policies from
|
|
106
|
+
`apps.config.rate_limits['<type_slug>_create']`.
|
|
107
|
+
|
|
108
|
+
## Soft delete
|
|
109
|
+
|
|
110
|
+
`remove` with `soft=true` (default) sets `is_active=false` + `updated_by`/
|
|
111
|
+
`updated_at` on `accounts`, `people`, `items`, `threads`, `messages`,
|
|
112
|
+
`watchers`, `item_progress`, `test_cases`. Any other entity — or
|
|
113
|
+
`soft=false` — performs a hard `DELETE`.
|
|
114
|
+
|
|
115
|
+
## Visibility projection
|
|
116
|
+
|
|
117
|
+
Principals without internal-audience roles never see `visibility:
|
|
118
|
+
'internal'` threads (filtered or 404), and internal `messages` are projected
|
|
119
|
+
to safe step summaries via `projectMessageForCustomer` — unless the parent
|
|
120
|
+
thread is `external`/`public`.
|
|
121
|
+
|
|
122
|
+
## Triggers
|
|
123
|
+
|
|
124
|
+
`create`/`update`/`remove` await `fireCreateTriggers` / `fireUpdateTriggers`
|
|
125
|
+
/ `fireDeleteTriggers` (errors logged, never fail the request). Update
|
|
126
|
+
triggers receive the previous row for field-change evaluation.
|
|
127
|
+
|
|
128
|
+
## Examples
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# List open items of a type
|
|
132
|
+
GET /api/admin-data?action=list&entity=items&type_slug=ticket&status=open&limit=25
|
|
133
|
+
|
|
134
|
+
# Get one item without lineage
|
|
135
|
+
GET /api/admin-data?action=get&entity=items&id=<uuid>&lineage=0
|
|
136
|
+
|
|
137
|
+
# Create a ticket (type by slug)
|
|
138
|
+
POST /api/admin-data?action=create
|
|
139
|
+
{"entity": "items", "type_slug": "ticket", "title": "Login broken",
|
|
140
|
+
"data": {"priority": "high"}}
|
|
141
|
+
|
|
142
|
+
# Partial update — only listed data fields are written, each needs field 'write'
|
|
143
|
+
PATCH /api/admin-data?action=update&entity=items&id=<uuid>
|
|
144
|
+
{"data": {"priority": "low"}, "status": "resolved"}
|
|
145
|
+
|
|
146
|
+
# Soft then hard delete
|
|
147
|
+
DELETE /api/admin-data?action=remove&entity=items&id=<uuid>
|
|
148
|
+
DELETE /api/admin-data?action=remove&entity=items&id=<uuid>&soft=false
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Client-side, `.framework/src/hooks/useEntityRecord.ts` shows the canonical
|
|
152
|
+
call pattern through `apiFetch` (`.framework/src/lib/api.ts`), which attaches
|
|
153
|
+
`Authorization`, `X-Account-Id`, `traceparent`, and `x-app-id`.
|
|
154
|
+
|
|
155
|
+
## Common failures
|
|
156
|
+
|
|
157
|
+
| Status | Cause |
|
|
158
|
+
|---|---|
|
|
159
|
+
| 400 | missing/invalid `entity`, missing `id`, missing `type_id`/`type_slug` on create, missing required raw-entity fields |
|
|
160
|
+
| 401 | no valid credential — see [Authentication](auth.md) |
|
|
161
|
+
| 403 | record/field permission denied; parent record not found or not accessible |
|
|
162
|
+
| 404-style | `get`/`update`/`remove` on an inaccessible row errors "Record not found or access denied" (RLS hides it) |
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: API Authentication
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/_shared/principal.ts
|
|
9
|
+
- .framework/functions/_shared/middleware.ts
|
|
10
|
+
- .framework/functions/_shared/hash-api-key.ts
|
|
11
|
+
- .framework/functions/_shared/db.ts
|
|
12
|
+
- .framework/functions/api-keys.ts
|
|
13
|
+
- .framework/migrations/004_core_rpc_and_broker.sql
|
|
14
|
+
- .framework/src/lib/api.ts
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# API Authentication
|
|
18
|
+
|
|
19
|
+
Every function wrapped by `createHandler` resolves a `Principal` from request
|
|
20
|
+
headers via `resolvePrincipal` (`.framework/functions/_shared/principal.ts`)
|
|
21
|
+
before your code runs. `createPublicHandler` resolves a principal too but
|
|
22
|
+
does not require one. `/api/*` paths proxy to Netlify functions via
|
|
23
|
+
`/.netlify/functions/:splat` (`.framework/public/_redirects`).
|
|
24
|
+
|
|
25
|
+
## Credential resolution order
|
|
26
|
+
|
|
27
|
+
`resolvePrincipal` checks headers in this order — **first match wins**:
|
|
28
|
+
|
|
29
|
+
1. **`X-Api-Key`** → machine principal. Looks up `api_keys` rows by
|
|
30
|
+
`key_prefix` (first 8 chars of the presented key) among `is_active` keys,
|
|
31
|
+
requires `expires_at` unset-or-future, verifies `key_hash` with a
|
|
32
|
+
timing-safe pbkdf2 compare (`.framework/functions/_shared/hash-api-key.ts`
|
|
33
|
+
— `pbkdf2$100000$sha256`), enforces per-key rate limit (`rate_limit`,
|
|
34
|
+
default 100 per `config.rate_limit_window_seconds`, default 60s) and a
|
|
35
|
+
per-IP ceiling (`rate_limit_hit` RPC, default 300/60s), then logs usage to
|
|
36
|
+
`api_key_usage_logs`. Scopes come from `key.scopes`; `*:*` grants
|
|
37
|
+
`system_admin`-equivalent reach. `key.machine_type` and `is_internal` ride
|
|
38
|
+
on the principal.
|
|
39
|
+
2. **`X-Internal-Secret`** → internal invocation. Compared against
|
|
40
|
+
`INTERNAL_INVOKE_SECRET` with a timing-safe hash compare. When valid,
|
|
41
|
+
`X-Cron-Id` resolves a cron principal from `schedules` (validated by the
|
|
42
|
+
`validate_schedule_creator` RPC), `X-Trigger-Id` resolves the trigger's
|
|
43
|
+
action `default_machine_principal_id` from `api_keys`, and a bare secret
|
|
44
|
+
resolves `SYSTEM_PRINCIPAL` (`*:*` scope). **Without this secret the
|
|
45
|
+
`x-cron-id`/`x-trigger-id` headers are never honored** — they exist only
|
|
46
|
+
for trusted function-to-function calls.
|
|
47
|
+
3. **`Authorization: Bearer <jwt>`** → human principal.
|
|
48
|
+
`supabase.auth.getUser` validates the JWT, `resolveInternalPersonId` maps
|
|
49
|
+
`auth.users.id` → `people.id` (auth_uid binding, then email fallback),
|
|
50
|
+
and the person's `roles.slug` becomes `principal.roles`. Results are
|
|
51
|
+
cached ~60s per token hash (max 500 entries); principals with neither
|
|
52
|
+
`account_id` nor `role_id` (mid-registration) are never cached.
|
|
53
|
+
4. **Nothing** → `ANONYMOUS_PRINCIPAL` → `createHandler` returns 401.
|
|
54
|
+
|
|
55
|
+
## Principal shape
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
interface Principal {
|
|
59
|
+
id: string // people.id, api_keys.id, 'system', or 'anonymous'
|
|
60
|
+
type: 'human' | 'machine'
|
|
61
|
+
accountId: string | null
|
|
62
|
+
roles?: string[] // role slugs (humans; *:* machines get system_admin)
|
|
63
|
+
scopes?: string[] // machine scopes, e.g. 'items:read', '*:*'
|
|
64
|
+
machineType?: 'integration' | 'service_account' | 'internal' | 'timer'
|
|
65
|
+
isInternal?: boolean
|
|
66
|
+
provenance: { sourceType: 'jwt'|'api_key'|'cron'|'trigger'|'manual'|'webhook'|'timer', ... }
|
|
67
|
+
authContext?: { jwt?: string } // transport-only — never serialize
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`getPrincipalDb(principal)` picks the request's DB client: humans with a JWT
|
|
72
|
+
get an RLS-scoped client (`getUserDb`); machines/anonymous get `adminDb`.
|
|
73
|
+
|
|
74
|
+
## Scope checking
|
|
75
|
+
|
|
76
|
+
`machineHasScope` honors exact `resource:action`, `resource:*`, and `*:*`
|
|
77
|
+
wildcards. `admin-data` create treats machine `*:*` as authorized; elsewhere
|
|
78
|
+
`isSystemAdmin` (human `system_admin` role or machine `*:*`) is the bypass.
|
|
79
|
+
`humanHasRole` matches role slugs.
|
|
80
|
+
|
|
81
|
+
## Client headers that matter
|
|
82
|
+
|
|
83
|
+
| Header | Effect |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `Authorization: Bearer <jwt>` | human auth |
|
|
86
|
+
| `X-Api-Key: <key>` | machine auth |
|
|
87
|
+
| `X-Internal-Secret: <secret>` | internal function-to-function auth (+ `X-Cron-Id`/`X-Trigger-Id`) |
|
|
88
|
+
| `X-Account-Id` | carried into `ctx` for account-scoped flows (set by `apiFetch` from `setAccountId`) |
|
|
89
|
+
| `X-App-Id` | app slug hint for app-scoped behavior (auto-set by `apiFetch` from the route prefix) |
|
|
90
|
+
| `X-Request-Id` | propagated request id (a UUID is generated when absent/invalid) |
|
|
91
|
+
| `traceparent`/`tracestate` | W3C trace context, echoed back on responses |
|
|
92
|
+
|
|
93
|
+
The browser client `apiFetch` (`.framework/src/lib/api.ts`) attaches
|
|
94
|
+
`Authorization`, `X-Account-Id`, `traceparent`, and `x-app-id`
|
|
95
|
+
automatically.
|
|
96
|
+
|
|
97
|
+
## Failure modes
|
|
98
|
+
|
|
99
|
+
| Status | Cause |
|
|
100
|
+
|---|---|
|
|
101
|
+
| 401 `Authentication required` | no credential resolved, expired/invalid JWT (`auth.getUser` fails → anonymous), or `resolvePrincipal` threw (see below) |
|
|
102
|
+
| 401 `Invalid or inactive machine principal` | unknown `key_prefix`, inactive key, expired key, or hash mismatch — thrown by `resolveMachinePrincipal` |
|
|
103
|
+
| 401 `Rate limit exceeded for this API key` / `for this address` | per-key or per-IP ceilings hit — thrown as errors, surfaced via the 500-unless-statusCode path (these errors carry no `statusCode`, so check function logs when a 500 appears on auth) |
|
|
104
|
+
| 401 `Invalid or inactive schedule: <id>` / `Invalid trigger: <id>` | `x-cron-id`/`x-trigger-id` resolution failed after a valid internal secret |
|
|
105
|
+
| 401 `Person lookup error …` / `Person not found: <id>` | JWT verified but no `people` row exists (incomplete registration or deleted person) |
|
|
106
|
+
| 403 `Access denied` | principal resolved but matched `security_blocklist` (person/account/email/IP), checked by `isBlockedRequest` — fails open when the table is absent |
|
|
107
|
+
| 400 `Invalid JSON in request body` | body was present but unparseable |
|
|
108
|
+
|
|
109
|
+
## Related
|
|
110
|
+
|
|
111
|
+
- [Errors and Envelopes](errors-and-envelopes.md) · [admin-data](admin-data.md)
|
|
112
|
+
- [API Keys](../../api-keys.md) · [Auth and API Keys](../../concepts/auth-and-api-keys.md)
|
|
113
|
+
- [Environment Variables](../env-vars.md) — `INTERNAL_INVOKE_SECRET`, `SPINE_IP_RATE_LIMIT`
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Errors and Response Envelopes
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/functions/_shared/middleware.ts
|
|
9
|
+
- .framework/functions/_shared/principal.ts
|
|
10
|
+
- .framework/functions/_shared/rate-limit.ts
|
|
11
|
+
- .framework/functions/_shared/tracing.ts
|
|
12
|
+
- .framework/src/lib/api.ts
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Errors and Response Envelopes
|
|
16
|
+
|
|
17
|
+
Every function wrapped by `createHandler` or `createPublicHandler`
|
|
18
|
+
(`.framework/functions/_shared/middleware.ts`) returns the same envelope and
|
|
19
|
+
error shape. Custom functions get this for free by returning/throwing from a
|
|
20
|
+
wrapped handler — never construct raw `{statusCode, body}` responses in app
|
|
21
|
+
code.
|
|
22
|
+
|
|
23
|
+
## Success envelope
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"data": <handler return value>,
|
|
28
|
+
"error": null,
|
|
29
|
+
"meta": { "requestId": "<uuid>", "duration": <ms> }
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
HTTP status is always `200` for a handler that returns normally — partial
|
|
34
|
+
failures the handler swallows are invisible at the status layer, so handlers
|
|
35
|
+
should throw for real failures.
|
|
36
|
+
|
|
37
|
+
## Error envelope
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{ "data": null, "error": "<message>" }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Two emitters produce it:
|
|
44
|
+
|
|
45
|
+
- `error(message, status)` — used for auth/blocklist/parse failures before
|
|
46
|
+
the handler runs.
|
|
47
|
+
- `structuredError(err, status)` — used for anything the handler throws.
|
|
48
|
+
The thrown error's `statusCode` number becomes the HTTP status; **absent
|
|
49
|
+
`statusCode` the status is `500`**, so domain errors that should surface
|
|
50
|
+
as 4xx must set `err.statusCode` (the codebase idiom is
|
|
51
|
+
`const e: any = new Error('…'); e.statusCode = 400; throw e`).
|
|
52
|
+
Whitelisted extra fields are copied onto the body when present on the
|
|
53
|
+
error: `action`, `limit`, `retry_after` (plus `retryAfter` → `retry_after`)
|
|
54
|
+
— that is how `RateLimitError` from
|
|
55
|
+
`.framework/functions/_shared/rate-limit.ts` renders a 429 with recovery
|
|
56
|
+
metadata.
|
|
57
|
+
|
|
58
|
+
## Status codes in practice
|
|
59
|
+
|
|
60
|
+
| Status | When |
|
|
61
|
+
|---|---|
|
|
62
|
+
| 200 | success; also preflight (`cors()` returns `{message:'CORS enabled'}`) |
|
|
63
|
+
| 400 | `statusCode`-tagged validation failures; unparsable JSON body (`Invalid JSON in request body`) |
|
|
64
|
+
| 401 | anonymous principal on a `createHandler` endpoint (`Authentication required`); credential resolution failures (see [Authentication](auth.md)) |
|
|
65
|
+
| 403 | `security_blocklist` hits (`Access denied`); `statusCode`-tagged permission failures (`Insufficient permissions …`) |
|
|
66
|
+
| 429 | `RateLimitError` — body carries `action`/`limit`/`retry_after` |
|
|
67
|
+
| 500 | any thrown error without `statusCode` — includes most `new Error('…')` sites |
|
|
68
|
+
|
|
69
|
+
Because untagged errors map to 500, a 500 does not always mean a crash — it
|
|
70
|
+
can be an intentional refusal (e.g. `Invalid action or method`, `Record not
|
|
71
|
+
found`) that nobody tagged. Check `error` text and function logs.
|
|
72
|
+
|
|
73
|
+
## Response headers
|
|
74
|
+
|
|
75
|
+
Set by `json()` on every response:
|
|
76
|
+
|
|
77
|
+
| Header | Value |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `Content-Type` | `application/json` |
|
|
80
|
+
| `Access-Control-Allow-Origin` | `SITE_URL` → `URL` → `*` (in that order) |
|
|
81
|
+
| `Access-Control-Allow-Headers` | `Content-Type, Authorization, X-Account-Id, X-App-Id, X-Request-ID, X-Source, X-Log-Level, traceparent, tracestate` |
|
|
82
|
+
| `Access-Control-Allow-Methods` | `GET, POST, PATCH, DELETE, OPTIONS` |
|
|
83
|
+
| `X-Request-ID` | the request id (echo of a valid `X-Request-Id` input, else generated UUID) |
|
|
84
|
+
| `traceparent` / `tracestate` | W3C trace context for the request span |
|
|
85
|
+
|
|
86
|
+
## Request context the handler receives
|
|
87
|
+
|
|
88
|
+
`ctx` (`RequestContext`) carries `principal`, `accountId`, `db` (traced,
|
|
89
|
+
principal-scoped client), `requestId`, `appId` (`X-App-Id`), `query`
|
|
90
|
+
(query-string params plus `method` filled from the HTTP method),
|
|
91
|
+
`requestPath`, lowercased `headers`, `source`/`logLevel` (from `X-Source`/
|
|
92
|
+
`X-Log-Level`), and `trace`. A `?id=` query param is copied into `body.id`
|
|
93
|
+
when the body lacks it.
|
|
94
|
+
|
|
95
|
+
## Observability side effects
|
|
96
|
+
|
|
97
|
+
Every request emits fire-and-forget spans (`emitSpan`) for
|
|
98
|
+
`request.<method>` / `request.error` / `request.blocked` /
|
|
99
|
+
`request.unauthenticated` / `request.parse_error`, and `traceDb` wraps
|
|
100
|
+
`ctx.db` calls into `db.*` spans. Telemetry failures never fail the request.
|
|
101
|
+
|
|
102
|
+
## Client handling
|
|
103
|
+
|
|
104
|
+
`apiFetch` (`.framework/src/lib/api.ts`) returns the raw `Response` — callers
|
|
105
|
+
read `response.json()` and inspect `error`. A `401` invokes the registered
|
|
106
|
+
`onUnauthorized` callback except on `/api/auth` and `/api/admin-data` paths.
|
|
107
|
+
Debug per-request tracing is opt-in via `localStorage.spine_debug_api = '1'`.
|
|
108
|
+
|
|
109
|
+
## Related
|
|
110
|
+
|
|
111
|
+
- [Authentication](auth.md) — credential resolution and its failure modes
|
|
112
|
+
- [admin-data](admin-data.md) — the largest consumer of this contract
|
|
113
|
+
- [Shared Modules](../shared-modules.md) — `middleware.ts` internals
|