spine-framework 0.4.4 → 0.5.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/install-app.ts +183 -13
- 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-app.ts +127 -1
- 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/app-manifest.ts +2 -0
- 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/_shared/permissions.ts +4 -0
- package/.framework/functions/admin-data.ts +51 -28
- package/.framework/functions/ai-agents.ts +10 -6
- package/.framework/functions/api-keys.ts +301 -122
- package/.framework/functions/apps.ts +63 -29
- 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 +149 -78
- 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 +278 -148
- 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/migrations/074_app_route_prefix_domains.sql +168 -0
- package/.framework/migrations/075_security_definer_grants.sql +157 -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/contexts/AppContext.tsx +5 -12
- package/.framework/src/contexts/AuthContext.tsx +17 -9
- package/.framework/src/hooks/useApps.ts +18 -22
- package/.framework/src/hooks/useEntityList.ts +12 -9
- package/.framework/src/hooks/useListSchema.ts +108 -58
- package/.framework/src/instructions/README.md +74 -0
- package/.framework/src/instructions/accounts.md +97 -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 +228 -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/internal/docs-contract.md +158 -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/notifications.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 +170 -0
- package/.framework/src/instructions/operations/troubleshooting.md +170 -0
- package/.framework/src/instructions/operations/upgrade.md +116 -0
- package/.framework/src/instructions/people.md +91 -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 +184 -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/frontend-sdk.md +207 -0
- package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
- package/.framework/src/instructions/reference/functions/admin-data.md +197 -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-collaboration.md +106 -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/document-your-app.md +117 -0
- package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
- package/.framework/src/instructions/tasks/register-an-app.md +137 -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 +84 -0
- package/.framework/src/instructions/triggers.md +69 -0
- package/.framework/src/instructions/types.md +93 -0
- package/.framework/src/lib/app-mount.ts +88 -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/src/pages/auth/LoginPage.tsx +3 -2
- package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
- package/.framework/templates/agent/AGENTS.md +167 -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 +79 -0
- package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
- package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
- package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
- package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -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 +394 -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/app-mount.test.ts +80 -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/security-definer-grants.test.ts +154 -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,280 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Design Schema Reference
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .framework/src/types/types.ts
|
|
9
|
+
- .framework/functions/_shared/schema-utils.ts
|
|
10
|
+
- .framework/functions/_shared/permissions.ts
|
|
11
|
+
- .framework/functions/admin-data.ts
|
|
12
|
+
- .framework/functions/types.ts
|
|
13
|
+
- .framework/cli/commands/validate-schema.ts
|
|
14
|
+
- .framework/src/components/shared/FieldRenderer.tsx
|
|
15
|
+
- .framework/src/components/app-shell/GenericListPage.tsx
|
|
16
|
+
- .framework/src/components/app-shell/GenericDetailPage.tsx
|
|
17
|
+
- .framework/src/hooks/useForm.ts
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Design Schema Reference
|
|
21
|
+
|
|
22
|
+
Exhaustive field-level spec for `public.types.design_schema` — the JSON every
|
|
23
|
+
type carries and every runtime record is stamped with at creation. The mental
|
|
24
|
+
model is in [Design Schema](../concepts/design-schema.md); this page is the
|
|
25
|
+
lookup table. The TypeScript source of truth is `DesignSchema`,
|
|
26
|
+
`FieldDefinition`, `ViewFieldConfig`, `ListView`, `DetailView`, and
|
|
27
|
+
`FunctionalityBindings` in `.framework/src/types/types.ts`.
|
|
28
|
+
|
|
29
|
+
## Top-level shape
|
|
30
|
+
|
|
31
|
+
| Key | Type | Required | Purpose |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `fields` | `Record<string, FieldDefinition>` | yes | Flat map of `snake_case` field names to definitions |
|
|
34
|
+
| `views` | `Record<string, View>` | yes for generic apps | `default_list` and `default_detail` drive the generic UI |
|
|
35
|
+
| `record_permissions` | `Record<string, string[]>` | yes | Base CRUD grant per role slug |
|
|
36
|
+
| `scope` | `"account" \| "platform"` | no (default `"account"`) | Which account owns records of this type at create |
|
|
37
|
+
| `functionality` | `FunctionalityBindings` or extensions | no | Automation bindings; `null` until a binding is confirmed live |
|
|
38
|
+
| `display_field` | `string` | no | Field used for record titles in detail pages |
|
|
39
|
+
|
|
40
|
+
`validate-schema` enforces: `fields` non-empty; every field has `data_type`,
|
|
41
|
+
`label`, `required`; `record_permissions` grants at least `system-admin` or
|
|
42
|
+
`system_admin`; `scope` is `account` or `platform`
|
|
43
|
+
(`.framework/cli/commands/validate-schema.ts`).
|
|
44
|
+
|
|
45
|
+
## `fields.<name>` — FieldDefinition
|
|
46
|
+
|
|
47
|
+
| Key | Type | Notes |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `data_type` | enum, see below | required |
|
|
50
|
+
| `label` | string | required |
|
|
51
|
+
| `required` | boolean | required; enforced client-side by `useForm` (`.framework/src/hooks/useForm.ts`), propagated into `validation_schema` |
|
|
52
|
+
| `system` | boolean | `true` → top-level record column; absent/`false` → `record.data.<name>` |
|
|
53
|
+
| `placeholder` | string | input hint |
|
|
54
|
+
| `description` | string | help text |
|
|
55
|
+
| `name` | string | optional field alias |
|
|
56
|
+
| `rows` | number | textarea height hint |
|
|
57
|
+
| `min`, `max`, `step` | number | range input bounds |
|
|
58
|
+
| `readonly`, `disabled` | boolean | form-state hints |
|
|
59
|
+
| `validation` | object | constraint keys, see below |
|
|
60
|
+
| `options` | `(string \| {value, label})[]` | required for `select`, `multiselect`, `radio` |
|
|
61
|
+
| `permissions` | `Record<string, string[]>` | per-role field overrides, `read`/`write` only |
|
|
62
|
+
|
|
63
|
+
### `data_type` values
|
|
64
|
+
|
|
65
|
+
The authoritative dispatch is `sanitizeFieldData` in
|
|
66
|
+
`.framework/functions/_shared/schema-utils.ts`. Unknown types are **not
|
|
67
|
+
rejected** — the value passes through unsanitized and unvalidated.
|
|
68
|
+
|
|
69
|
+
**Where the sanitizer runs.** `sanitizeFieldData` executes only on the
|
|
70
|
+
`admin-data` **update** path, only for `data.*` (non-system) fields, and only
|
|
71
|
+
for **non-system-admin** principals. `create` performs no field sanitization —
|
|
72
|
+
the request body is inserted as-is — and system columns (`system: true`,
|
|
73
|
+
including `select` fields like `status`) are never option-validated
|
|
74
|
+
server-side. `required` is enforced client-side by `useForm` only. So
|
|
75
|
+
"sanitizer behavior throws" below means: on a `data.*` update by a non-admin
|
|
76
|
+
caller. The trust boundary is the client + field permissions, not
|
|
77
|
+
`validation_schema` — treat API writes as unvalidated input.
|
|
78
|
+
|
|
79
|
+
| `data_type` | Stored as | Sanitizer behavior |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `text` | string | trim, strip control chars; `minLength` throws, `maxLength` truncates, `pattern` throws |
|
|
82
|
+
| `textarea` | string | trim, strip control chars; `minLength`/`maxLength` |
|
|
83
|
+
| `rich_text` | HTML string | tag allowlist (`p br strong em u ol ul li a h1–h6`), strips `<script>` and `on*=` handlers; `minLength`/`maxLength` |
|
|
84
|
+
| `email` | string | lowercase + trim; must match `^[^\s@]+@[^\s@]+\.[^\s@]+$` |
|
|
85
|
+
| `phone` | string | strips non-`0-9+` chars; optional `pattern` |
|
|
86
|
+
| `url` | string | parses URL; `http:`/`https:` only; normalized via `URL.toString()` |
|
|
87
|
+
| `number` | number | `Number()` coercion; `min`/`max` throw; `step` snaps down to step |
|
|
88
|
+
| `currency` | number | `Number()` coercion rounded to 2 decimals; `min`/`max` throw |
|
|
89
|
+
| `range` | number | same as `number` |
|
|
90
|
+
| `date` | `"YYYY-MM-DD"` | parses any `Date()` input, stores ISO date; `min`/`max` bounds |
|
|
91
|
+
| `datetime` | ISO string | parses any `Date()` input, stores `toISOString()`; `min`/`max` bounds |
|
|
92
|
+
| `boolean` | boolean | `true`/`false` pass through; strings `"true" "1" "yes" "on"` → `true`, `"false" "0" "no" "off"` → `false`; anything else throws |
|
|
93
|
+
| `checkbox` | boolean | alias of `boolean` |
|
|
94
|
+
| `select` | string | trim; when `options` are declared the value must match an option (`value` for `{label,value}` entries) or it throws |
|
|
95
|
+
| `multiselect` | string[] | accepts an array or comma-separated string; dedupes; each element must match an option; `validation.max` truncates the list |
|
|
96
|
+
| `radio` | string | same sanitizer as `select` |
|
|
97
|
+
| `color` | string | must match `#rgb`/`#rrggbb`; normalized to 6-digit uppercase hex |
|
|
98
|
+
| `file` | object | must be an object with `.name`; `maxSize` and `allowedTypes` (MIME) enforced; filename sanitized to `[a-zA-Z0-9.-]` |
|
|
99
|
+
| `image` | object | same sanitizer as `file` (`maxWidth`/`maxHeight` are declared but not enforced) |
|
|
100
|
+
| `json` | any JSON value | string input is `JSON.parse`d; rejects script tags, `javascript:`, `eval(`/`function(`, `on*=` handlers, and `__proto__` keys; arbitrary nesting allowed under `data.<name>` |
|
|
101
|
+
| `reference` | string (uuid) | must match the UUID regex; `validation.reference_kind`/`reference_type` describe the target |
|
|
102
|
+
| `address` | object | must be an object; string values are text-sanitized |
|
|
103
|
+
|
|
104
|
+
`formatFieldData` (display formatting) covers only `json`, `date`, `datetime`,
|
|
105
|
+
`currency`, `phone`, `url`, `reference`, `address`, `multiselect`, `boolean` —
|
|
106
|
+
all other types render raw.
|
|
107
|
+
|
|
108
|
+
### `validation.*` keys
|
|
109
|
+
|
|
110
|
+
Declared on `FieldDefinition.validation`; copied verbatim into the generated
|
|
111
|
+
`validation_schema` by `generateValidationSchema`
|
|
112
|
+
(`.framework/functions/_shared/schema-utils.ts`).
|
|
113
|
+
|
|
114
|
+
| Key | Applies to | Effect |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `pattern` | `text`, `phone` | regex must match or the write throws |
|
|
117
|
+
| `minLength`, `maxLength` | `text`, `textarea`, `rich_text` | min throws; max truncates |
|
|
118
|
+
| `min`, `max` | `number`, `currency`, `range`, `date`, `datetime` | bounds; out-of-range throws |
|
|
119
|
+
| `step` | `number`, `range` | value snapped down to step multiple |
|
|
120
|
+
| `integer` | `number` | declared; not enforced by the server sanitizer |
|
|
121
|
+
| `precision` | `number` | declared decimal precision; not enforced server-side |
|
|
122
|
+
| `maxSize` | `file`, `image` | byte-size limit, enforced server-side |
|
|
123
|
+
| `allowedTypes` | `file`, `image` | MIME allowlist, enforced server-side |
|
|
124
|
+
| `maxWidth`, `maxHeight` | `image` | declared; not enforced server-side |
|
|
125
|
+
| `max` | `multiselect` | truncates the list to `max` entries |
|
|
126
|
+
| `currency_code` | `currency` | display currency (e.g. `USD`) |
|
|
127
|
+
| `reference_kind` | `reference` | runtime table of the target (`item`, `person`, `account`, …) |
|
|
128
|
+
| `reference_type` | `reference` | type slug of the target record |
|
|
129
|
+
|
|
130
|
+
### `options` formats
|
|
131
|
+
|
|
132
|
+
Required for `select`/`multiselect`/`radio`; validated non-empty by
|
|
133
|
+
`validate-schema`. Both forms accepted:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
"options": ["todo", "done"]
|
|
137
|
+
"options": [{ "label": "Bug", "value": "bug" }]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `permissions` (field-level overrides)
|
|
141
|
+
|
|
142
|
+
`record_permissions` is the **base, most restrictive** grant: when a role can
|
|
143
|
+
`read`/`update` a record, every field defaults to `read`/`write` unless the
|
|
144
|
+
field declares a `permissions` entry for that role. Field-level actions are
|
|
145
|
+
`read` and `write` only; `create`/`delete` are record-level
|
|
146
|
+
(`.framework/functions/_shared/permissions.ts`, `resolveFirstSurfacePermissions`
|
|
147
|
+
step 5–6). Use field permissions to **loosen** one field beyond the record
|
|
148
|
+
grant or to re-restrict a `system: true` field.
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
"fields": {
|
|
152
|
+
"cost": {
|
|
153
|
+
"data_type": "currency", "label": "Cost", "required": false,
|
|
154
|
+
"permissions": { "member": ["read"], "finance": ["read", "write"] }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `system` flag semantics
|
|
160
|
+
|
|
161
|
+
`system: true` fields are stored as top-level record columns (`record.title`),
|
|
162
|
+
all others under `record.data.<name>` — see
|
|
163
|
+
[Runtime Records](../concepts/runtime-records.md). Writers split payloads the
|
|
164
|
+
same way (`useEntityRecord` / the update path). Conventionally `title` is
|
|
165
|
+
system; flag `system: true` fields that humans must not edit with a
|
|
166
|
+
`permissions` override restricting `write` to `system_admin`.
|
|
167
|
+
|
|
168
|
+
## `views`
|
|
169
|
+
|
|
170
|
+
`views` is a map of view names to `ListView | DetailView`. Generic apps
|
|
171
|
+
(`apps.renderer = "generic"`) need `default_list` and `default_detail`; a
|
|
172
|
+
custom app may omit views but loses the generic admin/runtime fallback.
|
|
173
|
+
|
|
174
|
+
### `default_list` — ListView
|
|
175
|
+
|
|
176
|
+
| Key | Type | Purpose |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `type` | `"list"` | required discriminator |
|
|
179
|
+
| `display` | `"table" \| "card" \| "board"` | layout in `GenericListPage` |
|
|
180
|
+
| `label` | string | view title |
|
|
181
|
+
| `fields` | `Record<string, ViewFieldConfig>` | column map, keyed by field name (may include `created_at`/`updated_at`). Non-system fields resolve automatically — key `priority` reads `record.data.priority`; the explicit `data.priority` form also works and filters on `data.<field>` are translated server-side |
|
|
182
|
+
| `default_sort` | `{field, direction: "asc"\|"desc"}` | initial ordering |
|
|
183
|
+
| `filters` | `(string \| EntityFilter)[]` | filter affordances — a bare field-name string is normalized from the field def (`select` → enum with its option values, `boolean` → boolean, else search); or pass full `{key, label, type: "search"\|"enum"\|"boolean", options?: string[]}` objects |
|
|
184
|
+
| `stats` | `{title, type: "count"\|"filter_count", icon?, color?, filter?}[]` | stat cards above the list; `filter` is a `{<field>: <value>}` map matched per row (e.g. `{"status": "todo"}`) |
|
|
185
|
+
| `group_by` | string | field to group rows by (board display) |
|
|
186
|
+
|
|
187
|
+
### `default_detail` — DetailView
|
|
188
|
+
|
|
189
|
+
| Key | Type | Purpose |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| `type` | `"detail"` | required discriminator |
|
|
192
|
+
| `label` | string | page title |
|
|
193
|
+
| `sections` | `DetailViewSection[]` | ordered sections of `{title, fields: Record<string, ViewFieldConfig>}` |
|
|
194
|
+
|
|
195
|
+
### `ViewFieldConfig` — `display_type`
|
|
196
|
+
|
|
197
|
+
Each view field is `{display_type, sortable?, searchable?}`. Implemented
|
|
198
|
+
`display_type` values — enforced by `validate-schema` and rendered by
|
|
199
|
+
`.framework/src/components/shared/FieldRenderer.tsx`:
|
|
200
|
+
|
|
201
|
+
| `display_type` | Context |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `text` | read-only text (list/detail) |
|
|
204
|
+
| `badge` | status pill (list) |
|
|
205
|
+
| `timestamp` | formatted date/time (list/detail) |
|
|
206
|
+
| `currency` | formatted amount |
|
|
207
|
+
| `number` | formatted number |
|
|
208
|
+
| `input` | single-line edit input (detail) |
|
|
209
|
+
| `textarea` | multi-line edit |
|
|
210
|
+
| `rich_text` | rich-text editor |
|
|
211
|
+
| `select`, `multiselect`, `radio` | option pickers |
|
|
212
|
+
| `checkbox`, `switch` | boolean toggles |
|
|
213
|
+
| `date_picker`, `datetime_picker` | date/time pickers |
|
|
214
|
+
| `color_picker` | color input |
|
|
215
|
+
| `file_upload`, `image_upload` | upload widgets |
|
|
216
|
+
| `range_slider` | numeric slider |
|
|
217
|
+
|
|
218
|
+
Reserved in `ViewFieldConfig` but not yet implemented by `FieldRenderer`:
|
|
219
|
+
`rating`, `autocomplete`, `address_form`, `reference_picker` (the type union
|
|
220
|
+
admits them in `.framework/src/types/types.ts`; treat as forward-compatible
|
|
221
|
+
placeholders).
|
|
222
|
+
|
|
223
|
+
## `record_permissions`
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
"record_permissions": {
|
|
227
|
+
"system_admin": ["create", "read", "update", "delete"],
|
|
228
|
+
"admin": ["create", "read", "update"],
|
|
229
|
+
"member": ["read"]
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
- Actions are `create`, `read`, `update`, `delete` — evaluated per request
|
|
234
|
+
against the caller's role slug.
|
|
235
|
+
- `"all"` is a wildcard role granted to every authenticated principal that
|
|
236
|
+
passed RLS; always evaluated when present
|
|
237
|
+
(`.framework/functions/_shared/permissions.ts`).
|
|
238
|
+
- Evaluation happens against the schema **stamped on the record**, not the
|
|
239
|
+
type row — records created before a schema edit keep the old grant.
|
|
240
|
+
- If a type's `design_schema` omits `record_permissions`, `admin-data` create
|
|
241
|
+
merges a permissive default `{all: [create,read,update,delete]}` before
|
|
242
|
+
stamping (`.framework/functions/admin-data.ts`, `PERMISSIONS_ALL`). An
|
|
243
|
+
explicit empty object denies everything downstream — always author
|
|
244
|
+
deliberately.
|
|
245
|
+
|
|
246
|
+
## `scope`
|
|
247
|
+
|
|
248
|
+
Read at create time as `designSchema.scope ?? 'account'`
|
|
249
|
+
(`.framework/functions/admin-data.ts`). `"platform"` stamps new records with
|
|
250
|
+
the `spine-system` account id; records created against a parent
|
|
251
|
+
(`thread_id`, `target_id`, `parent_id`) inherit the parent's stamped scope.
|
|
252
|
+
See [Scope and Accounts](../concepts/scope-and-accounts.md).
|
|
253
|
+
|
|
254
|
+
## `functionality`
|
|
255
|
+
|
|
256
|
+
`FunctionalityBindings` (`.framework/src/types/types.ts`) declares optional
|
|
257
|
+
automation:
|
|
258
|
+
|
|
259
|
+
| Binding | Shape |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `pipelines[]` | `{pipeline_id, trigger: manual\|on_create\|on_update\|on_field_change\|on_delete\|scheduled, field?, condition?, roles[]}` |
|
|
262
|
+
| `ai_agents[]` | `{agent_id, capabilities: (read\|summarize\|suggest\|update)[], trigger, roles[]}` |
|
|
263
|
+
| `embeddings[]` | `{slug, fields[], model, vector_column, trigger}` |
|
|
264
|
+
| `integrations[]` | `{integration_id, sync: bidirectional\|inbound\|outbound, field_map, trigger}` |
|
|
265
|
+
| `constraints[]` | `{type: unique\|conditional_required\|immutable, fields?/field?, condition?, message, after?: create\|update}` |
|
|
266
|
+
|
|
267
|
+
`admin-data` also reads `functionality.lineage`
|
|
268
|
+
(`{parent_fields: string[], link_type: string, children: {[slug]: field}}`)
|
|
269
|
+
on `items` get/list to attach `parents`/`children` — declared per type in the
|
|
270
|
+
seed, interpreted by `attachLineage` in `.framework/functions/admin-data.ts`.
|
|
271
|
+
Set `functionality` to `null` until the binding you need is confirmed live.
|
|
272
|
+
|
|
273
|
+
## `validation_schema` (generated, never authored)
|
|
274
|
+
|
|
275
|
+
`generateValidationSchema` derives `{fields: {name: {data_type, required,
|
|
276
|
+
...validation, options, reference_kind?, reference_type?}}}` from
|
|
277
|
+
`design_schema.fields` on every type create and on every update that touches
|
|
278
|
+
`design_schema` (`.framework/functions/types.ts`,
|
|
279
|
+
`.framework/cli/commands/install-app.ts` seed path). Never hand-edit — change
|
|
280
|
+
`design_schema` and let the writer regenerate it.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Environment Variables
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: f69c37d
|
|
6
|
+
verified_on: 2026-10-30
|
|
7
|
+
source_files:
|
|
8
|
+
- .env.example
|
|
9
|
+
- .framework/functions/_shared/principal.ts
|
|
10
|
+
- .framework/functions/_shared/db.ts
|
|
11
|
+
- .framework/functions/_shared/middleware.ts
|
|
12
|
+
- .framework/functions/_shared/trigger-engine.ts
|
|
13
|
+
- .framework/functions/_shared/resolve-credentials.ts
|
|
14
|
+
- .framework/functions/system-cron.ts
|
|
15
|
+
- .framework/functions/pipeline-runner-background.ts
|
|
16
|
+
- .framework/functions/security.ts
|
|
17
|
+
- .framework/functions/invites.ts
|
|
18
|
+
- .framework/functions/adapters/resend-outbound.ts
|
|
19
|
+
- .framework/cli/context.ts
|
|
20
|
+
- .framework/cli/commands/migrate.ts
|
|
21
|
+
- .framework/cli/commands/install-app.ts
|
|
22
|
+
- .framework/src/lib/supabase.ts
|
|
23
|
+
- .framework/src/App.tsx
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Environment Variables
|
|
27
|
+
|
|
28
|
+
Every variable read across `.framework/` — where it's read, whether it's
|
|
29
|
+
required, and whether it is browser-exposed. `VITE_*` vars are compiled into
|
|
30
|
+
the frontend bundle and are **public by definition**; all others are
|
|
31
|
+
server-only (Netlify functions, CLI, MCP tooling). `.env` at the project root
|
|
32
|
+
is preloaded by the CLI and dev tooling.
|
|
33
|
+
|
|
34
|
+
> **Security rule:** never set `VITE_SUPABASE_SERVICE_ROLE_KEY` or put any
|
|
35
|
+
> secret behind a `VITE_*` name. The service-role key bypasses RLS entirely;
|
|
36
|
+
> shipping it to the browser hands full database access to every visitor.
|
|
37
|
+
> Only `VITE_SUPABASE_ANON_KEY` belongs in the client bundle.
|
|
38
|
+
|
|
39
|
+
## Required — server
|
|
40
|
+
|
|
41
|
+
| Variable | Read by | Purpose |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `SUPABASE_URL` | `.framework/functions/_shared/db.ts`, `.framework/functions/_shared/principal.ts`, CLI (`context.ts`, `install-app.ts`, `migrate.ts`, `status.ts`, `system.ts`) | Supabase project URL for service + user clients |
|
|
44
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | `.framework/functions/_shared/db.ts` (`adminDb`), `.framework/cli/context.ts`, `install-app.ts`, `status.ts` | Service-role client; bypasses RLS. **Server-only** |
|
|
45
|
+
| `SUPABASE_ANON_KEY` | `.framework/functions/_shared/principal.ts`, `.framework/functions/_shared/db.ts` (`getUserDb`) | JWT verification and user-scoped PostgREST clients inside functions |
|
|
46
|
+
|
|
47
|
+
Missing `SUPABASE_URL`/`SUPABASE_SERVICE_ROLE_KEY` fails CLI commands that
|
|
48
|
+
touch the DB and leaves `adminDb` on placeholder credentials — function
|
|
49
|
+
calls then fail at query time.
|
|
50
|
+
|
|
51
|
+
> **Key naming:** all three values are under Supabase Dashboard → Project
|
|
52
|
+
> Settings → API Keys (older dashboards: Settings → API / Data API).
|
|
53
|
+
> Current dashboards issue the anon key as a **Publishable key**
|
|
54
|
+
> (`sb_publishable_…`) and the service-role key as a **Secret key**
|
|
55
|
+
> (`sb_secret_…`) — set `*_ANON_KEY` / `SUPABASE_SERVICE_ROLE_KEY` to them
|
|
56
|
+
> directly; they work everywhere the legacy JWTs did.
|
|
57
|
+
|
|
58
|
+
## Required for specific features — server
|
|
59
|
+
|
|
60
|
+
| Variable | Read by | Required for |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `SUPABASE_DB_PASSWORD` | `.framework/cli/commands/migrate.ts`, `install-app.ts` | Direct Postgres connection for `migrate`/`install-app` (from Supabase Dashboard → Database) |
|
|
63
|
+
| `SUPABASE_DB_URL` | `.framework/cli/commands/migrate.ts` | Optional full connection string overriding host/user/port — use the **Session Pooler** (`postgres://postgres.<ref>:<pw>@aws-0-<region>.pooler.supabase.com:5432/postgres`) when `db.<ref>.supabase.co:5432` is unreachable (direct connections are IPv6-only on most Supabase projects) |
|
|
64
|
+
| `INTERNAL_INVOKE_SECRET` | `.framework/functions/_shared/principal.ts` (`isInternalInvokeAuthorized`), `.framework/functions/_shared/trigger-engine.ts`, `.framework/functions/pipeline-runner-background.ts` | Authenticates function-to-function invocation via `x-internal-secret`. **Required for trigger dispatch** — without it, `dispatchTriggerPipeline` cannot invoke `pipeline-runner-background` and `x-cron-id`/`x-trigger-id`/`x-internal-secret` headers are never honored. Generate with `openssl rand -hex 32` |
|
|
65
|
+
| `SCHEDULER_API_KEY` | `.framework/functions/system-cron.ts` | `?api_key=`/`?scheduler_key=` on the scheduler endpoint; unset disables key-gated scheduling |
|
|
66
|
+
| `APP_BASE_URL` | `.framework/functions/invites.ts` | Base URL for invite links, auth redirects, and the allowlist for client-supplied `base_url` params |
|
|
67
|
+
| `RESEND_API_KEY` | resolved via `env_var` credentials on the `transactional-email`/`resend` integration (`.framework/functions/_shared/resolve-credentials.ts`) | Outbound email (invites, notification stages). Unset → invites fall back to copyable links, email stages skip |
|
|
68
|
+
| `EMAIL_FROM` | `.framework/functions/adapters/resend-outbound.ts` | Sender identity for outbound email |
|
|
69
|
+
| `OPENAI_API_KEY` | resolved via `env_var` credentials on the `openai` integration; `.framework/functions/_shared/agent-runner.ts` mocks responses when absent | LLM chat/embeddings for agents, pipelines, KB |
|
|
70
|
+
| `LLM_DEFAULT_MODEL` | `.framework/functions/_shared/agent-runner.ts`, `.framework/functions/embeddings.ts` | Default model slug when prompts don't pin one |
|
|
71
|
+
| `NETLIFY_ACCESS_TOKEN`, `NETLIFY_SITE_ID` | `.framework/functions/security.ts` | Push banned IPs to Netlify edge Firewall rules; unset skips the edge layer |
|
|
72
|
+
|
|
73
|
+
Integration credentials generally use `source: 'env_var'` +
|
|
74
|
+
`value: '<VAR_NAME>'` in `integrations.config.credentials` —
|
|
75
|
+
`resolveCredentials` reads `process.env[value]` server-side
|
|
76
|
+
(`.framework/functions/_shared/resolve-credentials.ts`). Any env var name is
|
|
77
|
+
permitted there.
|
|
78
|
+
|
|
79
|
+
## Optional — server behavior tuning
|
|
80
|
+
|
|
81
|
+
| Variable | Read by | Default |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `SITE_URL` | `.framework/functions/_shared/middleware.ts` | `Access-Control-Allow-Origin` header; falls back to Netlify's `URL`, then `*` |
|
|
84
|
+
| `URL` | `.framework/functions/_shared/middleware.ts`, `.framework/mcp/` | Netlify auto-set site URL; CORS and MCP base-URL fallback |
|
|
85
|
+
| `ALLOWED_ORIGINS` | `.framework/functions/invites.ts` | extra CORS origins for invite endpoints |
|
|
86
|
+
| `SPINE_IP_RATE_LIMIT` | `.framework/functions/_shared/principal.ts` | per-IP request ceiling for API-key auth; default `300`/60s |
|
|
87
|
+
| `REPO_PATH` | `.framework/functions/_shared/app-manifest.ts`, `.framework/mcp/` | project root for manifest discovery; defaults to `process.cwd()` |
|
|
88
|
+
| `DB_SCHEMA` | `.framework/functions/_shared/db.ts` | PostgREST schema; default `public` (multi-instance installs only) |
|
|
89
|
+
| `EMAIL_WEBHOOK_URL` | outbound adapters | optional inbound-email relay endpoint |
|
|
90
|
+
|
|
91
|
+
## Client-exposed (`VITE_*`, browser bundle)
|
|
92
|
+
|
|
93
|
+
| Variable | Read by | Purpose |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `VITE_SUPABASE_URL` | `.framework/src/lib/supabase.ts` | Supabase URL for the browser client — required |
|
|
96
|
+
| `VITE_SUPABASE_ANON_KEY` | `.framework/src/lib/supabase.ts` | anon key for the browser client — required, RLS-scoped, safe |
|
|
97
|
+
| `VITE_ENVIRONMENT` | `.framework/src/contexts/AppContext.tsx`, `.framework/src/hooks/useApps.ts` | environment badge (`dev`/`staging`/`prod`) |
|
|
98
|
+
| `VITE_SETUP_MODE` | `.framework/src/App.tsx` | setup-mode UI flag |
|
|
99
|
+
| `VITE_TURNSTILE_SITE_KEY` | `.framework/src/components/auth/TurnstileWidget.tsx` | Cloudflare Turnstile site key on auth pages — public by design |
|
|
100
|
+
| `VITE_APP_NAME` | `.env.example` (branding) | app name for UI/localStorage/webhook headers — prefer `branding.app_name` in `spine.config.json` |
|
|
101
|
+
| `VITE_DB_SCHEMA` | `.env.example` | deprecated multi-instance knob; leave unset |
|
|
102
|
+
|
|
103
|
+
## CLI / local tooling
|
|
104
|
+
|
|
105
|
+
| Variable | Read by | Purpose |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `SPINE_CLI_API_KEY` | `.framework/cli/context.ts` | API-key auth for remote CLI commands (`items`, `pipelines`, `agents`, `system`) |
|
|
108
|
+
| `SPINE_CLI_JWT` | `.framework/cli/context.ts` | JWT auth alternative for the same commands |
|
|
109
|
+
| `SPINE_CLI_ACCOUNT_ID` | `.framework/cli/context.ts` | default account scope; `--account` overrides |
|
|
110
|
+
| `SPINE_CLI_DEBUG` | `dev.ts`, `init.ts`, `install-app.ts`, `migrate.ts`, `upgrade.ts` | verbose CLI diagnostics |
|
|
111
|
+
| `SPINE_URL`, `SPINE_API_KEY`, `SPINE_FRAMEWORK_API_KEY` | `.framework/mcp/` | MCP server endpoint + key |
|
|
112
|
+
| `OPENAI_BASE_URL`, `LLM_API_KEY`, `OPENAI_API_KEY` | `.framework/mcp/` | MCP-side LLM provider config |
|
|
113
|
+
|
|
114
|
+
## Consumer-side conventions
|
|
115
|
+
|
|
116
|
+
Not read by framework runtime code — a convention for agent/tooling
|
|
117
|
+
integrations so the same credential location works everywhere:
|
|
118
|
+
|
|
119
|
+
| Variable | Read by | Purpose |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `SPINE_API_KEY` | agent tooling (`.framework/mcp/` honors it too) | the `sk_…` key an agent sends as `x-api-key` on `/api/*` requests; the user creates a scoped key in Admin → Configs → API Keys |
|
|
122
|
+
|
|
123
|
+
## Test-only
|
|
124
|
+
|
|
125
|
+
`SPINE_DEV_URL`, `SPINE_DEV_JWT`, `SPINE_TEST_API_KEY`, `SPINE_TEST_ACCOUNT_ID`,
|
|
126
|
+
`ACCOUNT_ID`, `SERVICE_KEY` — used by `.framework/tests/` fixtures and
|
|
127
|
+
integration suites; never required at runtime.
|
|
128
|
+
|
|
129
|
+
## Related
|
|
130
|
+
|
|
131
|
+
- `.env.example` — annotated starter file
|
|
132
|
+
- [Authentication](api/auth.md) — how `x-api-key`/`x-internal-secret`/Bearer resolve
|
|
133
|
+
- [Shared Modules](shared-modules.md)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Frontend SDK Reference (@core)
|
|
3
|
+
audience: developers, agents
|
|
4
|
+
kind: reference
|
|
5
|
+
verified_against: bc17374
|
|
6
|
+
verified_on: 2026-11-02
|
|
7
|
+
source_files:
|
|
8
|
+
- config/vite.config.ts
|
|
9
|
+
- config/tsconfig.json
|
|
10
|
+
- .framework/src/lib/api.ts
|
|
11
|
+
- .framework/src/lib/supabase.ts
|
|
12
|
+
- .framework/src/lib/utils.ts
|
|
13
|
+
- .framework/src/lib/registration.ts
|
|
14
|
+
- .framework/src/lib/branding.ts
|
|
15
|
+
- .framework/src/hooks/useAppPath.ts
|
|
16
|
+
- .framework/src/hooks/useApi.ts
|
|
17
|
+
- .framework/src/hooks/useApps.ts
|
|
18
|
+
- .framework/src/contexts/AuthContext.tsx
|
|
19
|
+
- .framework/src/contexts/AppContext.tsx
|
|
20
|
+
- .framework/src/components/ui/
|
|
21
|
+
- .framework/src/components/shared/
|
|
22
|
+
- .framework/src/components/kb/
|
|
23
|
+
- .framework/src/components/layout/
|
|
24
|
+
- .framework/src/types/types.ts
|
|
25
|
+
- .framework/src/types/auth.ts
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Frontend SDK Reference (`@core`)
|
|
29
|
+
|
|
30
|
+
Custom apps write frontend code against the framework's `@core` import
|
|
31
|
+
surface — the same tree as `.framework/src`, resolved through the bundled
|
|
32
|
+
`.assembled/src`. This page is the contract: what each module exports, what
|
|
33
|
+
it is for, and when to reach for it. Ordered by how often real apps use it.
|
|
34
|
+
|
|
35
|
+
## Import aliases
|
|
36
|
+
|
|
37
|
+
Defined in `config/vite.config.ts` (build/dev) and `config/tsconfig.json`
|
|
38
|
+
(types):
|
|
39
|
+
|
|
40
|
+
| Alias | Resolves to | Use |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `@core/*` | `.assembled/src/*` (types: `.framework/src/*`) | **Frontend core.** All imports below. |
|
|
43
|
+
| `@/*` | `.assembled/src/*` | Same tree as `@core` — prefer `@core` in custom apps for clarity. |
|
|
44
|
+
| `@custom/*` | `custom/apps/*` | Cross-app imports; rare. |
|
|
45
|
+
| `@shared/*` | `.framework/functions/_shared/*` | **Server-side only** (Netlify/Deno functions). Never import from a React page — it pulls server-only deps into the frontend bundle. |
|
|
46
|
+
|
|
47
|
+
The loader mounts custom apps via `import.meta.glob('../../../custom/apps/*/index.tsx')`
|
|
48
|
+
— only `index.tsx` is the entry point; everything else is imported from it.
|
|
49
|
+
|
|
50
|
+
## 1. `@core/lib/api` — HTTP client (use this for all API calls)
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { apiFetch } from '@core/lib/api'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Export | Contract |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `apiFetch(path, options?) → Promise<Response>` | Authenticated `fetch`. Auto-attaches `Authorization: Bearer <supabase session>` and `X-Account-Id` when set. Adds a `traceparent` header for request tracing. Derives an `x-app-id` hint from the URL's first segment (server validates it against installed apps). Returns the raw `Response` — check `.ok`, then `.json()`. Strips bogus `Bearer null/undefined` headers you pass. On `401` (except `/api/auth` and `/api/admin-data` paths) it fires the registered unauthorized callback. |
|
|
59
|
+
| `setAccountId(id \| null)` / `getAccountId()` | Set/read the account ID sent as `X-Account-Id` on subsequent calls. AuthContext sets this on login; apps rarely touch it. |
|
|
60
|
+
| `getAuthHeaders() → Promise<Record<string,string>>` | The Bearer + `X-Account-Id` header pair, if you need raw headers (e.g. non-fetch transport). |
|
|
61
|
+
| `onUnauthorized(cb) → unsubscribe` | Register a global 401 handler (framework registers the logout redirect). One callback at a time. |
|
|
62
|
+
|
|
63
|
+
Debug: `localStorage.spine_debug_api = '1'` logs `X-Request-ID` + traceparent per call.
|
|
64
|
+
|
|
65
|
+
**Do not** call `fetch()` directly against `/api/*` — you'll lose auth,
|
|
66
|
+
account scoping, and trace headers. For runtime record CRUD, `apiFetch` +
|
|
67
|
+
`/api/admin-data` is the only path (see [admin-data](api/admin-data.md)).
|
|
68
|
+
|
|
69
|
+
## 2. `@core/hooks/useAppPath` — mount-agnostic navigation (required)
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { useAppPath } from '@core/hooks/useAppPath'
|
|
73
|
+
const appPath = useAppPath()
|
|
74
|
+
appPath('/tickets') // → '/my-app/tickets' (or '/tickets' when route_prefix === '/')
|
|
75
|
+
appPath('/') // → '/my-app' (or '/')
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Returns a function that prepends the current app's `route_prefix` (trailing
|
|
79
|
+
slash stripped; `'/'` treated as empty). **All app-internal links and
|
|
80
|
+
`navigate()` calls must go through it** — hardcoding `/dashboard` escapes the
|
|
81
|
+
app when it is directory-mounted, and breaks differently when it is
|
|
82
|
+
root-mounted. Alternatively use relative `path`/`to` inside the app's own
|
|
83
|
+
`<Routes>`.
|
|
84
|
+
|
|
85
|
+
## 3. `@core/components/ui/*` — the component kit
|
|
86
|
+
|
|
87
|
+
A shadcn-style kit (~60 components) plus framework-level widgets. Import
|
|
88
|
+
per component: `import { Button } from '@core/components/ui/button'`.
|
|
89
|
+
|
|
90
|
+
**Framework widgets** (higher-level, Spine-aware):
|
|
91
|
+
|
|
92
|
+
| Component | Purpose |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `DataTable` | Sortable/paginated table |
|
|
95
|
+
| `ItemListView`, `ItemCard` | Record list/card rendering |
|
|
96
|
+
| `Form`, `field`, `input-group` | Form scaffolding (react-hook-form aligned) |
|
|
97
|
+
| `Modal`, `dialog`, `alert-dialog`, `drawer`, `sheet` | Overlays |
|
|
98
|
+
| `RichTextEditor` | Rich text input |
|
|
99
|
+
| `LoadingSpinner`, `spinner`, `skeleton` | Loading states |
|
|
100
|
+
| `chart` | Chart wrappers (recharts) |
|
|
101
|
+
| `sonner` | Toast notifications |
|
|
102
|
+
|
|
103
|
+
**Plain kit** (pick what you need): `accordion`, `alert`, `aspect-ratio`,
|
|
104
|
+
`avatar`, `badge`, `breadcrumb`, `button`, `button-group`, `calendar`,
|
|
105
|
+
`card`, `carousel`, `checkbox`, `collapsible`, `combobox`, `command`,
|
|
106
|
+
`context-menu`, `direction`, `dropdown-menu`, `empty`, `hover-card`,
|
|
107
|
+
`input`, `input-otp`, `item`, `kbd`, `label`, `menubar`, `native-select`,
|
|
108
|
+
`navigation-menu`, `pagination`, `popover`, `progress`, `radio-group`,
|
|
109
|
+
`resizable`, `scroll-area`, `select`, `separator`, `sidebar`, `slider`,
|
|
110
|
+
`switch`, `table`, `table-primitives`, `tabs`, `textarea`, `toggle`,
|
|
111
|
+
`toggle-group`, `tooltip`.
|
|
112
|
+
|
|
113
|
+
## 4. `@core/contexts/AuthContext` — who is signed in
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { useAuth } from '@core/contexts/AuthContext'
|
|
117
|
+
const { user, isLoading } = useAuth()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`user` is `null` or a `User` (`@core/types/auth`):
|
|
121
|
+
|
|
122
|
+
| Field | Notes |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `id`, `email`, `full_name` | Person record identity |
|
|
125
|
+
| `account_id` | Currently active account (drives `X-Account-Id`) |
|
|
126
|
+
| `account?` | The Account row |
|
|
127
|
+
| `roles: string[]` | Role slugs — check `includes('system_admin')` etc. |
|
|
128
|
+
| `permissions: string[]` | Resolved permission strings |
|
|
129
|
+
| `is_system_admin?: boolean` | Shortcut for the system-admin role |
|
|
130
|
+
| `status?: string` | `'pending'` users are gated to the pending-approval page |
|
|
131
|
+
| `accessible_accounts?: Account[]` | Accounts the user may switch between |
|
|
132
|
+
|
|
133
|
+
## 5. `@core/lib/utils` — `cn` + formatters
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { cn, formatDateTime, formatCurrency } from '@core/lib/utils'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`cn(...)` = `twMerge(clsx(...))` for conditional Tailwind classes.
|
|
140
|
+
Formatters: `formatDate`, `formatTime`, `formatDateTime`,
|
|
141
|
+
`formatRelativeTime`, `formatFileSize`, `formatCurrency`, `formatNumber`,
|
|
142
|
+
`truncateText`. Use them instead of hand-rolling `toLocaleString` calls so
|
|
143
|
+
dates/money render consistently with the rest of the shell.
|
|
144
|
+
|
|
145
|
+
## 6. `@core/components/shared` — instant account/entity UI
|
|
146
|
+
|
|
147
|
+
| Component | Purpose |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `MemberList` | Account members with roles |
|
|
150
|
+
| `InviteManager` | Invite creation/management UI |
|
|
151
|
+
| `AgentView` | AI-agent interaction surface |
|
|
152
|
+
| `FieldRenderer` | Render a `design_schema` field for display/input |
|
|
153
|
+
| `SchemaFields` | Render a whole `design_schema.fields` section |
|
|
154
|
+
|
|
155
|
+
`FieldRenderer`/`SchemaFields` are how generic pages render schema-driven
|
|
156
|
+
forms — reuse them in custom apps instead of reimplementing field widgets.
|
|
157
|
+
|
|
158
|
+
## 7. `@core/hooks/useApi` — request state wrappers
|
|
159
|
+
|
|
160
|
+
Thin React state over `apiFetch`-style calls. All three return
|
|
161
|
+
`{ data, loading, error, ... }` shapes with cancellation and callbacks.
|
|
162
|
+
|
|
163
|
+
| Hook | Signature | Use |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `useApi<T>` | `(apiFn: (params?) => Promise<T>, { immediate?, onSuccess?, onError?, initialData? })` → `{ data, loading, error, execute, reset, refetch }` | One-shot fetch with cancellation |
|
|
166
|
+
| `usePaginatedApi<T>` | `(apiFn: ({page, itemsPerPage, ...}) => Promise<{data, pagination}>, { itemsPerPage? })` → adds `pagination`, `nextPage`/`prevPage` | Server-paginated lists |
|
|
167
|
+
| `useMutation<T, P>` | `(mutateFn: (params: P) => Promise<T>, { onSuccess?, onError?, onSettled? })` → `{ data, loading, error, mutate, reset }` | Create/update/delete calls |
|
|
168
|
+
|
|
169
|
+
## 8. `@core/components/kb` — knowledge-base widgets
|
|
170
|
+
|
|
171
|
+
`KBArticleList`, `KBArticleEditor` — list and editing UI for `kb` records.
|
|
172
|
+
Use when an app surfaces framework knowledge rather than its own KB types.
|
|
173
|
+
|
|
174
|
+
## 9. Layout, app context, client, types
|
|
175
|
+
|
|
176
|
+
| Import | Exports | Use |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `@core/components/layout` | `AppShell`, `Header`, `Sidebar`, `Layout` | Wrap custom pages in the app chrome |
|
|
179
|
+
| `@core/contexts/AppContext` | `useCurrentApp() → AppRecord`, `useAppsRegistry() → { apps, routableApps, loading, error, refetch }`, `AppProvider`, `AppsRegistryProvider` | Current app's manifest row (slug, route_prefix, config, nav_items); the whole registry |
|
|
180
|
+
| `@core/lib/supabase` | `supabase` (browser client), `getCurrentSession`, `getCurrentUser`, `refreshSession` | Direct Supabase access — prefer `apiFetch`/endpoints for record data |
|
|
181
|
+
| `@core/types` | `DesignSchema`, `FieldDefinition`, `View`/`ListView`/`DetailView`, `ItemType`, `Item`, `PaginationParams`, `SortParams`, `FilterParams`, `FormState` | Shared type defs for schema-driven work |
|
|
182
|
+
|
|
183
|
+
## Other exports — available, unproven
|
|
184
|
+
|
|
185
|
+
`useEntityList`, `useEntityRecord`, `useSchemaRecord`, `useListSchema`,
|
|
186
|
+
`useForm`, `useBranding`, `useApps` exist in `hooks/`/`lib/` but shipped
|
|
187
|
+
apps do not use them today (they predate or duplicate the surfaces above).
|
|
188
|
+
Treat as internal scaffolding; prefer the documented tiers.
|
|
189
|
+
|
|
190
|
+
## Boundaries
|
|
191
|
+
|
|
192
|
+
- **Never import `@core/pages/*` or `@core/apps/*`** from a custom app —
|
|
193
|
+
admin/internal pages are not SDK surface (one app importing
|
|
194
|
+
`pages/admin/LogsPage` is a known violation, not a pattern).
|
|
195
|
+
- **Never import `@shared/*` in frontend code** — server-only modules.
|
|
196
|
+
- Framework code never imports from `custom/`; do not rely on your app
|
|
197
|
+
being reachable from core.
|
|
198
|
+
- For runtime record CRUD, always `/api/admin-data` via `apiFetch` — never
|
|
199
|
+
bespoke server endpoints (see [admin-data](api/admin-data.md)).
|
|
200
|
+
|
|
201
|
+
## Related
|
|
202
|
+
|
|
203
|
+
- [Build a custom page](../tasks/build-a-custom-page.md) — the task doc
|
|
204
|
+
that uses this surface end to end
|
|
205
|
+
- [Apps and renderers](../concepts/apps-and-renderers.md) — mounting model
|
|
206
|
+
and `useAppPath`'s role in it
|
|
207
|
+
- [admin-data API](api/admin-data.md) · [errors and envelopes](api/errors-and-envelopes.md)
|