okengine 0.14.1 → 0.16.0
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/package.json +3 -4
- package/site/content/docs/elements/ai.mdx +2 -1
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +3 -0
- package/site/content/docs/elements/flow.mdx +9 -0
- package/site/content/docs/elements/gate.mdx +80 -22
- package/site/content/docs/elements/signal.mdx +4 -4
- package/site/content/docs/elements/store.mdx +84 -27
- package/site/content/docs/elements/vault.mdx +4 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +28 -21
- package/site/content/docs/reference/fx.mdx +29 -18
- package/site/content/docs/reference/security.md +2 -2
- package/src/auth/api-key-sql.test.ts +47 -0
- package/src/auth/api-key-sql.ts +254 -0
- package/src/auth/api-keys.ts +273 -14
- package/src/auth/auth.test.ts +56 -0
- package/src/auth/bindings.ts +14 -5
- package/src/auth/config.ts +5 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/auth/index.ts +25 -1
- package/src/auth/schema.ts +8 -0
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/attach-host-console.test.ts +40 -2
- package/src/cli/attach-host-console.ts +9 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/effects-infer.ts +17 -2
- package/src/compiler/extract.test.ts +409 -33
- package/src/compiler/extract.ts +318 -67
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/access.test.ts +116 -8
- package/src/console/server/access.ts +70 -23
- package/src/console/server/app.ts +1 -0
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/dev-identities.test.ts +127 -0
- package/src/console/server/dev-identities.ts +229 -0
- package/src/console/server/flows-invoke.test.ts +21 -4
- package/src/console/server/flows.ts +233 -98
- package/src/console/server/gates.test.ts +24 -0
- package/src/console/server/gates.ts +8 -21
- package/src/console/server/index.ts +1 -0
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/live.test.ts +26 -1
- package/src/console/server/live.ts +4 -2
- package/src/console/server/operator-db.ts +2 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +37 -55
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
- package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
- package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
- package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
- package/src/console/ui-next/dist/index.html +12 -10
- package/src/console/ui-next/index.html +3 -3
- package/src/console/ui-next/src/client.ts +202 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
- package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
- package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
- package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
- package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
- package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
- package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
- package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
- package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
- package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
- package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
- package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
- package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
- package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
- package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
- package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
- package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
- package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
- package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
- package/src/console/ui-next/src/lib/document-meta.ts +2 -1
- package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
- package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
- package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
- package/src/console/ui-next/src/lib/shortcut.ts +3 -0
- package/src/console/ui-next/src/router.tsx +10 -1
- package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
- package/src/console/ui-next/ui-next-seed-store.ts +43 -15
- package/src/console/xss.gate.test.ts +1 -0
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +3 -0
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/types.ts +10 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +38 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/store/cache.ts +1 -1
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +18 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +260 -41
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-select.types.test.ts +71 -0
- package/src/elements/store/sql-session.ts +135 -35
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +17 -0
- package/src/http.ts +20 -3
- package/src/index.ts +11 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/api-key-host-persist.test.ts +143 -0
- package/src/kernel/app.ts +145 -16
- package/src/kernel/auth-resolve.ts +10 -5
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +49 -9
- package/src/kernel/boot.test.ts +5 -5
- package/src/kernel/boot.ts +2 -2
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +12 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx-auth-keys.test.ts +88 -0
- package/src/kernel/fx-auth-keys.ts +198 -0
- package/src/kernel/fx.test.ts +17 -1
- package/src/kernel/fx.ts +59 -18
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +4 -4
- package/src/kernel/pipeline-api-key.test.ts +192 -7
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/pipeline.ts +4 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/types.ts +22 -3
- package/src/release/build-lib.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +8 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
|
@@ -27,12 +27,16 @@ import { maskPiiValue, maskWideEventForConsole, piiFieldNamesFromManifest } from
|
|
|
27
27
|
import { ConsoleRunsQueryError, RUNS_QUERY_PII_GAP, RUNS_QUERY_TIMEOUT_MS } from "./runs-query.ts";
|
|
28
28
|
import { DuckQueryTimeoutError } from "../../runs/duckdb.ts";
|
|
29
29
|
import { ClockResourceNotFoundError, ScheduleNotOverridableError } from "./clock.ts";
|
|
30
|
+
import { isoAt } from "./iso-at.ts";
|
|
30
31
|
import { createFileDiff, emitStructuralDiff } from "./structural.ts";
|
|
31
32
|
import type { ConsoleState } from "./state.ts";
|
|
32
33
|
import { consoleFailureMessage } from "./i18n.ts";
|
|
33
34
|
import { CONSOLE_PASSWORD_POLICY } from "../password-policy.ts";
|
|
34
35
|
import { PUBLIC_CONSOLE_FLOWS } from "./public-flows.ts";
|
|
35
36
|
import { resolveInvokeAs } from "./invoke-user-flow.ts";
|
|
37
|
+
import { resolveRlsIdentity } from "../../elements/store.ts";
|
|
38
|
+
import type { RlsIdentity } from "../../drivers/pg-rls.ts";
|
|
39
|
+
import { accessUserCeiling } from "./access.ts";
|
|
36
40
|
import { isKnownConsoleSqlGate, StoreFileNotFoundError, tenancyDeclared } from "./store.ts";
|
|
37
41
|
import {
|
|
38
42
|
PG_STAT_STATEMENTS_NOT_CREATED,
|
|
@@ -152,7 +156,7 @@ const TracesReplayOut = z.object({
|
|
|
152
156
|
ok: z.literal(true),
|
|
153
157
|
rootId: z.string(),
|
|
154
158
|
dryRun: z.boolean(),
|
|
155
|
-
at: z.
|
|
159
|
+
at: z.string(),
|
|
156
160
|
flow: z.string(),
|
|
157
161
|
});
|
|
158
162
|
|
|
@@ -260,7 +264,7 @@ const SignalsReplayOut = z.object({
|
|
|
260
264
|
messageId: z.string().optional(),
|
|
261
265
|
}),
|
|
262
266
|
),
|
|
263
|
-
at: z.
|
|
267
|
+
at: z.string(),
|
|
264
268
|
});
|
|
265
269
|
|
|
266
270
|
const SignalsDiscardIn = z.object({
|
|
@@ -273,7 +277,7 @@ const SignalsDiscardIn = z.object({
|
|
|
273
277
|
const SignalsDiscardOut = z.object({
|
|
274
278
|
ok: z.literal(true),
|
|
275
279
|
discarded: z.number(),
|
|
276
|
-
at: z.
|
|
280
|
+
at: z.string(),
|
|
277
281
|
});
|
|
278
282
|
|
|
279
283
|
const ActionPingIn = z.object({
|
|
@@ -283,7 +287,7 @@ const ActionPingIn = z.object({
|
|
|
283
287
|
const ActionPingOut = z.object({
|
|
284
288
|
ok: z.literal(true),
|
|
285
289
|
note: z.string().optional(),
|
|
286
|
-
at: z.
|
|
290
|
+
at: z.string(),
|
|
287
291
|
});
|
|
288
292
|
|
|
289
293
|
const StructuralIn = z.object({
|
|
@@ -354,6 +358,13 @@ const InvokeOut = z.object({
|
|
|
354
358
|
durationMs: z.number().optional(),
|
|
355
359
|
peakTier: z.enum(["none", "reads", "writes", "emits", "external", "capabilities"]),
|
|
356
360
|
auditedAt: z.number(),
|
|
361
|
+
rls: z
|
|
362
|
+
.object({
|
|
363
|
+
gate: z.string().nullable(),
|
|
364
|
+
userId: z.string(),
|
|
365
|
+
applied: z.boolean(),
|
|
366
|
+
})
|
|
367
|
+
.optional(),
|
|
357
368
|
});
|
|
358
369
|
|
|
359
370
|
const SetupClosed = z.object({ reason: z.string() });
|
|
@@ -504,7 +515,7 @@ const ChannelRevealOut = z.object({
|
|
|
504
515
|
ok: z.literal(true),
|
|
505
516
|
id: z.string(),
|
|
506
517
|
to: z.string(),
|
|
507
|
-
at: z.
|
|
518
|
+
at: z.string(),
|
|
508
519
|
});
|
|
509
520
|
|
|
510
521
|
const ChannelSendTestIn = z.object({
|
|
@@ -521,7 +532,7 @@ const ChannelSendTestOut = z.object({
|
|
|
521
532
|
messageId: z.string(),
|
|
522
533
|
status: z.string(),
|
|
523
534
|
chain: z.string(),
|
|
524
|
-
at: z.
|
|
535
|
+
at: z.string(),
|
|
525
536
|
});
|
|
526
537
|
|
|
527
538
|
const VaultNotFound = z.object({
|
|
@@ -634,7 +645,7 @@ const ClockRunNowOut = z.object({
|
|
|
634
645
|
ok: z.literal(true),
|
|
635
646
|
name: z.string(),
|
|
636
647
|
ran: z.boolean(),
|
|
637
|
-
at: z.
|
|
648
|
+
at: z.string(),
|
|
638
649
|
});
|
|
639
650
|
|
|
640
651
|
const ClockPauseIn = z.object({ name: z.string().min(1) });
|
|
@@ -642,7 +653,7 @@ const ClockPauseOut = z.object({
|
|
|
642
653
|
ok: z.literal(true),
|
|
643
654
|
name: z.string(),
|
|
644
655
|
status: z.string(),
|
|
645
|
-
at: z.
|
|
656
|
+
at: z.string(),
|
|
646
657
|
});
|
|
647
658
|
|
|
648
659
|
const ClockEditIn = z.object({
|
|
@@ -656,7 +667,7 @@ const ClockEditOut = z.object({
|
|
|
656
667
|
name: z.string(),
|
|
657
668
|
effectiveCron: z.string().optional(),
|
|
658
669
|
effectiveEvery: z.string().optional(),
|
|
659
|
-
at: z.
|
|
670
|
+
at: z.string(),
|
|
660
671
|
});
|
|
661
672
|
|
|
662
673
|
const ClockWakeEarlyIn = z.object({ runId: z.string().min(1) });
|
|
@@ -665,7 +676,7 @@ const ClockWakeEarlyOut = z.object({
|
|
|
665
676
|
runId: z.string(),
|
|
666
677
|
wakeAt: z.number(),
|
|
667
678
|
resumed: z.boolean(),
|
|
668
|
-
at: z.
|
|
679
|
+
at: z.string(),
|
|
669
680
|
});
|
|
670
681
|
|
|
671
682
|
const VaultResolutionStepOut = z.object({
|
|
@@ -746,7 +757,7 @@ const VaultWriteOut = z.object({
|
|
|
746
757
|
ok: z.literal(true),
|
|
747
758
|
name: z.string(),
|
|
748
759
|
fingerprint: z.string().nullable(),
|
|
749
|
-
at: z.
|
|
760
|
+
at: z.string(),
|
|
750
761
|
});
|
|
751
762
|
|
|
752
763
|
const VaultAuditRowOut = z.object({
|
|
@@ -780,7 +791,7 @@ const VaultRotateMasterOut = z.object({
|
|
|
780
791
|
kekVersion: z.number(),
|
|
781
792
|
remaining: z.number(),
|
|
782
793
|
masterKey: z.string().nullable(),
|
|
783
|
-
at: z.
|
|
794
|
+
at: z.string(),
|
|
784
795
|
});
|
|
785
796
|
|
|
786
797
|
const AiMetricOut = z.object({
|
|
@@ -1154,6 +1165,22 @@ const GatesPowersOut = z.object({
|
|
|
1154
1165
|
deniedFlowIds: z.array(z.string()),
|
|
1155
1166
|
});
|
|
1156
1167
|
|
|
1168
|
+
const AccessKeyRowOut = z.object({
|
|
1169
|
+
id: z.string(),
|
|
1170
|
+
name: z.string(),
|
|
1171
|
+
plane: z.enum(["user", "operator"]),
|
|
1172
|
+
creatorId: z.string(),
|
|
1173
|
+
creatorScopes: z.array(z.string()),
|
|
1174
|
+
scopes: z.array(z.string()),
|
|
1175
|
+
createdAt: z.number(),
|
|
1176
|
+
lastUsedAt: z.number().nullable(),
|
|
1177
|
+
expiresAt: z.number().nullable(),
|
|
1178
|
+
revokedAt: z.number().nullable(),
|
|
1179
|
+
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable(),
|
|
1180
|
+
ipAllowlist: z.array(z.string()),
|
|
1181
|
+
unused90d: z.boolean(),
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1157
1184
|
const AccessPlaneSection = z.object({
|
|
1158
1185
|
plane: z.enum(["user", "operator"]),
|
|
1159
1186
|
operators: z
|
|
@@ -1192,21 +1219,7 @@ const AccessPlaneSection = z.object({
|
|
|
1192
1219
|
memberCount: z.number(),
|
|
1193
1220
|
}),
|
|
1194
1221
|
),
|
|
1195
|
-
keys: z.array(
|
|
1196
|
-
z.object({
|
|
1197
|
-
id: z.string(),
|
|
1198
|
-
name: z.string(),
|
|
1199
|
-
plane: z.enum(["user", "operator"]),
|
|
1200
|
-
scopes: z.array(z.string()),
|
|
1201
|
-
createdAt: z.number(),
|
|
1202
|
-
lastUsedAt: z.number().nullable(),
|
|
1203
|
-
expiresAt: z.number().nullable(),
|
|
1204
|
-
revokedAt: z.number().nullable(),
|
|
1205
|
-
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable(),
|
|
1206
|
-
ipAllowlist: z.array(z.string()),
|
|
1207
|
-
unused90d: z.boolean(),
|
|
1208
|
-
}),
|
|
1209
|
-
),
|
|
1222
|
+
keys: z.array(AccessKeyRowOut),
|
|
1210
1223
|
invites: z
|
|
1211
1224
|
.array(
|
|
1212
1225
|
z.object({
|
|
@@ -1226,21 +1239,7 @@ const AccessListOut = z.object({
|
|
|
1226
1239
|
operatorPlane: AccessPlaneSection,
|
|
1227
1240
|
userPlane: AccessPlaneSection,
|
|
1228
1241
|
hygiene: z.object({
|
|
1229
|
-
unusedKeys: z.array(
|
|
1230
|
-
z.object({
|
|
1231
|
-
id: z.string(),
|
|
1232
|
-
name: z.string(),
|
|
1233
|
-
plane: z.enum(["user", "operator"]),
|
|
1234
|
-
scopes: z.array(z.string()),
|
|
1235
|
-
createdAt: z.number(),
|
|
1236
|
-
lastUsedAt: z.number().nullable(),
|
|
1237
|
-
expiresAt: z.number().nullable(),
|
|
1238
|
-
revokedAt: z.number().nullable(),
|
|
1239
|
-
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable(),
|
|
1240
|
-
ipAllowlist: z.array(z.string()),
|
|
1241
|
-
unused90d: z.boolean(),
|
|
1242
|
-
}),
|
|
1243
|
-
),
|
|
1242
|
+
unusedKeys: z.array(AccessKeyRowOut),
|
|
1244
1243
|
neverSignedInOperators: z.array(
|
|
1245
1244
|
z.object({
|
|
1246
1245
|
id: z.string(),
|
|
@@ -1307,25 +1306,28 @@ const AccessCreateKeyIn = z.object({
|
|
|
1307
1306
|
plane: z.enum(["user", "operator"]),
|
|
1308
1307
|
name: z.string().min(1),
|
|
1309
1308
|
scopes: z.array(z.string()),
|
|
1309
|
+
/** Required for user-plane keys — issuer whose grants are the ceiling. */
|
|
1310
|
+
creatorUserId: z.string().min(1).optional(),
|
|
1311
|
+
expiresAt: z.number().nullable().optional(),
|
|
1312
|
+
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable().optional(),
|
|
1313
|
+
ipAllowlist: z.array(z.string()).optional(),
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
const AccessUpdateKeyIn = z.object({
|
|
1317
|
+
keyId: z.string().min(1),
|
|
1318
|
+
name: z.string().min(1).optional(),
|
|
1319
|
+
scopes: z.array(z.string()).optional(),
|
|
1310
1320
|
expiresAt: z.number().nullable().optional(),
|
|
1311
1321
|
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable().optional(),
|
|
1312
1322
|
ipAllowlist: z.array(z.string()).optional(),
|
|
1313
1323
|
});
|
|
1314
1324
|
|
|
1325
|
+
const AccessUpdateKeyOut = z.object({
|
|
1326
|
+
key: AccessKeyRowOut,
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1315
1329
|
const AccessCreateKeyOut = z.object({
|
|
1316
|
-
key:
|
|
1317
|
-
id: z.string(),
|
|
1318
|
-
name: z.string(),
|
|
1319
|
-
plane: z.enum(["user", "operator"]),
|
|
1320
|
-
scopes: z.array(z.string()),
|
|
1321
|
-
createdAt: z.number(),
|
|
1322
|
-
lastUsedAt: z.number().nullable(),
|
|
1323
|
-
expiresAt: z.number().nullable(),
|
|
1324
|
-
revokedAt: z.number().nullable(),
|
|
1325
|
-
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable(),
|
|
1326
|
-
ipAllowlist: z.array(z.string()),
|
|
1327
|
-
unused90d: z.boolean(),
|
|
1328
|
-
}),
|
|
1330
|
+
key: AccessKeyRowOut,
|
|
1329
1331
|
/** Raw secret — returned exactly once. */
|
|
1330
1332
|
secret: z.string(),
|
|
1331
1333
|
});
|
|
@@ -1337,19 +1339,7 @@ const AccessRevokeKeyIn = z.object({
|
|
|
1337
1339
|
});
|
|
1338
1340
|
|
|
1339
1341
|
const AccessRevokeKeyOut = z.object({
|
|
1340
|
-
key:
|
|
1341
|
-
id: z.string(),
|
|
1342
|
-
name: z.string(),
|
|
1343
|
-
plane: z.enum(["user", "operator"]),
|
|
1344
|
-
scopes: z.array(z.string()),
|
|
1345
|
-
createdAt: z.number(),
|
|
1346
|
-
lastUsedAt: z.number().nullable(),
|
|
1347
|
-
expiresAt: z.number().nullable(),
|
|
1348
|
-
revokedAt: z.number().nullable(),
|
|
1349
|
-
rateLimit: z.object({ max: z.number(), per: z.string() }).nullable(),
|
|
1350
|
-
ipAllowlist: z.array(z.string()),
|
|
1351
|
-
unused90d: z.boolean(),
|
|
1352
|
-
}),
|
|
1342
|
+
key: AccessKeyRowOut,
|
|
1353
1343
|
blastRadius: AccessKeyBlastOut,
|
|
1354
1344
|
});
|
|
1355
1345
|
|
|
@@ -1422,6 +1412,7 @@ const StoreListOut = z.object({
|
|
|
1422
1412
|
piiColumns: z.array(z.string()),
|
|
1423
1413
|
columnDescriptions: z.record(z.string(), z.string()),
|
|
1424
1414
|
rls: z.boolean().optional(),
|
|
1415
|
+
rlsPolicyCount: z.number().int().nonnegative().optional(),
|
|
1425
1416
|
}),
|
|
1426
1417
|
),
|
|
1427
1418
|
replicaLagMs: z.number().nullable(),
|
|
@@ -1445,6 +1436,17 @@ const StoreListOut = z.object({
|
|
|
1445
1436
|
),
|
|
1446
1437
|
});
|
|
1447
1438
|
|
|
1439
|
+
const StoreRlsAs = {
|
|
1440
|
+
asGate: z.string().min(1).optional(),
|
|
1441
|
+
asUserId: z.string().min(1).optional(),
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
const StoreRlsOut = z.object({
|
|
1445
|
+
gate: z.string().nullable(),
|
|
1446
|
+
userId: z.string(),
|
|
1447
|
+
applied: z.boolean(),
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1448
1450
|
const StoreQueryIn = z.object({
|
|
1449
1451
|
ref: z.string().min(1),
|
|
1450
1452
|
child: z.string().optional(),
|
|
@@ -1455,6 +1457,7 @@ const StoreQueryIn = z.object({
|
|
|
1455
1457
|
q: z.string().optional(),
|
|
1456
1458
|
topK: z.number().min(1).max(100).optional(),
|
|
1457
1459
|
revealPii: z.boolean().optional(),
|
|
1460
|
+
...StoreRlsAs,
|
|
1458
1461
|
});
|
|
1459
1462
|
|
|
1460
1463
|
const StoreQueryOut = z.object({
|
|
@@ -1483,6 +1486,7 @@ const StoreQueryOut = z.object({
|
|
|
1483
1486
|
.optional(),
|
|
1484
1487
|
masked: z.boolean(),
|
|
1485
1488
|
routedRole: z.enum(["primary", "replica"]).optional(),
|
|
1489
|
+
rls: StoreRlsOut.optional(),
|
|
1486
1490
|
});
|
|
1487
1491
|
|
|
1488
1492
|
const StoreRevealIn = z.object({
|
|
@@ -1496,7 +1500,7 @@ const StoreRevealIn = z.object({
|
|
|
1496
1500
|
const StoreRevealOut = z.object({
|
|
1497
1501
|
ok: z.literal(true),
|
|
1498
1502
|
value: z.unknown(),
|
|
1499
|
-
at: z.
|
|
1503
|
+
at: z.string(),
|
|
1500
1504
|
});
|
|
1501
1505
|
|
|
1502
1506
|
const StoreFileGetIn = z.object({
|
|
@@ -1527,6 +1531,7 @@ const StoreEditIn = z.object({
|
|
|
1527
1531
|
reason: z.string().optional(),
|
|
1528
1532
|
/** When false/omitted, returns willNotFire without applying. */
|
|
1529
1533
|
commit: z.boolean().optional(),
|
|
1534
|
+
...StoreRlsAs,
|
|
1530
1535
|
});
|
|
1531
1536
|
|
|
1532
1537
|
const StoreEditOut = z.object({
|
|
@@ -1540,7 +1545,8 @@ const StoreEditOut = z.object({
|
|
|
1540
1545
|
resource: z.string(),
|
|
1541
1546
|
}),
|
|
1542
1547
|
),
|
|
1543
|
-
at: z.
|
|
1548
|
+
at: z.string(),
|
|
1549
|
+
rls: StoreRlsOut.optional(),
|
|
1544
1550
|
});
|
|
1545
1551
|
|
|
1546
1552
|
const StoreDeleteIn = z.object({
|
|
@@ -1556,7 +1562,7 @@ const StoreDeleteIn = z.object({
|
|
|
1556
1562
|
const StoreDeleteOut = z.object({
|
|
1557
1563
|
ok: z.literal(true),
|
|
1558
1564
|
deleted: z.number(),
|
|
1559
|
-
at: z.
|
|
1565
|
+
at: z.string(),
|
|
1560
1566
|
});
|
|
1561
1567
|
|
|
1562
1568
|
const StorePurgeIn = z.object({
|
|
@@ -1568,7 +1574,7 @@ const StorePurgeIn = z.object({
|
|
|
1568
1574
|
const StorePurgeOut = z.object({
|
|
1569
1575
|
ok: z.literal(true),
|
|
1570
1576
|
keys: z.array(z.string()),
|
|
1571
|
-
at: z.
|
|
1577
|
+
at: z.string(),
|
|
1572
1578
|
});
|
|
1573
1579
|
|
|
1574
1580
|
const StoreSqlIn = z.object({
|
|
@@ -1578,6 +1584,7 @@ const StoreSqlIn = z.object({
|
|
|
1578
1584
|
allowWrite: z.boolean().optional(),
|
|
1579
1585
|
revealPii: z.boolean().optional(),
|
|
1580
1586
|
asGate: z.string().min(1).optional(),
|
|
1587
|
+
asUserId: z.string().min(1).optional(),
|
|
1581
1588
|
});
|
|
1582
1589
|
|
|
1583
1590
|
const StoreSqlOut = z.object({
|
|
@@ -1585,7 +1592,9 @@ const StoreSqlOut = z.object({
|
|
|
1585
1592
|
masked: z.boolean(),
|
|
1586
1593
|
routedRole: z.enum(["primary", "replica"]),
|
|
1587
1594
|
asGate: z.string().nullable(),
|
|
1595
|
+
asUserId: z.string().nullable(),
|
|
1588
1596
|
gateApplied: z.boolean(),
|
|
1597
|
+
rls: StoreRlsOut.optional(),
|
|
1589
1598
|
});
|
|
1590
1599
|
|
|
1591
1600
|
const StoreSqlStatsIn = z.object({
|
|
@@ -1749,7 +1758,7 @@ const StorePreviewOut = z.object({
|
|
|
1749
1758
|
resource: z.string(),
|
|
1750
1759
|
}),
|
|
1751
1760
|
),
|
|
1752
|
-
at: z.
|
|
1761
|
+
at: z.string(),
|
|
1753
1762
|
});
|
|
1754
1763
|
|
|
1755
1764
|
/**
|
|
@@ -1826,6 +1835,7 @@ export function createConsoleBindings(state: ConsoleState): {
|
|
|
1826
1835
|
readonly createKey: AnyFlowDef;
|
|
1827
1836
|
readonly revokeKey: AnyFlowDef;
|
|
1828
1837
|
readonly rotateKey: AnyFlowDef;
|
|
1838
|
+
readonly updateKey: AnyFlowDef;
|
|
1829
1839
|
readonly setRoleGrants: AnyFlowDef;
|
|
1830
1840
|
};
|
|
1831
1841
|
readonly diff: {
|
|
@@ -1899,6 +1909,7 @@ export function createConsoleBindings(state: ConsoleState): {
|
|
|
1899
1909
|
const accessCreateKeyFlow = createAccessCreateKey(state);
|
|
1900
1910
|
const accessRevokeKeyFlow = createAccessRevokeKey(state);
|
|
1901
1911
|
const accessRotateKeyFlow = createAccessRotateKey(state);
|
|
1912
|
+
const accessUpdateKeyFlow = createAccessUpdateKey(state);
|
|
1902
1913
|
const accessSetRoleGrantsFlow = createAccessSetRoleGrants(state);
|
|
1903
1914
|
const diffList = createDiffList(state);
|
|
1904
1915
|
const pluginsList = createPluginsList(state);
|
|
@@ -1961,6 +1972,7 @@ export function createConsoleBindings(state: ConsoleState): {
|
|
|
1961
1972
|
bindHttp(http.post("/console/access/keys"), accessCreateKeyFlow),
|
|
1962
1973
|
bindHttp(http.post("/console/access/keys/revoke"), accessRevokeKeyFlow),
|
|
1963
1974
|
bindHttp(http.post("/console/access/keys/rotate"), accessRotateKeyFlow),
|
|
1975
|
+
bindHttp(http.post("/console/access/keys/update"), accessUpdateKeyFlow),
|
|
1964
1976
|
bindHttp(http.post("/console/access/roles/grants"), accessSetRoleGrantsFlow),
|
|
1965
1977
|
bindHttp(http.get("/console/diff"), diffList),
|
|
1966
1978
|
bindHttp(http.get("/console/plugins"), pluginsList),
|
|
@@ -2030,6 +2042,7 @@ export function createConsoleBindings(state: ConsoleState): {
|
|
|
2030
2042
|
createKey: accessCreateKeyFlow,
|
|
2031
2043
|
revokeKey: accessRevokeKeyFlow,
|
|
2032
2044
|
rotateKey: accessRotateKeyFlow,
|
|
2045
|
+
updateKey: accessUpdateKeyFlow,
|
|
2033
2046
|
setRoleGrants: accessSetRoleGrantsFlow,
|
|
2034
2047
|
},
|
|
2035
2048
|
diff: { list: diffList },
|
|
@@ -2362,7 +2375,7 @@ function createTracesReplay(state: ConsoleState) {
|
|
|
2362
2375
|
ok: true as const,
|
|
2363
2376
|
rootId: input.rootId,
|
|
2364
2377
|
dryRun,
|
|
2365
|
-
at: Date.now(),
|
|
2378
|
+
at: isoAt(Date.now()),
|
|
2366
2379
|
flow: root.flow,
|
|
2367
2380
|
};
|
|
2368
2381
|
},
|
|
@@ -2443,7 +2456,7 @@ function createSignalsDiscard(state: ConsoleState) {
|
|
|
2443
2456
|
return {
|
|
2444
2457
|
ok: true as const,
|
|
2445
2458
|
discarded: result.discarded,
|
|
2446
|
-
at: state.now(),
|
|
2459
|
+
at: isoAt(state.now()),
|
|
2447
2460
|
};
|
|
2448
2461
|
},
|
|
2449
2462
|
});
|
|
@@ -2517,7 +2530,7 @@ async function runSignalReplay(
|
|
|
2517
2530
|
dryRun: result.dryRun,
|
|
2518
2531
|
results: [...result.results],
|
|
2519
2532
|
wouldHaveFired: [...result.wouldHaveFired],
|
|
2520
|
-
at: state.now(),
|
|
2533
|
+
at: isoAt(state.now()),
|
|
2521
2534
|
};
|
|
2522
2535
|
}
|
|
2523
2536
|
|
|
@@ -2562,7 +2575,7 @@ function createActionPing(state: ConsoleState) {
|
|
|
2562
2575
|
operatorId: fx.operator.id,
|
|
2563
2576
|
note: input.note,
|
|
2564
2577
|
});
|
|
2565
|
-
return { ok: true as const, note: input.note, at: state.now() };
|
|
2578
|
+
return { ok: true as const, note: input.note, at: isoAt(state.now()) };
|
|
2566
2579
|
},
|
|
2567
2580
|
});
|
|
2568
2581
|
}
|
|
@@ -2664,6 +2677,7 @@ function createFlowsInvoke(state: ConsoleState) {
|
|
|
2664
2677
|
...(assumed.bypassGates ? { bypassGates: true } : {}),
|
|
2665
2678
|
...(input.reason !== undefined ? { reason: input.reason } : {}),
|
|
2666
2679
|
...(input.revealPii === true ? { revealPii: true } : {}),
|
|
2680
|
+
...(assumed.rls ? { rls: assumed.rls } : {}),
|
|
2667
2681
|
});
|
|
2668
2682
|
|
|
2669
2683
|
const reveal = input.revealPii === true;
|
|
@@ -2716,6 +2730,11 @@ function createFlowsInvoke(state: ConsoleState) {
|
|
|
2716
2730
|
...(host.durationMs !== undefined ? { durationMs: host.durationMs } : {}),
|
|
2717
2731
|
peakTier,
|
|
2718
2732
|
auditedAt: state.now(),
|
|
2733
|
+
rls: {
|
|
2734
|
+
gate: assumed.rls?.gate ?? null,
|
|
2735
|
+
userId: assumed.rls?.userId ?? "",
|
|
2736
|
+
applied: assumed.rls !== null,
|
|
2737
|
+
},
|
|
2719
2738
|
};
|
|
2720
2739
|
},
|
|
2721
2740
|
});
|
|
@@ -2806,6 +2825,32 @@ function createStoreList(state: ConsoleState) {
|
|
|
2806
2825
|
});
|
|
2807
2826
|
}
|
|
2808
2827
|
|
|
2828
|
+
function consoleStoreRls(
|
|
2829
|
+
state: ConsoleState,
|
|
2830
|
+
input: { readonly asGate?: string; readonly asUserId?: string },
|
|
2831
|
+
):
|
|
2832
|
+
| { readonly ok: true; readonly rls: RlsIdentity | null }
|
|
2833
|
+
| { readonly ok: false; readonly reason: string } {
|
|
2834
|
+
if (input.asGate !== undefined && !isKnownConsoleSqlGate(state.manifest, input.asGate)) {
|
|
2835
|
+
return { ok: false, reason: `unknown gate: ${input.asGate}` };
|
|
2836
|
+
}
|
|
2837
|
+
if (input.asUserId !== undefined) {
|
|
2838
|
+
const identity = state.identities.find((row) => row.id === input.asUserId);
|
|
2839
|
+
if (!identity || identity.status === "disabled") {
|
|
2840
|
+
return { ok: false, reason: `unknown identity: ${input.asUserId}` };
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
return {
|
|
2844
|
+
ok: true,
|
|
2845
|
+
rls: resolveRlsIdentity({
|
|
2846
|
+
asGate: input.asGate,
|
|
2847
|
+
asUserId: input.asUserId,
|
|
2848
|
+
identities: state.identities,
|
|
2849
|
+
manifest: state.manifest,
|
|
2850
|
+
}),
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2809
2854
|
function createStoreQuery(state: ConsoleState) {
|
|
2810
2855
|
return flow("console.store.query", {
|
|
2811
2856
|
plane: "operator",
|
|
@@ -2824,6 +2869,8 @@ function createStoreQuery(state: ConsoleState) {
|
|
|
2824
2869
|
tenant: input.tenant,
|
|
2825
2870
|
});
|
|
2826
2871
|
}
|
|
2872
|
+
const assumed = consoleStoreRls(state, input);
|
|
2873
|
+
if (!assumed.ok) return fail("StoreNotFound", { ref: assumed.reason });
|
|
2827
2874
|
return state.queryStore({
|
|
2828
2875
|
ref: input.ref as ResourceRef,
|
|
2829
2876
|
child: input.child,
|
|
@@ -2834,6 +2881,9 @@ function createStoreQuery(state: ConsoleState) {
|
|
|
2834
2881
|
q: input.q,
|
|
2835
2882
|
topK: input.topK,
|
|
2836
2883
|
revealPii: input.revealPii === true,
|
|
2884
|
+
...(input.asGate !== undefined ? { asGate: input.asGate } : {}),
|
|
2885
|
+
...(input.asUserId !== undefined ? { asUserId: input.asUserId } : {}),
|
|
2886
|
+
...(assumed.rls ? { rls: assumed.rls } : {}),
|
|
2837
2887
|
});
|
|
2838
2888
|
},
|
|
2839
2889
|
});
|
|
@@ -2869,7 +2919,7 @@ function createStoreReveal(state: ConsoleState) {
|
|
|
2869
2919
|
return {
|
|
2870
2920
|
ok: true as const,
|
|
2871
2921
|
value: row[input.column],
|
|
2872
|
-
at: state.now(),
|
|
2922
|
+
at: isoAt(state.now()),
|
|
2873
2923
|
};
|
|
2874
2924
|
},
|
|
2875
2925
|
});
|
|
@@ -2922,6 +2972,14 @@ function createStoreEdit(state: ConsoleState) {
|
|
|
2922
2972
|
const tenantFail = requireTenantIfDeclared(state, input.tenant);
|
|
2923
2973
|
if (tenantFail) return tenantFail;
|
|
2924
2974
|
|
|
2975
|
+
const assumed = consoleStoreRls(state, input);
|
|
2976
|
+
if (!assumed.ok) return fail("StoreNotFound", { ref: assumed.reason });
|
|
2977
|
+
const rlsFields = {
|
|
2978
|
+
...(input.asGate !== undefined ? { asGate: input.asGate } : {}),
|
|
2979
|
+
...(input.asUserId !== undefined ? { asUserId: input.asUserId } : {}),
|
|
2980
|
+
...(assumed.rls ? { rls: assumed.rls } : {}),
|
|
2981
|
+
};
|
|
2982
|
+
|
|
2925
2983
|
if (!input.commit) {
|
|
2926
2984
|
// Return will-not-fire payload without applying — informational confirm.
|
|
2927
2985
|
const preview = await state.editStore(
|
|
@@ -2932,6 +2990,7 @@ function createStoreEdit(state: ConsoleState) {
|
|
|
2932
2990
|
id: input.id,
|
|
2933
2991
|
key: input.key,
|
|
2934
2992
|
patch: input.patch,
|
|
2993
|
+
...rlsFields,
|
|
2935
2994
|
},
|
|
2936
2995
|
{ dryRun: true },
|
|
2937
2996
|
);
|
|
@@ -2941,7 +3000,8 @@ function createStoreEdit(state: ConsoleState) {
|
|
|
2941
3000
|
applied: false,
|
|
2942
3001
|
willNotFire: preview.willNotFire,
|
|
2943
3002
|
wouldHaveFired: [...preview.wouldHaveFired],
|
|
2944
|
-
at: state.now(),
|
|
3003
|
+
at: isoAt(state.now()),
|
|
3004
|
+
...(preview.rls ? { rls: preview.rls } : {}),
|
|
2945
3005
|
};
|
|
2946
3006
|
}
|
|
2947
3007
|
|
|
@@ -2962,6 +3022,7 @@ function createStoreEdit(state: ConsoleState) {
|
|
|
2962
3022
|
id: input.id,
|
|
2963
3023
|
key: input.key,
|
|
2964
3024
|
patch: input.patch,
|
|
3025
|
+
...rlsFields,
|
|
2965
3026
|
},
|
|
2966
3027
|
{ dryRun: false },
|
|
2967
3028
|
);
|
|
@@ -2978,7 +3039,8 @@ function createStoreEdit(state: ConsoleState) {
|
|
|
2978
3039
|
applied: result.applied,
|
|
2979
3040
|
willNotFire: result.willNotFire,
|
|
2980
3041
|
wouldHaveFired: [...result.wouldHaveFired],
|
|
2981
|
-
at: state.now(),
|
|
3042
|
+
at: isoAt(state.now()),
|
|
3043
|
+
...(result.rls ? { rls: result.rls } : {}),
|
|
2982
3044
|
};
|
|
2983
3045
|
},
|
|
2984
3046
|
});
|
|
@@ -3018,7 +3080,7 @@ function createStoreDelete(state: ConsoleState) {
|
|
|
3018
3080
|
return {
|
|
3019
3081
|
ok: true as const,
|
|
3020
3082
|
deleted: result.deleted,
|
|
3021
|
-
at: state.now(),
|
|
3083
|
+
at: isoAt(state.now()),
|
|
3022
3084
|
};
|
|
3023
3085
|
},
|
|
3024
3086
|
});
|
|
@@ -3050,7 +3112,7 @@ function createStorePurgeCache(state: ConsoleState) {
|
|
|
3050
3112
|
return {
|
|
3051
3113
|
ok: true as const,
|
|
3052
3114
|
keys: [...result.keys],
|
|
3053
|
-
at: state.now(),
|
|
3115
|
+
at: isoAt(state.now()),
|
|
3054
3116
|
};
|
|
3055
3117
|
},
|
|
3056
3118
|
});
|
|
@@ -3074,9 +3136,8 @@ function createStoreSql(state: ConsoleState) {
|
|
|
3074
3136
|
tenant: input.tenant,
|
|
3075
3137
|
});
|
|
3076
3138
|
}
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
}
|
|
3139
|
+
const assumed = consoleStoreRls(state, input);
|
|
3140
|
+
if (!assumed.ok) return fail("StoreNotFound", { ref: assumed.reason });
|
|
3080
3141
|
if (input.asGate !== undefined) {
|
|
3081
3142
|
fx.log.info("console.store.sql.asGate", {
|
|
3082
3143
|
operatorId: fx.operator.id,
|
|
@@ -3090,6 +3151,8 @@ function createStoreSql(state: ConsoleState) {
|
|
|
3090
3151
|
revealPii: input.revealPii === true,
|
|
3091
3152
|
tenant: input.tenant,
|
|
3092
3153
|
...(input.asGate !== undefined ? { asGate: input.asGate } : {}),
|
|
3154
|
+
...(input.asUserId !== undefined ? { asUserId: input.asUserId } : {}),
|
|
3155
|
+
...(assumed.rls ? { rls: assumed.rls } : {}),
|
|
3093
3156
|
});
|
|
3094
3157
|
fx.log.info("console.store.sql", {
|
|
3095
3158
|
operatorId: fx.operator.id,
|
|
@@ -3097,6 +3160,7 @@ function createStoreSql(state: ConsoleState) {
|
|
|
3097
3160
|
allowWrite: input.allowWrite === true,
|
|
3098
3161
|
revealPii: input.revealPii === true,
|
|
3099
3162
|
asGate: input.asGate ?? null,
|
|
3163
|
+
asUserId: input.asUserId ?? null,
|
|
3100
3164
|
rowCount: result.rows.length,
|
|
3101
3165
|
});
|
|
3102
3166
|
return result;
|
|
@@ -3312,7 +3376,7 @@ function createStorePreview(state: ConsoleState) {
|
|
|
3312
3376
|
dryRun: true as const,
|
|
3313
3377
|
willNotFire: preview.willNotFire,
|
|
3314
3378
|
wouldHaveFired: [...preview.wouldHaveFired],
|
|
3315
|
-
at: state.now(),
|
|
3379
|
+
at: isoAt(state.now()),
|
|
3316
3380
|
};
|
|
3317
3381
|
} catch (err) {
|
|
3318
3382
|
if (err instanceof DryRunWriteIsolationError) {
|
|
@@ -3368,7 +3432,7 @@ function createVaultCreate(state: ConsoleState) {
|
|
|
3368
3432
|
ok: true as const,
|
|
3369
3433
|
name: result.name,
|
|
3370
3434
|
fingerprint: result.fingerprint,
|
|
3371
|
-
at: state.now(),
|
|
3435
|
+
at: isoAt(state.now()),
|
|
3372
3436
|
};
|
|
3373
3437
|
} catch (err) {
|
|
3374
3438
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -3408,7 +3472,7 @@ function createVaultSet(state: ConsoleState) {
|
|
|
3408
3472
|
ok: true as const,
|
|
3409
3473
|
name: result.name,
|
|
3410
3474
|
fingerprint: result.fingerprint,
|
|
3411
|
-
at: state.now(),
|
|
3475
|
+
at: isoAt(state.now()),
|
|
3412
3476
|
};
|
|
3413
3477
|
} catch (err) {
|
|
3414
3478
|
return fail("VaultNotFound", {
|
|
@@ -3446,7 +3510,7 @@ function createVaultRotate(state: ConsoleState) {
|
|
|
3446
3510
|
ok: true as const,
|
|
3447
3511
|
name: result.name,
|
|
3448
3512
|
fingerprint: result.fingerprint,
|
|
3449
|
-
at: state.now(),
|
|
3513
|
+
at: isoAt(state.now()),
|
|
3450
3514
|
};
|
|
3451
3515
|
} catch (err) {
|
|
3452
3516
|
return fail("VaultNotFound", {
|
|
@@ -3492,7 +3556,7 @@ function createVaultRotateMaster(state: ConsoleState) {
|
|
|
3492
3556
|
kekVersion: result.kekVersion,
|
|
3493
3557
|
remaining: result.remaining,
|
|
3494
3558
|
masterKey: result.masterKey,
|
|
3495
|
-
at: state.now(),
|
|
3559
|
+
at: isoAt(state.now()),
|
|
3496
3560
|
};
|
|
3497
3561
|
} catch (err) {
|
|
3498
3562
|
return mapVaultOperatorError(err);
|
|
@@ -3774,6 +3838,46 @@ function actorScopesOf(state: ConsoleState, operatorId: string): string[] {
|
|
|
3774
3838
|
return [...new Set([...fromRoles, "console:*"])];
|
|
3775
3839
|
}
|
|
3776
3840
|
|
|
3841
|
+
/**
|
|
3842
|
+
* Derive the issuer for a Console-minted key.
|
|
3843
|
+
* Operator-plane: the acting operator. User-plane: required `creatorUserId`.
|
|
3844
|
+
*
|
|
3845
|
+
* @param state - Console state
|
|
3846
|
+
* @param operatorId - Acting operator
|
|
3847
|
+
* @param input - Create payload
|
|
3848
|
+
*/
|
|
3849
|
+
export function resolveAccessKeyIssuer(
|
|
3850
|
+
state: ConsoleState,
|
|
3851
|
+
operatorId: string,
|
|
3852
|
+
input: { readonly plane: "user" | "operator"; readonly creatorUserId?: string },
|
|
3853
|
+
):
|
|
3854
|
+
| { readonly creatorId: string; readonly creatorScopes: readonly string[] }
|
|
3855
|
+
| { readonly error: string } {
|
|
3856
|
+
if (input.plane === "operator") {
|
|
3857
|
+
return { creatorId: operatorId, creatorScopes: actorScopesOf(state, operatorId) };
|
|
3858
|
+
}
|
|
3859
|
+
const userId = input.creatorUserId;
|
|
3860
|
+
if (!userId) {
|
|
3861
|
+
return { error: "creatorUserId is required for user-plane keys" };
|
|
3862
|
+
}
|
|
3863
|
+
const identity = state.identities.find((row) => row.id === userId);
|
|
3864
|
+
const roleIds = [...state.roleMembers.entries()]
|
|
3865
|
+
.filter(([, members]) => members.includes(userId))
|
|
3866
|
+
.map(([id]) => id);
|
|
3867
|
+
if (!identity && roleIds.length === 0) {
|
|
3868
|
+
return { error: `unknown user: ${userId}` };
|
|
3869
|
+
}
|
|
3870
|
+
return {
|
|
3871
|
+
creatorId: userId,
|
|
3872
|
+
creatorScopes: accessUserCeiling({
|
|
3873
|
+
userId,
|
|
3874
|
+
identityScopes: identity?.scopes,
|
|
3875
|
+
roles: state.roles,
|
|
3876
|
+
roleMembers: state.roleMembers,
|
|
3877
|
+
}),
|
|
3878
|
+
};
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3777
3881
|
function createAccessList(state: ConsoleState) {
|
|
3778
3882
|
return flow("console.access.list", {
|
|
3779
3883
|
plane: "operator",
|
|
@@ -3836,12 +3940,16 @@ function createAccessCreateKey(state: ConsoleState) {
|
|
|
3836
3940
|
do: async (input: z.infer<typeof AccessCreateKeyIn>, fx) => {
|
|
3837
3941
|
if (!fx.operator.id) return fail("AuthFailed", {});
|
|
3838
3942
|
try {
|
|
3943
|
+
const issued = resolveAccessKeyIssuer(state, fx.operator.id, input);
|
|
3944
|
+
if ("error" in issued) {
|
|
3945
|
+
return fail("AccessGrantDenied", { reason: issued.error });
|
|
3946
|
+
}
|
|
3839
3947
|
const created = await state.accessCreateKey({
|
|
3840
3948
|
plane: input.plane,
|
|
3841
3949
|
name: input.name,
|
|
3842
3950
|
scopes: input.scopes,
|
|
3843
|
-
creatorId:
|
|
3844
|
-
creatorScopes:
|
|
3951
|
+
creatorId: issued.creatorId,
|
|
3952
|
+
creatorScopes: issued.creatorScopes,
|
|
3845
3953
|
expiresAt: input.expiresAt,
|
|
3846
3954
|
rateLimit: input.rateLimit,
|
|
3847
3955
|
ipAllowlist: input.ipAllowlist,
|
|
@@ -3924,6 +4032,33 @@ function createAccessRotateKey(state: ConsoleState) {
|
|
|
3924
4032
|
});
|
|
3925
4033
|
}
|
|
3926
4034
|
|
|
4035
|
+
function createAccessUpdateKey(state: ConsoleState) {
|
|
4036
|
+
return flow("console.access.updateKey", {
|
|
4037
|
+
plane: "operator",
|
|
4038
|
+
in: AccessUpdateKeyIn,
|
|
4039
|
+
out: AccessUpdateKeyOut,
|
|
4040
|
+
errors: { AuthFailed, AccessKeyNotFound, AccessGrantDenied },
|
|
4041
|
+
do: async (input: z.infer<typeof AccessUpdateKeyIn>, fx) => {
|
|
4042
|
+
if (!fx.operator.id) return fail("AuthFailed", {});
|
|
4043
|
+
try {
|
|
4044
|
+
const key = await state.accessUpdateKey(input);
|
|
4045
|
+
if (!key) {
|
|
4046
|
+
return fail("AccessKeyNotFound", { keyId: input.keyId });
|
|
4047
|
+
}
|
|
4048
|
+
fx.log.info("console.access.updateKey", {
|
|
4049
|
+
keyId: key.id,
|
|
4050
|
+
operatorId: fx.operator.id,
|
|
4051
|
+
});
|
|
4052
|
+
return { key: { ...key } };
|
|
4053
|
+
} catch (err) {
|
|
4054
|
+
return fail("AccessGrantDenied", {
|
|
4055
|
+
reason: err instanceof Error ? err.message : "grant denied",
|
|
4056
|
+
});
|
|
4057
|
+
}
|
|
4058
|
+
},
|
|
4059
|
+
});
|
|
4060
|
+
}
|
|
4061
|
+
|
|
3927
4062
|
function createAccessSetRoleGrants(state: ConsoleState) {
|
|
3928
4063
|
return flow("console.access.setRoleGrants", {
|
|
3929
4064
|
plane: "operator",
|
|
@@ -4010,7 +4145,7 @@ function createClockRunNow(state: ConsoleState) {
|
|
|
4010
4145
|
ok: true as const,
|
|
4011
4146
|
name: input.name,
|
|
4012
4147
|
ran: result.ran,
|
|
4013
|
-
at: state.now(),
|
|
4148
|
+
at: isoAt(state.now()),
|
|
4014
4149
|
};
|
|
4015
4150
|
} catch (err) {
|
|
4016
4151
|
if (err instanceof ClockResourceNotFoundError) {
|
|
@@ -4040,7 +4175,7 @@ function createClockPause(state: ConsoleState) {
|
|
|
4040
4175
|
ok: true as const,
|
|
4041
4176
|
name: result.name,
|
|
4042
4177
|
status: result.status,
|
|
4043
|
-
at: state.now(),
|
|
4178
|
+
at: isoAt(state.now()),
|
|
4044
4179
|
};
|
|
4045
4180
|
} catch (err) {
|
|
4046
4181
|
if (err instanceof ClockResourceNotFoundError) {
|
|
@@ -4075,7 +4210,7 @@ function createClockEditSchedule(state: ConsoleState) {
|
|
|
4075
4210
|
name: result.name,
|
|
4076
4211
|
effectiveCron: result.effectiveCron,
|
|
4077
4212
|
effectiveEvery: result.effectiveEvery,
|
|
4078
|
-
at: state.now(),
|
|
4213
|
+
at: isoAt(state.now()),
|
|
4079
4214
|
};
|
|
4080
4215
|
} catch (err) {
|
|
4081
4216
|
if (err instanceof ScheduleNotOverridableError) {
|
|
@@ -4110,7 +4245,7 @@ function createClockWakeEarly(state: ConsoleState) {
|
|
|
4110
4245
|
runId: result.runId,
|
|
4111
4246
|
wakeAt: result.wakeAt,
|
|
4112
4247
|
resumed: result.resumed,
|
|
4113
|
-
at: state.now(),
|
|
4248
|
+
at: isoAt(state.now()),
|
|
4114
4249
|
};
|
|
4115
4250
|
} catch (err) {
|
|
4116
4251
|
if (err instanceof ClockResourceNotFoundError) {
|
|
@@ -4187,7 +4322,7 @@ function createChannelReveal(state: ConsoleState) {
|
|
|
4187
4322
|
ok: true as const,
|
|
4188
4323
|
id: revealed.id,
|
|
4189
4324
|
to: revealed.to,
|
|
4190
|
-
at: state.now(),
|
|
4325
|
+
at: isoAt(state.now()),
|
|
4191
4326
|
};
|
|
4192
4327
|
},
|
|
4193
4328
|
});
|
|
@@ -4234,7 +4369,7 @@ function createChannelSendTest(state: ConsoleState) {
|
|
|
4234
4369
|
});
|
|
4235
4370
|
return {
|
|
4236
4371
|
...result,
|
|
4237
|
-
at: state.now(),
|
|
4372
|
+
at: isoAt(state.now()),
|
|
4238
4373
|
};
|
|
4239
4374
|
} catch (err) {
|
|
4240
4375
|
return fail("ChannelNotFound", {
|