okengine 0.2.6 → 0.3.2
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/AGENTS.md +33 -30
- package/README.md +74 -340
- package/{spec/manifest.v1.schema.json → manifest.v1.schema.json} +40 -9
- package/package.json +47 -44
- package/site/content/docs/ai/llms-txt.mdx +54 -0
- package/site/content/docs/ai/mcp.mdx +123 -0
- package/site/content/docs/ai/meta.json +5 -0
- package/site/content/docs/ai/skills.mdx +53 -0
- package/site/content/docs/console/access.mdx +29 -0
- package/site/content/docs/console/ai.mdx +35 -0
- package/site/content/docs/console/architecture.mdx +35 -0
- package/site/content/docs/console/channels.mdx +37 -0
- package/site/content/docs/console/clock.mdx +31 -0
- package/site/content/docs/console/flows.mdx +31 -0
- package/site/content/docs/console/gates.mdx +35 -0
- package/site/content/docs/console/manifest-diff.mdx +34 -0
- package/site/content/docs/console/meta.json +23 -0
- package/site/content/docs/console/overview.mdx +40 -0
- package/site/content/docs/console/plugins.mdx +41 -0
- package/site/content/docs/console/privacy.mdx +32 -0
- package/site/content/docs/console/runs.mdx +40 -0
- package/site/content/docs/console/signals.mdx +31 -0
- package/site/content/docs/console/store.mdx +32 -0
- package/site/content/docs/console/tenancy.mdx +32 -0
- package/site/content/docs/console/traces.mdx +34 -0
- package/site/content/docs/console/vault.mdx +37 -0
- package/site/content/docs/elements/ai.mdx +180 -0
- package/site/content/docs/elements/channel.mdx +167 -0
- package/site/content/docs/elements/clock.mdx +182 -0
- package/site/content/docs/elements/flow.mdx +288 -0
- package/site/content/docs/elements/gate.mdx +171 -0
- package/site/content/docs/elements/meta.json +5 -0
- package/site/content/docs/elements/signal.mdx +171 -0
- package/site/content/docs/elements/store.mdx +320 -0
- package/site/content/docs/elements/vault.mdx +263 -0
- package/site/content/docs/get-started/basic-usage.mdx +124 -0
- package/site/content/docs/get-started/comparison.mdx +65 -0
- package/site/content/docs/get-started/installation.mdx +113 -0
- package/site/content/docs/get-started/introduction.mdx +123 -0
- package/site/content/docs/get-started/meta.json +5 -0
- package/site/content/docs/index.mdx +63 -0
- package/site/content/docs/meta.json +5 -0
- package/site/content/docs/plugins/compression.mdx +60 -0
- package/site/content/docs/plugins/cors.mdx +92 -0
- package/site/content/docs/plugins/csrf.mdx +96 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +92 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +101 -0
- package/site/content/docs/plugins/meta.json +15 -0
- package/site/content/docs/plugins/security-headers.mdx +136 -0
- package/site/content/docs/reference/cli.md +101 -0
- package/site/content/docs/reference/configuration.mdx +159 -0
- package/site/content/docs/reference/environment-variables.mdx +87 -0
- package/site/content/docs/reference/errors.mdx +80 -0
- package/site/content/docs/reference/fx.mdx +117 -0
- package/site/content/docs/reference/meta.json +5 -0
- package/site/content/docs/reference/plugins.mdx +249 -0
- package/site/content/docs/reference/security.md +63 -0
- package/src/auth/api-keys.ts +2 -5
- package/src/auth/attenuation.ts +2 -6
- package/src/auth/auth.test.ts +56 -31
- package/src/auth/invoke-as.ts +1 -3
- package/src/auth/operator.ts +8 -26
- package/src/auth/planes.ts +2 -7
- package/src/auth/plugin.ts +4 -7
- package/src/auth/roles.ts +1 -5
- package/src/auth/sessions.ts +10 -39
- package/src/cli/args.ts +1 -5
- package/src/cli/ask-dev-mode.ts +40 -0
- package/src/cli/build.ts +1 -2
- package/src/cli/client-add.test.ts +8 -15
- package/src/cli/client-add.ts +5 -14
- package/src/cli/completion.test.ts +1 -5
- package/src/cli/completion.ts +4 -17
- package/src/cli/db-auto-push.test.ts +64 -0
- package/src/cli/db-auto-push.ts +72 -0
- package/src/cli/db.test.ts +189 -0
- package/src/cli/db.ts +456 -0
- package/src/cli/dev-app-runner.ts +2 -8
- package/src/cli/dev-db-push.test.ts +193 -0
- package/src/cli/dev-mode.test.ts +45 -0
- package/src/cli/dev-mode.ts +78 -0
- package/src/cli/dev-schema-sync.test.ts +66 -0
- package/src/cli/dev-schema-sync.ts +139 -0
- package/src/cli/dev.test.ts +264 -15
- package/src/cli/dev.ts +355 -139
- package/src/cli/doc-staleness.test.ts +69 -0
- package/src/cli/docker-cli.test.ts +27 -8
- package/src/cli/docker.ts +11 -3
- package/src/cli/doctor-diff.test.ts +1 -5
- package/src/cli/doctor-diff.ts +17 -46
- package/src/cli/doctor-pii.ts +2 -7
- package/src/cli/doctor.test.ts +26 -4
- package/src/cli/doctor.ts +35 -11
- package/src/cli/drizzle-env.test.ts +67 -0
- package/src/cli/drizzle-env.ts +78 -0
- package/src/cli/ensure-drizzle-config.ts +50 -0
- package/src/cli/eval.ts +4 -14
- package/src/cli/gates-list.ts +1 -4
- package/src/cli/hero-meta.test.ts +17 -19
- package/src/cli/hero-meta.ts +20 -30
- package/src/cli/images.ts +3 -8
- package/src/cli/index.ts +11 -2
- package/src/cli/load-config.images.test.ts +14 -14
- package/src/cli/load-config.ts +47 -47
- package/src/cli/mcp-from-console.ts +1 -4
- package/src/cli/mode.ts +65 -0
- package/src/cli/openbao-bootstrap.test.ts +147 -0
- package/src/cli/openbao-bootstrap.ts +280 -0
- package/src/cli/openbao-restart.integration.test.ts +136 -0
- package/src/cli/ports.test.ts +7 -14
- package/src/cli/ports.ts +7 -5
- package/src/cli/privacy-erase.ts +1 -3
- package/src/cli/registry.ts +75 -25
- package/src/cli/resolve-dev-sql-env.test.ts +48 -0
- package/src/cli/resolve-dev-sql-env.ts +42 -0
- package/src/cli/schema.ts +3 -10
- package/src/cli/stack.ts +8 -10
- package/src/cli/start.ts +4 -15
- package/src/cli/upgrade.ts +1 -4
- package/src/cli/vault-cmd.ts +63 -0
- package/src/client/budget-entry.ts +1 -6
- package/src/client/create.test.ts +6 -12
- package/src/client/create.ts +2 -8
- package/src/client/errors.ts +1 -4
- package/src/client/index.ts +1 -6
- package/src/client/notes-contract.test.ts +4 -13
- package/src/client/transport.ts +9 -30
- package/src/client/types.ts +15 -30
- package/src/compiler/aot.test.ts +2 -9
- package/src/compiler/aot.ts +1 -4
- package/src/compiler/differential.test.ts +3 -11
- package/src/compiler/dynamic.ts +1 -4
- package/src/compiler/effects-infer.ts +8 -34
- package/src/compiler/emit.ts +1 -3
- package/src/compiler/extract.test.ts +136 -30
- package/src/compiler/extract.ts +485 -157
- package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
- package/src/compiler/fixtures/skyport.expected.json +24 -83
- package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
- package/src/compiler/http-parse.ts +2 -9
- package/src/compiler/index.ts +1 -5
- package/src/compiler/response.ts +16 -7
- package/src/compiler/sucrose.ts +11 -31
- package/src/config/define-config.test.ts +55 -34
- package/src/config/index.ts +177 -46
- package/src/config/resolve-driver.test.ts +28 -17
- package/src/console/budget.test.ts +1 -4
- package/src/console/index.ts +1 -4
- package/src/console/server/access.test.ts +15 -43
- package/src/console/server/access.ts +19 -49
- package/src/console/server/ai.test.ts +3 -11
- package/src/console/server/ai.ts +23 -46
- package/src/console/server/app.ts +16 -45
- package/src/console/server/auth-rate.test.ts +4 -12
- package/src/console/server/bind.ts +1 -5
- package/src/console/server/channels.test.ts +2 -6
- package/src/console/server/channels.ts +10 -35
- package/src/console/server/claim.ts +2 -8
- package/src/console/server/clock.test.ts +2 -6
- package/src/console/server/clock.ts +8 -26
- package/src/console/server/console.test.ts +1 -3
- package/src/console/server/diff.test.ts +5 -17
- package/src/console/server/diff.ts +10 -31
- package/src/console/server/dry-run.audit.test.ts +2 -8
- package/src/console/server/flows.ts +64 -233
- package/src/console/server/gates.test.ts +14 -50
- package/src/console/server/gates.ts +13 -48
- package/src/console/server/index.ts +2 -9
- package/src/console/server/live.ts +3 -12
- package/src/console/server/operator-db.test.ts +4 -15
- package/src/console/server/operator-db.ts +13 -32
- package/src/console/server/plugin.ts +50 -53
- package/src/console/server/plugins.test.ts +2 -6
- package/src/console/server/plugins.ts +17 -56
- package/src/console/server/runs-pii.ts +2 -7
- package/src/console/server/security.gate.test.ts +18 -40
- package/src/console/server/serve.ts +10 -25
- package/src/console/server/signals.test.ts +1 -3
- package/src/console/server/signals.ts +17 -40
- package/src/console/server/state.ts +21 -59
- package/src/console/server/store.test.ts +20 -22
- package/src/console/server/store.ts +31 -76
- package/src/console/server/vault.test.ts +12 -29
- package/src/console/server/vault.ts +44 -66
- package/src/console/ui/access/AccessA11yView.tsx +6 -21
- package/src/console/ui/access/a11y.test.tsx +3 -12
- package/src/console/ui/access/acknowledgement.test.ts +1 -4
- package/src/console/ui/access/acknowledgement.ts +1 -3
- package/src/console/ui/access/blast-radius.ts +1 -3
- package/src/console/ui/access/confirmation.test.ts +1 -5
- package/src/console/ui/access/confirmation.ts +1 -4
- package/src/console/ui/access/fixture.ts +1 -5
- package/src/console/ui/access/grantable.test.ts +1 -3
- package/src/console/ui/access/grantable.ts +2 -7
- package/src/console/ui/access/index.ts +2 -9
- package/src/console/ui/access/provenance.test.ts +1 -3
- package/src/console/ui/access/provenance.ts +2 -6
- package/src/console/ui/access/search.test.ts +1 -5
- package/src/console/ui/access/search.ts +2 -6
- package/src/console/ui/ai/AiA11yView.tsx +6 -17
- package/src/console/ui/ai/a11y.test.tsx +2 -5
- package/src/console/ui/ai/fixture.ts +2 -7
- package/src/console/ui/ai/format.ts +1 -3
- package/src/console/ui/ai/group.test.ts +1 -3
- package/src/console/ui/ai/group.ts +4 -12
- package/src/console/ui/ai/index.ts +2 -9
- package/src/console/ui/ai/promotion.test.ts +2 -7
- package/src/console/ui/ai/promotion.ts +4 -13
- package/src/console/ui/ai/search.test.ts +2 -11
- package/src/console/ui/ai/search.ts +3 -13
- package/src/console/ui/ai/types.ts +1 -8
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
- package/src/console/ui/architecture/a11y.test.tsx +1 -2
- package/src/console/ui/architecture/declared.ts +1 -5
- package/src/console/ui/architecture/index.ts +7 -35
- package/src/console/ui/architecture/layout.ts +1 -3
- package/src/console/ui/architecture/pathologies.ts +10 -19
- package/src/console/ui/architecture/search.ts +6 -15
- package/src/console/ui/architecture/traffic.ts +4 -14
- package/src/console/ui/architecture/types.ts +2 -10
- package/src/console/ui/architecture/view.test.ts +7 -19
- package/src/console/ui/architecture/view.ts +9 -36
- package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
- package/src/console/ui/channels/a11y.test.tsx +1 -2
- package/src/console/ui/channels/confirmation.test.ts +1 -4
- package/src/console/ui/channels/confirmation.ts +1 -4
- package/src/console/ui/channels/findings.ts +1 -5
- package/src/console/ui/channels/index.ts +2 -8
- package/src/console/ui/channels/locale.test.ts +1 -3
- package/src/console/ui/channels/locale.ts +2 -8
- package/src/console/ui/channels/search.ts +4 -13
- package/src/console/ui/channels/taxonomy.ts +1 -3
- package/src/console/ui/clock/ClockA11yView.tsx +9 -26
- package/src/console/ui/clock/a11y.test.tsx +2 -5
- package/src/console/ui/clock/confirmation.test.ts +1 -3
- package/src/console/ui/clock/confirmation.ts +1 -4
- package/src/console/ui/clock/findings.ts +1 -4
- package/src/console/ui/clock/health.ts +2 -7
- package/src/console/ui/clock/index.ts +2 -10
- package/src/console/ui/clock/search.test.ts +1 -3
- package/src/console/ui/clock/search.ts +2 -6
- package/src/console/ui/clock/timeline.test.ts +1 -3
- package/src/console/ui/clock/waiting-on.test.ts +1 -5
- package/src/console/ui/clock/waiting-on.ts +2 -7
- package/src/console/ui/diff/DiffA11yView.tsx +8 -17
- package/src/console/ui/diff/a11y.test.tsx +2 -5
- package/src/console/ui/diff/group.test.ts +3 -13
- package/src/console/ui/diff/group.ts +2 -8
- package/src/console/ui/diff/index.ts +2 -8
- package/src/console/ui/diff/search.test.ts +1 -5
- package/src/console/ui/diff/search.ts +4 -16
- package/src/console/ui/diff/summary.ts +1 -2
- package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
- package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
- package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
- package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
- package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
- package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
- package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
- package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
- package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
- package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
- package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
- package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
- package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
- package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
- package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
- package/src/console/ui/dist/index.html +1 -1
- package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
- package/src/console/ui/flows/a11y.test.tsx +2 -8
- package/src/console/ui/flows/buffer.test.ts +3 -11
- package/src/console/ui/flows/buffer.ts +4 -11
- package/src/console/ui/flows/confirmation.ts +1 -3
- package/src/console/ui/flows/contract.test.ts +3 -7
- package/src/console/ui/flows/contract.ts +12 -36
- package/src/console/ui/flows/graph.test.ts +3 -11
- package/src/console/ui/flows/graph.ts +5 -15
- package/src/console/ui/flows/index.ts +1 -6
- package/src/console/ui/flows/save-as-test.ts +1 -3
- package/src/console/ui/flows/search.ts +8 -25
- package/src/console/ui/flows/tiers.ts +2 -9
- package/src/console/ui/gates/GatesA11yView.tsx +4 -13
- package/src/console/ui/gates/a11y.test.tsx +2 -5
- package/src/console/ui/gates/audit.ts +1 -3
- package/src/console/ui/gates/denial.test.ts +1 -3
- package/src/console/ui/gates/denial.ts +4 -12
- package/src/console/ui/gates/findings.ts +1 -3
- package/src/console/ui/gates/group.test.ts +3 -3
- package/src/console/ui/gates/group.ts +3 -11
- package/src/console/ui/gates/index.ts +2 -10
- package/src/console/ui/gates/search.ts +3 -10
- package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
- package/src/console/ui/overview/a11y.test.tsx +1 -2
- package/src/console/ui/overview/busiest.ts +1 -3
- package/src/console/ui/overview/compose.ts +1 -13
- package/src/console/ui/overview/cost.ts +34 -38
- package/src/console/ui/overview/fixture.ts +21 -26
- package/src/console/ui/overview/golden.ts +4 -14
- package/src/console/ui/overview/index.ts +2 -9
- package/src/console/ui/overview/rank.test.ts +3 -10
- package/src/console/ui/overview/rank.ts +2 -7
- package/src/console/ui/overview/slo.test.ts +5 -9
- package/src/console/ui/overview/slo.ts +6 -23
- package/src/console/ui/overview/verdict.ts +3 -10
- package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
- package/src/console/ui/plugins/a11y.test.tsx +1 -2
- package/src/console/ui/plugins/fixture.ts +2 -4
- package/src/console/ui/plugins/group.test.ts +1 -3
- package/src/console/ui/plugins/group.ts +1 -6
- package/src/console/ui/plugins/index.ts +3 -13
- package/src/console/ui/plugins/search.ts +7 -19
- package/src/console/ui/runs/RunsA11yView.tsx +5 -20
- package/src/console/ui/runs/a11y.test.tsx +2 -5
- package/src/console/ui/runs/explain.ts +2 -6
- package/src/console/ui/runs/group.ts +3 -12
- package/src/console/ui/runs/histogram.test.ts +2 -11
- package/src/console/ui/runs/histogram.ts +1 -4
- package/src/console/ui/runs/index.ts +4 -18
- package/src/console/ui/runs/project.ts +2 -6
- package/src/console/ui/runs/query.test.ts +1 -3
- package/src/console/ui/runs/query.ts +12 -35
- package/src/console/ui/runs/search.ts +5 -19
- package/src/console/ui/runs/trace-link.test.ts +2 -6
- package/src/console/ui/runs/trace-link.ts +4 -16
- package/src/console/ui/runs/types.ts +1 -3
- package/src/console/ui/shell/App.tsx +2 -10
- package/src/console/ui/shell/client.ts +22 -115
- package/src/console/ui/shell/components/ui.tsx +1 -4
- package/src/console/ui/shell/layout/Shell.tsx +2 -4
- package/src/console/ui/shell/main.tsx +31 -96
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
- package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
- package/src/console/ui/shell/setup/Wizard.tsx +3 -5
- package/src/console/ui/shell/styles.css +1 -2
- package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
- package/src/console/ui/signals/a11y.test.tsx +2 -5
- package/src/console/ui/signals/confirmation.test.ts +1 -5
- package/src/console/ui/signals/confirmation.ts +2 -6
- package/src/console/ui/signals/dry-run.ts +1 -3
- package/src/console/ui/signals/durable.ts +1 -3
- package/src/console/ui/signals/findings.ts +1 -3
- package/src/console/ui/signals/group.test.ts +2 -8
- package/src/console/ui/signals/group.ts +2 -10
- package/src/console/ui/signals/index.ts +1 -4
- package/src/console/ui/signals/monitor.ts +2 -7
- package/src/console/ui/signals/schema-form.test.ts +3 -14
- package/src/console/ui/signals/schema-form.ts +1 -4
- package/src/console/ui/signals/search.ts +4 -13
- package/src/console/ui/store/StoreA11yView.tsx +3 -9
- package/src/console/ui/store/a11y.test.tsx +2 -5
- package/src/console/ui/store/confirmation.test.ts +1 -3
- package/src/console/ui/store/confirmation.ts +1 -4
- package/src/console/ui/store/dry-run.ts +2 -5
- package/src/console/ui/store/fixture.ts +1 -2
- package/src/console/ui/store/group.test.ts +1 -6
- package/src/console/ui/store/group.ts +1 -4
- package/src/console/ui/store/index.ts +1 -5
- package/src/console/ui/store/search.ts +2 -6
- package/src/console/ui/store/will-not-fire.ts +1 -2
- package/src/console/ui/traces/TracesA11yView.tsx +10 -29
- package/src/console/ui/traces/a11y.test.tsx +1 -2
- package/src/console/ui/traces/chain.test.ts +5 -19
- package/src/console/ui/traces/chain.ts +1 -3
- package/src/console/ui/traces/critical-path.test.ts +1 -3
- package/src/console/ui/traces/critical-path.ts +2 -6
- package/src/console/ui/traces/filter.test.ts +2 -6
- package/src/console/ui/traces/filter.ts +5 -13
- package/src/console/ui/traces/fold.test.ts +4 -12
- package/src/console/ui/traces/fold.ts +1 -3
- package/src/console/ui/traces/mini.ts +1 -3
- package/src/console/ui/traces/replay.test.ts +1 -3
- package/src/console/ui/traces/replay.ts +1 -3
- package/src/console/ui/traces/sampling.ts +1 -4
- package/src/console/ui/traces/search.ts +4 -17
- package/src/console/ui/traces/tier.ts +1 -3
- package/src/console/ui/vault/VaultA11yView.tsx +5 -17
- package/src/console/ui/vault/a11y.test.tsx +1 -2
- package/src/console/ui/vault/blast-radius.ts +1 -3
- package/src/console/ui/vault/confirmation.test.ts +1 -5
- package/src/console/ui/vault/confirmation.ts +1 -4
- package/src/console/ui/vault/dormant.test.ts +1 -4
- package/src/console/ui/vault/export-safe.test.ts +1 -5
- package/src/console/ui/vault/export-safe.ts +1 -4
- package/src/console/ui/vault/fixture.ts +3 -3
- package/src/console/ui/vault/group.ts +1 -2
- package/src/console/ui/vault/index.ts +2 -10
- package/src/console/ui/vault/search.ts +2 -6
- package/src/console/ui/vault/types.ts +1 -1
- package/src/console/xss.gate.test.ts +1 -3
- package/src/docker/compose.ts +215 -27
- package/src/docker/derive.ts +6 -14
- package/src/docker/docker.test.ts +118 -15
- package/src/docker/dockerfile.integration.test.ts +12 -21
- package/src/docker/helpers.ts +5 -4
- package/src/docker/index.ts +9 -6
- package/src/docker/pin.ts +1 -9
- package/src/docker/recipes/index.ts +6 -6
- package/src/docker/recipes/mailpit.ts +22 -0
- package/src/docker/recipes/openbao.ts +47 -0
- package/src/docker/recipes/postgres.ts +17 -6
- package/src/docker/recipes/redis.ts +11 -2
- package/src/docker/recipes/rustfs.ts +32 -0
- package/src/docker/stack-id.test.ts +73 -5
- package/src/docker/stack-id.ts +124 -33
- package/src/docker/stack.integration.test.ts +6 -21
- package/src/docker/stack.ts +36 -4
- package/src/docker/types.ts +16 -4
- package/src/docs-origin.ts +4 -4
- package/src/drivers/ai-anthropic.ts +5 -14
- package/src/drivers/ai-mock.ts +2 -5
- package/src/drivers/ai-openai-compatible.ts +4 -10
- package/src/drivers/ai-providers.test.ts +6 -13
- package/src/drivers/bun-native-completeness.test.ts +6 -15
- package/src/drivers/channel-console.ts +6 -7
- package/src/drivers/channel-fcm.ts +20 -30
- package/src/drivers/channel-resend.ts +1 -3
- package/src/drivers/channel-smtp.ts +2 -6
- package/src/drivers/channel-types.ts +2 -12
- package/src/drivers/channel-unifonic.ts +2 -6
- package/src/drivers/channel-wa-cloud.ts +2 -6
- package/src/drivers/channel-webpush.ts +21 -60
- package/src/drivers/conformance.test.ts +2 -10
- package/src/drivers/conformance.ts +8 -21
- package/src/drivers/drizzle-dialect.test.ts +20 -0
- package/src/drivers/drizzle-dialect.ts +37 -0
- package/src/drivers/fs.ts +2 -8
- package/src/drivers/index.ts +12 -57
- package/src/drivers/memory.ts +287 -75
- package/src/drivers/pgvector.ts +7 -11
- package/src/drivers/postgres.ts +7 -13
- package/src/drivers/redis.ts +13 -37
- package/src/drivers/s3.ts +15 -18
- package/src/drivers/signal-engine.ts +12 -49
- package/src/drivers/signal-memory.ts +2 -8
- package/src/drivers/signal-nats.ts +4 -9
- package/src/drivers/signal-postgres.ts +62 -129
- package/src/drivers/signal-redis.ts +7 -21
- package/src/drivers/signal-types.ts +1 -4
- package/src/drivers/sqlite.ts +3 -12
- package/src/drivers/types.ts +4 -23
- package/src/drivers/vault-driver-removal.test.ts +55 -0
- package/src/drivers/vault-env.ts +2 -6
- package/src/drivers/vault-infisical.ts +1 -5
- package/src/drivers/vault-managed.ts +1 -5
- package/src/drivers/vault-memory.ts +1 -5
- package/src/drivers/vault-openbao.test.ts +97 -0
- package/src/drivers/vault-openbao.ts +103 -40
- package/src/drivers/vault-types.ts +3 -16
- package/src/elements/ai/declare.ts +4 -14
- package/src/elements/ai/eval.ts +2 -6
- package/src/elements/ai/pii.ts +1 -3
- package/src/elements/ai/runtime.ts +12 -50
- package/src/elements/ai/schema.ts +4 -15
- package/src/elements/ai.test.ts +13 -24
- package/src/elements/ai.ts +1 -5
- package/src/elements/channel/consent.ts +3 -12
- package/src/elements/channel/costs.ts +4 -15
- package/src/elements/channel/declare.ts +1 -4
- package/src/elements/channel/email-auth.ts +1 -4
- package/src/elements/channel/locale.ts +2 -10
- package/src/elements/channel/mask.ts +1 -3
- package/src/elements/channel/mime.ts +2 -12
- package/src/elements/channel/outcomes.test.ts +1 -5
- package/src/elements/channel/outcomes.ts +5 -15
- package/src/elements/channel/receipts.ts +2 -7
- package/src/elements/channel/runtime.ts +28 -74
- package/src/elements/channel/suppression.ts +8 -14
- package/src/elements/channel.test.ts +8 -31
- package/src/elements/channel.ts +3 -14
- package/src/elements/clock/actions.ts +6 -18
- package/src/elements/clock/declare.ts +1 -3
- package/src/elements/clock/dst.ts +1 -5
- package/src/elements/clock/durable.ts +4 -16
- package/src/elements/clock/health.ts +2 -7
- package/src/elements/clock/leader.ts +2 -9
- package/src/elements/clock/reconcile.ts +4 -15
- package/src/elements/clock/runtime.ts +2 -5
- package/src/elements/clock/schedule.ts +4 -20
- package/src/elements/clock.test.ts +2 -7
- package/src/elements/clock.ts +5 -23
- package/src/elements/gate/permissions.ts +1 -4
- package/src/elements/gate/runtime.ts +7 -28
- package/src/elements/gate.ts +1 -5
- package/src/elements/index.ts +9 -7
- package/src/elements/signal/declare.ts +2 -7
- package/src/elements/signal/reconcile.test.ts +1 -4
- package/src/elements/signal/runtime.ts +1 -3
- package/src/elements/signal.test.ts +3 -15
- package/src/elements/signal.ts +2 -8
- package/src/elements/store/cache.test.ts +46 -0
- package/src/elements/store/cache.ts +3 -11
- package/src/elements/store/classify.ts +5 -22
- package/src/elements/store/declare.ts +11 -17
- package/src/elements/store/domain-ddl.test.ts +86 -0
- package/src/elements/store/emit-drizzle.ts +363 -0
- package/src/elements/store/files-policy.test.ts +1 -5
- package/src/elements/store/files-policy.ts +4 -12
- package/src/elements/store/load-plugin-tables.ts +103 -0
- package/src/elements/store/missing-relation.test.ts +83 -0
- package/src/elements/store/missing-relation.ts +38 -0
- package/src/elements/store/replica.ts +1 -4
- package/src/elements/store/resource-list-docs.fixture.ts +56 -0
- package/src/elements/store/resource-list-docs.test.ts +79 -0
- package/src/elements/store/resource.test.ts +253 -0
- package/src/elements/store/resource.ts +786 -0
- package/src/elements/store/runtime.ts +25 -28
- package/src/elements/store/schema-decl.test.ts +227 -0
- package/src/elements/store/schema-decl.ts +516 -0
- package/src/elements/store/sql-condition.test.ts +132 -0
- package/src/elements/store/sql-condition.ts +293 -65
- package/src/elements/store/sql-session.test.ts +135 -0
- package/src/elements/store/sql-session.ts +241 -84
- package/src/elements/store/table.ts +69 -23
- package/src/elements/store.test.ts +17 -39
- package/src/elements/store.ts +53 -31
- package/src/elements/vault/chain.ts +34 -0
- package/src/elements/vault/declare.ts +22 -32
- package/src/elements/vault/fingerprint.ts +2 -7
- package/src/elements/vault/redact.ts +2 -6
- package/src/elements/vault/runtime.ts +19 -33
- package/src/elements/vault.test.ts +12 -55
- package/src/elements/vault.ts +9 -13
- package/src/index.ts +5 -2
- package/src/kernel/adopt-routes.ts +15 -29
- package/src/kernel/app.ts +92 -118
- package/src/kernel/auth-resolve.ts +2 -9
- package/src/kernel/boot-bind/ai.ts +1 -4
- package/src/kernel/boot-bind/channel.test.ts +60 -0
- package/src/kernel/boot-bind/channel.ts +61 -5
- package/src/kernel/boot-bind/clock.ts +2 -6
- package/src/kernel/boot-bind/gate.ts +2 -8
- package/src/kernel/boot-bind/runs.ts +1 -3
- package/src/kernel/boot-bind/signal.ts +3 -10
- package/src/kernel/boot-bind/store.test.ts +27 -18
- package/src/kernel/boot-bind/store.ts +93 -52
- package/src/kernel/boot-bind/vault.ts +2 -8
- package/src/kernel/boot.test.ts +4 -10
- package/src/kernel/boot.ts +30 -77
- package/src/kernel/budget.test.ts +1 -3
- package/src/kernel/capability.ts +3 -10
- package/src/kernel/dry-run.test.ts +1 -4
- package/src/kernel/dry-run.ts +3 -11
- package/src/kernel/edge.test.ts +68 -0
- package/src/kernel/effects.test.ts +1 -5
- package/src/kernel/effects.ts +4 -11
- package/src/kernel/errors.registry.test.ts +2 -8
- package/src/kernel/errors.ts +23 -32
- package/src/kernel/flow.test.ts +12 -25
- package/src/kernel/flow.ts +13 -25
- package/src/kernel/fx.test.ts +31 -40
- package/src/kernel/fx.ts +135 -90
- package/src/kernel/hook-timing.test.ts +3 -6
- package/src/kernel/hook-timing.ts +5 -14
- package/src/kernel/hooks.test.ts +43 -10
- package/src/kernel/hooks.ts +25 -12
- package/src/kernel/index.ts +11 -17
- package/src/kernel/journal.ts +7 -31
- package/src/kernel/on.ts +45 -9
- package/src/kernel/pipeline.test.ts +10 -19
- package/src/kernel/pipeline.ts +5 -17
- package/src/kernel/plug.ts +4 -11
- package/src/kernel/plugin/conflicts.test.ts +13 -25
- package/src/kernel/plugin/decorate.test.ts +8 -13
- package/src/kernel/plugin/scoping.test.ts +4 -11
- package/src/kernel/plugin.ts +81 -27
- package/src/kernel/registry-isolation.test.ts +74 -0
- package/src/kernel/registry.ts +102 -30
- package/src/kernel/router.test.ts +15 -13
- package/src/kernel/router.ts +9 -29
- package/src/kernel/routing-budget.test.ts +1 -3
- package/src/kernel/run-telemetry.ts +1 -3
- package/src/kernel/triggers.ts +63 -21
- package/src/manifest/diff.test.ts +26 -91
- package/src/manifest/diff.ts +95 -225
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/manifest/index.ts +3 -10
- package/src/manifest/types.ts +30 -6
- package/src/manifest/undeclared.test.ts +4 -12
- package/src/manifest/undeclared.ts +1 -2
- package/src/manifest/validate.test.ts +2 -6
- package/src/manifest/validate.ts +5 -15
- package/src/mcp/authorization.ts +8 -27
- package/src/mcp/confirmation.ts +3 -10
- package/src/mcp/data.ts +2 -6
- package/src/mcp/docs-index.ts +4 -7
- package/src/mcp/docs-mcp.test.ts +3 -12
- package/src/mcp/docs-server.ts +7 -27
- package/src/mcp/docs-tools.ts +2 -6
- package/src/mcp/index.ts +1 -6
- package/src/mcp/injection.gate.test.ts +4 -18
- package/src/mcp/mcp.test.ts +18 -49
- package/src/mcp/protocol.ts +3 -10
- package/src/mcp/server.ts +11 -52
- package/src/mcp/session.ts +4 -14
- package/src/mcp/tools.ts +14 -50
- package/src/plugins/catalogue.test.ts +3 -7
- package/src/plugins/catalogue.ts +1 -3
- package/src/plugins/compression.test.ts +127 -0
- package/src/plugins/compression.ts +94 -0
- package/src/plugins/config-source.test.ts +204 -0
- package/src/plugins/config-source.ts +209 -0
- package/src/plugins/cors.test.ts +138 -0
- package/src/plugins/cors.ts +129 -0
- package/src/plugins/csrf.test.ts +102 -0
- package/src/plugins/csrf.ts +86 -0
- package/src/plugins/headers.ts +54 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/ip-allowlist.test.ts +105 -0
- package/src/plugins/ip-allowlist.ts +76 -0
- package/src/plugins/maintenance-mode.test.ts +91 -0
- package/src/plugins/maintenance-mode.ts +85 -0
- package/src/plugins/node-import-scan.ts +2 -5
- package/src/plugins/security-headers.test.ts +243 -0
- package/src/plugins/security-headers.ts +255 -0
- package/src/plugins/supply-chain.ts +5 -17
- package/src/release/exports.ts +3 -8
- package/src/release/measure.exports.test.ts +1 -3
- package/src/release/measure.ts +16 -45
- package/src/runs/bench.test.ts +1 -3
- package/src/runs/collect.ts +8 -25
- package/src/runs/drivers/clickhouse.ts +2 -9
- package/src/runs/drivers/files.ts +9 -31
- package/src/runs/drivers/memory.ts +5 -22
- package/src/runs/drivers/postgres.ts +8 -20
- package/src/runs/duckdb.ts +2 -8
- package/src/runs/index.ts +3 -14
- package/src/runs/outlier.ts +3 -10
- package/src/runs/parquet.ts +8 -26
- package/src/runs/privacy.ts +2 -8
- package/src/runs/runs.test.ts +3 -9
- package/src/runs/runtime.ts +7 -32
- package/src/runs/shred.ts +10 -29
- package/src/runs/types.ts +1 -4
- package/src/runtime/bun.ts +4 -15
- package/src/runtime/cold-start.bench.ts +1 -3
- package/src/runtime/dev-request-log.test.ts +1 -4
- package/src/runtime/dev-request-log.ts +1 -4
- package/src/runtime/index.ts +1 -6
- package/src/runtime/primitives.ts +5 -4
- package/src/runtime/security.ts +3 -13
- package/src/runtime/serve.test.ts +4 -7
- package/src/runtime/types.ts +1 -4
- package/src/runtime/web-standard.ts +3 -15
- package/src/term.test.ts +4 -6
- package/src/term.ts +49 -98
- package/src/test/create-test-app.test.ts +4 -14
- package/src/test/create-test-app.ts +27 -33
- package/src/test/provisions.integration.test.ts +3 -11
- package/src/upgrade/codemods.test.ts +25 -6
- package/src/upgrade/codemods.ts +116 -3
- package/src/validation/standard-schema.test.ts +2 -5
- package/src/validation/standard-schema.ts +13 -33
- package/docs/spec/console.md +0 -739
- package/docs/spec/example.md +0 -1196
- package/docs/spec/four-applications.md +0 -1195
- package/docs/spec/unified-theory.md +0 -489
- package/src/cli/doc-drift.test.ts +0 -146
- package/src/cli/doc-drift.ts +0 -430
- package/src/cli/doctor-diff-examples.ts +0 -92
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
- package/src/drivers/vault-sops.ts +0 -271
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Gate"
|
|
3
|
+
description: "Permission to act — auth policies and rate limits attached to the trigger, evaluated before any effect runs, denied as typed errors."
|
|
4
|
+
icon: "ShieldCheck"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Gate is how your app answers **"may this happen?"** before it happens: is this request from a verified member? has this IP exhausted its minute quota? Gates attach to the trigger — the pipeline evaluates them before a single store write, emit, or channel send runs.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">
|
|
11
|
+
Permission sits **at the trigger**, and denial is a typed error value — never a thrown exception,
|
|
12
|
+
never an effect that runs halfway and rolls back.
|
|
13
|
+
</Callout>
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
<Steps>
|
|
18
|
+
|
|
19
|
+
<Step>
|
|
20
|
+
### Declare your gates
|
|
21
|
+
|
|
22
|
+
Policies are named predicates; rate limits are declarative budgets. Both live once, app-wide:
|
|
23
|
+
|
|
24
|
+
```typescript title="src/gates.ts"
|
|
25
|
+
import { gate } from "okengine";
|
|
26
|
+
|
|
27
|
+
export const member = gate.policy("member", ({ auth }) => !!auth?.verified);
|
|
28
|
+
|
|
29
|
+
export const fair = gate.rate({
|
|
30
|
+
strategy: "sliding-window-counter", // the default — near-exact, no boundary bursts
|
|
31
|
+
max: 60,
|
|
32
|
+
per: "1m",
|
|
33
|
+
keyBy: "ip",
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
</Step>
|
|
38
|
+
|
|
39
|
+
<Step>
|
|
40
|
+
### Attach them to triggers
|
|
41
|
+
|
|
42
|
+
`.gate(...)` composes in registration order — evaluated left to right, first denial wins:
|
|
43
|
+
|
|
44
|
+
```typescript title="src/flows/links/shorten.ts"
|
|
45
|
+
export const shorten = on(
|
|
46
|
+
http.post("/links").gate(member, fair),
|
|
47
|
+
flow({
|
|
48
|
+
in: NewLink,
|
|
49
|
+
out: LinkCode,
|
|
50
|
+
errors: { Taken },
|
|
51
|
+
do: async ({ url, code }, fx) => {
|
|
52
|
+
/* runs only if both gates passed */
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
</Step>
|
|
59
|
+
|
|
60
|
+
<Step>
|
|
61
|
+
### Denials are typed failures
|
|
62
|
+
|
|
63
|
+
A denied request never reaches `do`. It returns one of three typed failures, like any entry in `errors: { … }`:
|
|
64
|
+
|
|
65
|
+
| Gate result | Typed failure |
|
|
66
|
+
| -------------------------------- | ------------------------------------- |
|
|
67
|
+
| Policy denied, not authenticated | `Unauthorized` |
|
|
68
|
+
| Policy denied, authenticated | `Forbidden` (with gate name + reason) |
|
|
69
|
+
| Rate limit exceeded | `RateLimited` (with `retryAfterMs`) |
|
|
70
|
+
|
|
71
|
+
</Step>
|
|
72
|
+
|
|
73
|
+
</Steps>
|
|
74
|
+
|
|
75
|
+
## Two kinds of gates
|
|
76
|
+
|
|
77
|
+
| Declaration | Question it answers | Evaluated against |
|
|
78
|
+
| -------------------------- | -------------------------------------- | ---------------------------------- |
|
|
79
|
+
| `gate.policy(name, check)` | Is this principal allowed? (ABAC) | auth / operator / request metadata |
|
|
80
|
+
| `gate.rate(options)` | Is there budget left for this subject? | an atomic counter on the kv driver |
|
|
81
|
+
|
|
82
|
+
### The policy context
|
|
83
|
+
|
|
84
|
+
The predicate receives everything it may decide with — nothing else:
|
|
85
|
+
|
|
86
|
+
| Field | Contents |
|
|
87
|
+
| ---------- | -------------------------------------------------------- |
|
|
88
|
+
| `auth` | User principal: `userId`, `scopes` (a `Set`), `verified` |
|
|
89
|
+
| `operator` | Operator principal (Console plane): `id` |
|
|
90
|
+
| `meta` | Request metadata for keying decisions: `ip`, `userId`, … |
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
export const admin = gate.policy("admin", ({ auth }) => auth.scopes.has("admin"));
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A policy name containing `:` is also a `Module:Action` permission — the same declaration feeds role-based access reviews in the Console.
|
|
97
|
+
|
|
98
|
+
### Rate options
|
|
99
|
+
|
|
100
|
+
| Option | Type | Default | Meaning |
|
|
101
|
+
| ------------- | ------------ | -------------------------- | -------------------------------------------- |
|
|
102
|
+
| `max` | number | — (**required**) | Takes allowed within `per` |
|
|
103
|
+
| `per` | string | — (**required**) | Window / refill period (`"1m"`, `"60s"`, …) |
|
|
104
|
+
| `keyBy` | string | — | Subject dimension (`"ip"`, `"user"`, …) |
|
|
105
|
+
| `strategy` | RateStrategy | `"sliding-window-counter"` | Algorithm (below) |
|
|
106
|
+
| `overridable` | boolean | `false` | Allow the Console to retune `max`/`per` live |
|
|
107
|
+
|
|
108
|
+
| Strategy | Behavior |
|
|
109
|
+
| ------------------------ | ------------------------------------------------------ |
|
|
110
|
+
| `sliding-window-counter` | Near-exact, two keys, no boundary bursts — the default |
|
|
111
|
+
| `fixed-window` | Cheapest; allows bursts at window boundaries |
|
|
112
|
+
| `sliding-log` | Exact; stores one timestamp per take |
|
|
113
|
+
| `token-bucket` | Steady refill; permits saved-up bursts |
|
|
114
|
+
| `leaky-bucket` | Smooths output to a constant rate |
|
|
115
|
+
|
|
116
|
+
All five run as atomic Lua on the kv driver — correct under concurrency, identical on the memory driver in tests.
|
|
117
|
+
|
|
118
|
+
## Identity comes from auth
|
|
119
|
+
|
|
120
|
+
Gates don't log users in — they decide about principals that already exist. The built-in auth plugin (`okengine/auth`) provides the user plane: hybrid sessions, argon2id password hashing, roles, API keys, and invites, with zero-config defaults. Whatever it establishes lands on `fx.auth`, which is exactly what your policies read.
|
|
121
|
+
|
|
122
|
+
## Every decision is recorded
|
|
123
|
+
|
|
124
|
+
Pass **and** deny, every evaluation lands on the run's `gates` dimension in the telemetry trace. The Console (`:6533` → Gates) shows which gates fired on a request and their outcomes — an audit trail of permission decisions you never had to build. Rate gates declared `overridable: true` can also be retuned (`max`/`per`) from the Console without a redeploy; anything else rejects edits by design.
|
|
125
|
+
|
|
126
|
+
## Troubleshooting
|
|
127
|
+
|
|
128
|
+
<Accordions>
|
|
129
|
+
<Accordion title="Can a policy read the request body?">
|
|
130
|
+
|
|
131
|
+
No — and that is deliberate. Gates run before the flow, so they see only the policy context (`auth`, `operator`, `meta`). Body-dependent decisions belong inside `do`, expressed as typed `errors`.
|
|
132
|
+
|
|
133
|
+
</Accordion>
|
|
134
|
+
<Accordion title="Logged-in users still get Forbidden">
|
|
135
|
+
|
|
136
|
+
`Forbidden` means the request _was_ authenticated but a policy said no. The failure includes the gate name and reason — check whether your predicate requires `verified` or a scope the user lacks.
|
|
137
|
+
|
|
138
|
+
</Accordion>
|
|
139
|
+
<Accordion title="Which keyBy should I use?">
|
|
140
|
+
|
|
141
|
+
`"ip"` for public unauthenticated surfaces (sign-up, password reset), `"user"` for authenticated quotas. Keying an authenticated endpoint by IP punishes users behind shared NAT; keying a public endpoint by user is impossible.
|
|
142
|
+
|
|
143
|
+
</Accordion>
|
|
144
|
+
<Accordion title="The Console won't retune my rate limit">
|
|
145
|
+
|
|
146
|
+
The gate was declared without `overridable: true`. Add it and redeploy — live retuning is opt-in per gate so a mistyped change cannot silently open a floodgate.
|
|
147
|
+
|
|
148
|
+
</Accordion>
|
|
149
|
+
</Accordions>
|
|
150
|
+
|
|
151
|
+
## Learn more
|
|
152
|
+
|
|
153
|
+
- [Flow](/docs/elements/flow) — the trigger pipeline gates plug into
|
|
154
|
+
- [Console · Gates](/docs/console/gates) — decision audit and live retuning
|
|
155
|
+
- [Vault](/docs/elements/vault) — the credentials your policies protect
|
|
156
|
+
|
|
157
|
+
## Next
|
|
158
|
+
|
|
159
|
+
<Cards>
|
|
160
|
+
<Card title="Vault" description="Continue to Vault." href="/docs/elements/vault" />
|
|
161
|
+
<Card
|
|
162
|
+
title="Introduction"
|
|
163
|
+
description="Eight elements overview."
|
|
164
|
+
href="/docs/get-started/introduction"
|
|
165
|
+
/>
|
|
166
|
+
<Card
|
|
167
|
+
title="Console"
|
|
168
|
+
description="Panels derived from the Manifest."
|
|
169
|
+
href="/docs/console/overview"
|
|
170
|
+
/>
|
|
171
|
+
</Cards>
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Signal"
|
|
3
|
+
description: "Data in motion — queues, pub/sub, and streams as one declaration whose delivery physics you choose explicitly."
|
|
4
|
+
icon: "Radio"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Signal is how your app moves data between flows and to clients: background jobs, events, fan-out notifications, live feeds. What other stacks split into a queue library, a pub/sub client, and a streaming platform is **one declaration** here — you pick the delivery physics, and the driver underneath is swappable per environment.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">
|
|
11
|
+
`delivery` is **mandatory with no default**. Delivery physics is a semantic decision about your
|
|
12
|
+
data — guessing it produces silent, expensive bugs, so omitting it is a type error.
|
|
13
|
+
</Callout>
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
<Steps>
|
|
18
|
+
|
|
19
|
+
<Step>
|
|
20
|
+
### Declare the signal
|
|
21
|
+
|
|
22
|
+
A signal is a named, typed channel. The schema describes the payload; `delivery` describes its physics:
|
|
23
|
+
|
|
24
|
+
```typescript title="src/signals.ts"
|
|
25
|
+
import { signal } from "okengine";
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
28
|
+
export const orderPlaced = signal("order-placed", {
|
|
29
|
+
schema: z.object({ orderId: z.string(), total: z.number() }),
|
|
30
|
+
delivery: "once", // queue physics: one consumer, retries, DLQ
|
|
31
|
+
retries: 3,
|
|
32
|
+
deadLetter: true,
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</Step>
|
|
37
|
+
|
|
38
|
+
<Step>
|
|
39
|
+
### Emit it from a Flow
|
|
40
|
+
|
|
41
|
+
Producers never touch a broker client — they call `fx.emit`:
|
|
42
|
+
|
|
43
|
+
```typescript title="src/flows/orders/place.ts"
|
|
44
|
+
do: async (input, fx) => {
|
|
45
|
+
await fx.store(db).insert(orders).values({ id: input.id /* … */ });
|
|
46
|
+
await fx.emit(orderPlaced, { orderId: input.id, total: input.total });
|
|
47
|
+
// With the postgres driver this enrols in the same transaction as the insert —
|
|
48
|
+
// the message is only ever sent if the row is committed.
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
</Step>
|
|
53
|
+
|
|
54
|
+
<Step>
|
|
55
|
+
### Consume it with a Flow
|
|
56
|
+
|
|
57
|
+
A signal is a trigger like any other — the consumer is an ordinary Flow:
|
|
58
|
+
|
|
59
|
+
```typescript title="src/flows/orders/send-confirmation.ts"
|
|
60
|
+
export const sendConfirmation = on(
|
|
61
|
+
orderPlaced,
|
|
62
|
+
flow({
|
|
63
|
+
in: z.object({ orderId: z.string(), total: z.number() }),
|
|
64
|
+
do: async (input, fx) => {
|
|
65
|
+
await fx.channel(email).send(/* … */);
|
|
66
|
+
},
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
</Step>
|
|
72
|
+
|
|
73
|
+
</Steps>
|
|
74
|
+
|
|
75
|
+
That's the loop: declare → `fx.emit` → `on(signal, flow)`. The runtime handles routing, retries, and dead-lettering.
|
|
76
|
+
|
|
77
|
+
## The three delivery physics
|
|
78
|
+
|
|
79
|
+
| `delivery` | Semantics | Use for |
|
|
80
|
+
| ------------- | ------------------------------------------------------ | ---------------------------------------------------------- |
|
|
81
|
+
| `"once"` | Queue: competing consumers, retries, dead-letter queue | Jobs that must happen exactly once — emails, payments sync |
|
|
82
|
+
| `"broadcast"` | Pub/sub: **every** subscriber receives a copy | Cache invalidation, cross-service events |
|
|
83
|
+
| `"live"` | Stream: client-subscribable and replayable | Live feeds, dashboards, progress updates |
|
|
84
|
+
|
|
85
|
+
The declaration is identical in shape for all three — switching physics later is a one-word change, not a migration to another library.
|
|
86
|
+
|
|
87
|
+
### Options
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Meaning |
|
|
90
|
+
| ------------ | --------------------------------- | ---------------- | -------------------------------------------------------------- |
|
|
91
|
+
| `delivery` | `"once" \| "broadcast" \| "live"` | — (**required**) | Delivery physics |
|
|
92
|
+
| `schema` | zod / Standard Schema | — | Payload contract; typed emits and Manifest docs |
|
|
93
|
+
| `retries` | number | `3` | Max delivery attempts before dead-letter (`once`) |
|
|
94
|
+
| `deadLetter` | boolean | `true` | Preserve exhausted messages in the DLQ (`once`) |
|
|
95
|
+
| `optional` | boolean | `false` | Allow emitting while nobody subscribes (skip the orphan check) |
|
|
96
|
+
|
|
97
|
+
## When delivery fails
|
|
98
|
+
|
|
99
|
+
`once` signals retry automatically. Every attempt keeps a **typed failure reason**, and the full attempt history survives into the DLQ — so when a message lands there you see _why_ each attempt failed, not just that it did.
|
|
100
|
+
|
|
101
|
+
The **Console** (`:6533` → Signals) shows the topology (which flows emit and consume each signal), per-subscriber delivery stats, and the DLQ contents with replay / discard controls — no separate broker UI to run.
|
|
102
|
+
|
|
103
|
+
<Callout title="Orphan emits fail loudly">
|
|
104
|
+
Emitting a signal with zero subscribers is normally an error — it almost always means a typo or a
|
|
105
|
+
forgotten consumer. Set `optional: true` on the declaration only when zero-subscriber emits are
|
|
106
|
+
genuinely expected (e.g. an integration hook).
|
|
107
|
+
</Callout>
|
|
108
|
+
|
|
109
|
+
## Per-environment drivers
|
|
110
|
+
|
|
111
|
+
```typescript title="oke.config.ts"
|
|
112
|
+
drivers: {
|
|
113
|
+
signal: { local: "memory", docker: "postgres", test: "memory", prod: "postgres" },
|
|
114
|
+
},
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Driver | Runs as | Best for |
|
|
118
|
+
| ---------- | -------------------------------- | ------------------------------------------------------------------------- |
|
|
119
|
+
| `memory` | in-process | Local loop + tests — zero infrastructure |
|
|
120
|
+
| `postgres` | your existing Postgres container | Default for docker/prod — emits join your DB transaction (outbox pattern) |
|
|
121
|
+
| `redis` | Redis container | Explicit alternative when throughput outgrows Postgres |
|
|
122
|
+
| `nats` | NATS container | High-throughput fan-out |
|
|
123
|
+
|
|
124
|
+
`postgres` is the default outside local dev for a reason: `fx.emit` enrols in the caller's transaction, so "write row + emit event" commits or rolls back **atomically** — the classic dual-write bug is designed out.
|
|
125
|
+
|
|
126
|
+
## Troubleshooting
|
|
127
|
+
|
|
128
|
+
<Accordions>
|
|
129
|
+
<Accordion title="Emit fails with a 'no subscribers' error">
|
|
130
|
+
|
|
131
|
+
You emitted a signal that no flow consumes. Either wire a consumer with `on(signal, flow)`, or set `optional: true` on the declaration if zero-subscriber emits are intentional.
|
|
132
|
+
|
|
133
|
+
</Accordion>
|
|
134
|
+
<Accordion title="A message keeps retrying and then disappears">
|
|
135
|
+
|
|
136
|
+
After `retries` attempts the message moves to the DLQ — it is not lost. Open Console → Signals, inspect the typed failure reasons on each attempt, fix the consumer, then replay.
|
|
137
|
+
|
|
138
|
+
</Accordion>
|
|
139
|
+
<Accordion title="once vs broadcast vs live — how do I choose?">
|
|
140
|
+
|
|
141
|
+
Ask: _how many consumers should process each message?_ One → `once`. All of them → `broadcast`. Clients over time, with replay → `live`. If you need retries and a DLQ, you want `once`.
|
|
142
|
+
|
|
143
|
+
</Accordion>
|
|
144
|
+
<Accordion title="My emit happened but the row didn't (or vice versa)">
|
|
145
|
+
|
|
146
|
+
That is the dual-write problem, and it means the signal driver is not `postgres`. With the `postgres` driver the emit joins your SQL transaction; with `redis` / `nats` an outbox relay keeps the guarantee, but `postgres` is the only mode where atomicity is literal.
|
|
147
|
+
|
|
148
|
+
</Accordion>
|
|
149
|
+
</Accordions>
|
|
150
|
+
|
|
151
|
+
## Learn more
|
|
152
|
+
|
|
153
|
+
- [Flow](/docs/elements/flow) — `on(trigger, flow)` and `fx.emit`
|
|
154
|
+
- [Console · Signals](/docs/console/signals) — topology, delivery stats, DLQ replay
|
|
155
|
+
- [Clock](/docs/elements/clock) — scheduled and delayed work
|
|
156
|
+
|
|
157
|
+
## Next
|
|
158
|
+
|
|
159
|
+
<Cards>
|
|
160
|
+
<Card title="Store" description="Continue to Store." href="/docs/elements/store" />
|
|
161
|
+
<Card
|
|
162
|
+
title="Introduction"
|
|
163
|
+
description="Eight elements overview."
|
|
164
|
+
href="/docs/get-started/introduction"
|
|
165
|
+
/>
|
|
166
|
+
<Card
|
|
167
|
+
title="Console"
|
|
168
|
+
description="Panels derived from the Manifest."
|
|
169
|
+
href="/docs/console/overview"
|
|
170
|
+
/>
|
|
171
|
+
</Cards>
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Store"
|
|
3
|
+
description: "Data at rest — SQL tables, KV cache, file blobs, and vector search, declared once and swapped per environment by driver."
|
|
4
|
+
icon: "Database"
|
|
5
|
+
source: "docs/spec/unified-theory.md"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Store is where your app's **data at rest** lives. It covers four kinds of storage — relational tables, key-value cache, file blobs, and vector search — behind one declaration style. Your flow code never changes between SQLite on your laptop and Postgres in production; only the driver does.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">
|
|
11
|
+
Drivers are named after **protocols**, not vendors (`postgres`, `redis`, `s3` — never `neon` or
|
|
12
|
+
`minio`). Vendor choice lives in the `images` map of `oke.config.ts`.
|
|
13
|
+
</Callout>
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
<Steps>
|
|
18
|
+
|
|
19
|
+
<Step>
|
|
20
|
+
### Declare a table
|
|
21
|
+
|
|
22
|
+
In `src/schema.decl.ts`, describe your tables with plain field builders — no ORM syntax:
|
|
23
|
+
|
|
24
|
+
```typescript title="src/schema.decl.ts"
|
|
25
|
+
import { store, field, id, now } from "okengine";
|
|
26
|
+
|
|
27
|
+
export const notes = store.schema.table("notes", {
|
|
28
|
+
id: field.text().primaryKey().defaultFn(id),
|
|
29
|
+
title: field.text().notNull(),
|
|
30
|
+
body: field.text().notNull(),
|
|
31
|
+
createdAt: field.integer().notNull().defaultFn(now),
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
</Step>
|
|
36
|
+
|
|
37
|
+
<Step>
|
|
38
|
+
### Declare the store
|
|
39
|
+
|
|
40
|
+
Bind the schema to a SQL store in `src/core.ts`:
|
|
41
|
+
|
|
42
|
+
```typescript title="src/core.ts"
|
|
43
|
+
import { store } from "okengine";
|
|
44
|
+
import { notes } from "./schema.decl";
|
|
45
|
+
|
|
46
|
+
export const db = store.sql("notes", { schema: { notes } });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
</Step>
|
|
50
|
+
|
|
51
|
+
<Step>
|
|
52
|
+
### Push the schema
|
|
53
|
+
|
|
54
|
+
`oke dev` runs this automatically on save; or run it by hand:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
oke db push # dev — applies the schema to your local database
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
</Step>
|
|
61
|
+
|
|
62
|
+
<Step>
|
|
63
|
+
### Read and write in a Flow
|
|
64
|
+
|
|
65
|
+
All data access goes through `fx.store(db)` — a typed session, one table at a time:
|
|
66
|
+
|
|
67
|
+
```typescript title="src/flows/notes/create.ts"
|
|
68
|
+
export const createNote = on(
|
|
69
|
+
http.post("/notes"),
|
|
70
|
+
flow({
|
|
71
|
+
in: z.object({ title: z.string(), body: z.string() }),
|
|
72
|
+
out: z.object({ id: z.string() }),
|
|
73
|
+
do: async (input, fx) => {
|
|
74
|
+
const id = fx.id();
|
|
75
|
+
await fx
|
|
76
|
+
.store(db)
|
|
77
|
+
.insert(notes)
|
|
78
|
+
.values({ id, ...input, createdAt: Date.now() });
|
|
79
|
+
return { id };
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
</Step>
|
|
86
|
+
|
|
87
|
+
</Steps>
|
|
88
|
+
|
|
89
|
+
## The four facets
|
|
90
|
+
|
|
91
|
+
Pick the facet that matches the physics of your data. Each declaration is one line; the runtime handle shows what flows can do with it.
|
|
92
|
+
|
|
93
|
+
| Facet | Declares | Best for | `fx.store(…)` handle |
|
|
94
|
+
| ------------------------- | ----------------- | ------------------------------------- | ------------------------------------------------------ |
|
|
95
|
+
| `store.sql(name, opts)` | a SQL database | domain tables, relations, constraints | `select` · `insert` · `update` · `delete` · `findById` |
|
|
96
|
+
| `store.kv(name)` | a key-value space | cache, sessions, rate limits | `get` · `set(key, value, ttl?)` · `delete` · `list` |
|
|
97
|
+
| `store.files(name)` | a blob bucket | uploads, exports, attachments | `put` · `get` · `delete` · `list(prefix?)` |
|
|
98
|
+
| `store.index(name, opts)` | a vector index | semantic search / RAG (`dims`) | `upsert` · `search(vector, topK?)` · `delete` |
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
export const cache = store.kv("sessions");
|
|
102
|
+
export const uploads = store.files("attachments");
|
|
103
|
+
export const embeddings = store.index("docs", { dims: 1536 });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## CRUD without boilerplate — `store.resource`
|
|
107
|
+
|
|
108
|
+
Five conventional endpoints (list, create, get, update, remove) expand from one declaration. Each is an ordinary Flow underneath — same contracts, same `fx`:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const notesR = store.resource(db, notes, {
|
|
112
|
+
in: NewNote, // create/update input schema
|
|
113
|
+
out: Note, // response schema
|
|
114
|
+
update: NewNote.partial(),
|
|
115
|
+
list: {
|
|
116
|
+
cursor: [notes.createdAt, notes.id], // keyset pagination columns
|
|
117
|
+
direction: "desc",
|
|
118
|
+
search: [notes.title], // ?search= / ?q=
|
|
119
|
+
filter: "all", // ?col=op.value — "all" | Column[] | "none"
|
|
120
|
+
order: "all", // ?order=col.desc
|
|
121
|
+
},
|
|
122
|
+
unit: "notes",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const mounted = on(http.resource("/notes", notesR.all()));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The list endpoint's URL is the whole query language:
|
|
129
|
+
|
|
130
|
+
| Param | Meaning | Example |
|
|
131
|
+
| ----------------------------------- | --------------------------------------------------- | ----------------------- |
|
|
132
|
+
| `?cursor=` / `?offset=` / `?limit=` | paginate (keyset when `cursor` columns are set) | `?limit=20&cursor=eyJ…` |
|
|
133
|
+
| `?search=` (`?q=`) | substring match over `search` columns | `?q=invoice` |
|
|
134
|
+
| `?col=op.value` | filter — ops `eq ne gt gte lt lte like ilike in is` | `?title=like.%draft%` |
|
|
135
|
+
| `?or=(…)` / `?and=(…)` | grouped boolean filters | `?or=(a.eq.1,b.eq.2)` |
|
|
136
|
+
| `?order=` | sort | `?order=createdAt.desc` |
|
|
137
|
+
| `?select=` | project columns | `?select=id,title` |
|
|
138
|
+
|
|
139
|
+
Responses follow the Stripe-style envelope: `{ data, meta: { nextCursor, hasNextPage }, error }`. `create` answers **201**, `remove` answers **204**, and a missing row is a typed `NotFound` — never a crash.
|
|
140
|
+
|
|
141
|
+
## Querying by hand
|
|
142
|
+
|
|
143
|
+
When `store.resource` is too conventional, `fx.store(db)` is the full single-table session:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// select — chain where / orderBy / limit / offset in any order
|
|
147
|
+
const latest = await fx
|
|
148
|
+
.store(db)
|
|
149
|
+
.select()
|
|
150
|
+
.from(notes)
|
|
151
|
+
.where(like(notes.title, `%${input.q}%`))
|
|
152
|
+
.orderBy(desc(notes.createdAt))
|
|
153
|
+
.limit(20);
|
|
154
|
+
|
|
155
|
+
// findById, insert, update, delete
|
|
156
|
+
const one = await fx.store(db).findById(notes, input.id);
|
|
157
|
+
await fx.store(db).update(notes).set({ title: input.title }).where(eq(notes.id, input.id));
|
|
158
|
+
await fx.store(db).delete(notes).where(lt(notes.createdAt, cutoff));
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
<Callout title="One table per call — no relational with:">
|
|
162
|
+
`fx.store` is deliberately **single-table**: Drizzle's relational `findMany({ with: … })` is not
|
|
163
|
+
available through `fx`. Compose joins as separate single-table reads (or `fx.call`) so every
|
|
164
|
+
table shows up explicitly in the Manifest's `reads` / `writes` — powering caching and PII masking.
|
|
165
|
+
</Callout>
|
|
166
|
+
|
|
167
|
+
## Schema — declare once, generate per dialect
|
|
168
|
+
|
|
169
|
+
The recommended path: declare tables ORM-agnostically, then let `oke db` emit real Drizzle for the active dialect (`sqliteTable` locally, `pgTable` in docker/prod) into `src/schema.generated.ts`.
|
|
170
|
+
|
|
171
|
+
| Field API | Meaning |
|
|
172
|
+
| -------------------------------------------- | ----------------------------------------------- |
|
|
173
|
+
| `field.text()` / `field.integer()` | v1 column primitives |
|
|
174
|
+
| `.primaryKey()` · `.notNull()` · `.unique()` | constraints |
|
|
175
|
+
| `.default(v)` · `.defaultFn(id \| now)` | defaults |
|
|
176
|
+
| `.pii()` · `.sensitive()` · `.retain("30d")` | privacy classification |
|
|
177
|
+
| `.as("sql_name")` | override the automatic `camelCase → snake_case` |
|
|
178
|
+
| `.references(() => col, { onDelete })` | foreign key |
|
|
179
|
+
|
|
180
|
+
### Foreign keys and relations
|
|
181
|
+
|
|
182
|
+
Declare FKs on fields, and relation metadata once per schema:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
export const daily = store.schema.table("daily", {
|
|
186
|
+
id: field.text().primaryKey(),
|
|
187
|
+
code: field
|
|
188
|
+
.text()
|
|
189
|
+
.notNull()
|
|
190
|
+
.references(() => links.code),
|
|
191
|
+
day: field.text().notNull(),
|
|
192
|
+
clicks: field.integer().notNull().default(0),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
export const relations = store.schema.relations({ links, daily }, (r) => ({
|
|
196
|
+
links: { daily: r.many.daily({ from: r.links.code, to: r.daily.code }) },
|
|
197
|
+
daily: { link: r.one.links({ from: r.daily.code, to: r.links.code, optional: false }) },
|
|
198
|
+
}));
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
<Callout title="Many-to-many">
|
|
202
|
+
A junction table with two foreign keys plus two `one` / `many` relations composes many-to-many.
|
|
203
|
+
There is no separate API and no `.through()` — the junction is an ordinary table.
|
|
204
|
+
</Callout>
|
|
205
|
+
|
|
206
|
+
### Syncing the schema
|
|
207
|
+
|
|
208
|
+
| Command | When |
|
|
209
|
+
| ----------------- | -------------------------------------------------------------- |
|
|
210
|
+
| `oke db push` | Dev — apply directly to the live local DB (no migration files) |
|
|
211
|
+
| `oke db generate` | Write versioned SQL under `drizzle/` for review |
|
|
212
|
+
| `oke db migrate` | Apply those files — human or CI, **never** at boot |
|
|
213
|
+
|
|
214
|
+
`oke dev` (local) auto-pushes when the schema file changes — opt out with `--no-db-push` or `db: { autoPush: false }`. Docker/prod **never** auto-apply DDL; a missing table fails loudly as **OKE1101**, telling you to run `oke db migrate`.
|
|
215
|
+
|
|
216
|
+
<Callout title="Escape hatch">
|
|
217
|
+
Hand-written Drizzle in `src/schema.ts` stays supported — if there is nothing to emit, the emit
|
|
218
|
+
step is skipped and your file is used as-is. Plugins may contribute **whole new tables**;
|
|
219
|
+
extending an app-owned table with plugin columns is not supported in v1.
|
|
220
|
+
</Callout>
|
|
221
|
+
|
|
222
|
+
## Per-environment drivers
|
|
223
|
+
|
|
224
|
+
Same flow code, different backends — configured once in `oke.config.ts`:
|
|
225
|
+
|
|
226
|
+
```typescript title="oke.config.ts"
|
|
227
|
+
drivers: {
|
|
228
|
+
store: {
|
|
229
|
+
sql: { local: "sqlite", docker: "postgres", test: "memory", prod: "postgres" },
|
|
230
|
+
kv: { local: "memory", docker: "redis", test: "memory", prod: "redis" },
|
|
231
|
+
files: { local: "fs", docker: "s3", test: "memory", prod: "s3" },
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
| Facet | Local | Docker / prod | Runs as |
|
|
237
|
+
| ------- | -------- | ------------- | --------------------------------------- |
|
|
238
|
+
| `sql` | `sqlite` | `postgres` | file on disk → container + named volume |
|
|
239
|
+
| `kv` | `memory` | `redis` | in-process → container |
|
|
240
|
+
| `files` | `fs` | `s3` | project folder → RustFS container |
|
|
241
|
+
| `index` | `memory` | `pgvector` | in-process → pgvector image |
|
|
242
|
+
|
|
243
|
+
Container images come from the `images` map — change the vendor by changing the pin, never the driver id.
|
|
244
|
+
|
|
245
|
+
## Privacy built in
|
|
246
|
+
|
|
247
|
+
Columns tagged `.pii()` or `.sensitive()` are masked at the store boundary — flows, logs, and the Console see a mask, not the value. Revealing cleartext PII requires an explicit `pii:reveal` gate on the flow, so access is a permission, not a convention.
|
|
248
|
+
|
|
249
|
+
## Examples — what follows from each choice
|
|
250
|
+
|
|
251
|
+
### An admin table
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
list: { mode: "offset", count: "exact", limit: 20 },
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Consequence:** `count: "exact"` (the offset default) runs `COUNT(*)` to fill `meta.total`. On a huge table that count is the real cost — set `count: "none"` to return only `meta.offset`.
|
|
258
|
+
|
|
259
|
+
### An infinite feed
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
list: { cursor: [notes.createdAt, notes.id], direction: "desc", limit: 20, maxLimit: 100 },
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Consequence:** keyset (cursor) paging is the default when `cursor` columns are set — pages stay stable while new rows are inserted, where offset pages would shift and show duplicates.
|
|
266
|
+
|
|
267
|
+
### A public, restricted endpoint
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
list: { mode: "offset", filter: "none", limit: 20 },
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Consequence:** a request that filters on a forbidden column — `?secret=eq.x` — fails with **422** and the exact message `unknown list param "secret"`. Filterable columns are a whitelist, never an accident.
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
<Accordions>
|
|
278
|
+
<Accordion title="OKE1101 — missing table in docker or prod">
|
|
279
|
+
|
|
280
|
+
Schema DDL never runs automatically outside local dev. Run `oke db push` against the target database for a dev-style sync, or `oke db generate` + `oke db migrate` for reviewed, versioned migrations.
|
|
281
|
+
|
|
282
|
+
</Accordion>
|
|
283
|
+
<Accordion title="I need a join — with: is not supported">
|
|
284
|
+
|
|
285
|
+
`fx.store` is one table per call by design. Read each table separately and compose in the flow (or extract a shared flow and `fx.call` it). Every table then appears explicitly in the Manifest's effect graph — which is what powers cache invalidation and PII masking.
|
|
286
|
+
|
|
287
|
+
</Accordion>
|
|
288
|
+
<Accordion title="Pagination shows duplicates when rows are inserted">
|
|
289
|
+
|
|
290
|
+
You are on offset paging. Switch `list` to keyset by setting `cursor` columns with a stable order (e.g. `[createdAt, id]`) — the page boundary becomes a row predicate, not a row count.
|
|
291
|
+
|
|
292
|
+
</Accordion>
|
|
293
|
+
<Accordion title="meta.total is slow on a big table">
|
|
294
|
+
|
|
295
|
+
`count: "exact"` runs `COUNT(*)` per page. Set `count: "none"` in the `list` options to skip it, or use keyset mode where totals are rarely needed.
|
|
296
|
+
|
|
297
|
+
</Accordion>
|
|
298
|
+
</Accordions>
|
|
299
|
+
|
|
300
|
+
## Learn more
|
|
301
|
+
|
|
302
|
+
- [Flow](/docs/elements/flow) — the `fx.store` session inside `do`
|
|
303
|
+
- [Console · Store](/docs/console/store) — browse data, cache keys, PII masking
|
|
304
|
+
- [CLI Reference](/docs/reference/cli) — `oke db push` · `generate` · `migrate`
|
|
305
|
+
|
|
306
|
+
## Next
|
|
307
|
+
|
|
308
|
+
<Cards>
|
|
309
|
+
<Card title="Clock" description="Continue to Clock." href="/docs/elements/clock" />
|
|
310
|
+
<Card
|
|
311
|
+
title="Introduction"
|
|
312
|
+
description="Eight elements overview."
|
|
313
|
+
href="/docs/get-started/introduction"
|
|
314
|
+
/>
|
|
315
|
+
<Card
|
|
316
|
+
title="Console"
|
|
317
|
+
description="Panels derived from the Manifest."
|
|
318
|
+
href="/docs/console/overview"
|
|
319
|
+
/>
|
|
320
|
+
</Cards>
|