okengine 0.15.2 → 0.17.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.
Files changed (256) hide show
  1. package/manifest.v1.schema.json +21 -2
  2. package/package.json +1 -1
  3. package/site/content/docs/elements/clock.mdx +7 -0
  4. package/site/content/docs/elements/flow.mdx +20 -12
  5. package/site/content/docs/elements/gate.mdx +108 -3
  6. package/site/content/docs/elements/signal.mdx +46 -17
  7. package/site/content/docs/elements/store.mdx +36 -32
  8. package/site/content/docs/elements/vault.mdx +26 -0
  9. package/site/content/docs/get-started/project-structure.mdx +4 -4
  10. package/site/content/docs/reference/cli.md +1 -1
  11. package/site/content/docs/reference/client.mdx +75 -17
  12. package/site/content/docs/reference/configuration.mdx +7 -4
  13. package/site/content/docs/reference/errors.mdx +24 -17
  14. package/site/content/docs/reference/fx.mdx +39 -22
  15. package/site/content/docs/reference/security.md +1 -1
  16. package/src/auth/api-key-sql.test.ts +47 -0
  17. package/src/auth/api-key-sql.ts +261 -0
  18. package/src/auth/api-keys.ts +276 -14
  19. package/src/auth/auth.test.ts +56 -0
  20. package/src/auth/config.ts +24 -5
  21. package/src/auth/index.ts +62 -1
  22. package/src/auth/plugin.ts +6 -1
  23. package/src/auth/schema.ts +8 -0
  24. package/src/auth/sessions.ts +18 -0
  25. package/src/auth/tables.ts +32 -0
  26. package/src/auth/tenant-config.ts +74 -0
  27. package/src/auth/tenant-tables.ts +11 -0
  28. package/src/auth/tenants.test.ts +63 -0
  29. package/src/auth/tenants.ts +360 -0
  30. package/src/cli/attach-host-console.test.ts +37 -0
  31. package/src/cli/attach-host-console.ts +6 -0
  32. package/src/cli/build.ts +2 -2
  33. package/src/client/budget.test.ts +1 -1
  34. package/src/client/create.ts +75 -5
  35. package/src/client/index.ts +13 -0
  36. package/src/client/live.test.ts +422 -0
  37. package/src/client/live.ts +389 -0
  38. package/src/client/notes-contract.test.ts +41 -0
  39. package/src/client/types.ts +85 -6
  40. package/src/client-react/index.ts +85 -1
  41. package/src/client-react/use-live.test.ts +129 -0
  42. package/src/compiler/effects-infer.ts +37 -2
  43. package/src/compiler/extract.test.ts +143 -11
  44. package/src/compiler/extract.ts +210 -30
  45. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +1 -2
  46. package/src/compiler/fixtures/skyport.expected.json +2 -3
  47. package/src/compiler/response.ts +41 -11
  48. package/src/console/server/access.test.ts +116 -8
  49. package/src/console/server/access.ts +70 -23
  50. package/src/console/server/app.ts +45 -11
  51. package/src/console/server/console.test.ts +6 -8
  52. package/src/console/server/dev-identities.test.ts +127 -0
  53. package/src/console/server/dev-identities.ts +229 -0
  54. package/src/console/server/flows-invoke.test.ts +21 -4
  55. package/src/console/server/flows.ts +113 -58
  56. package/src/console/server/gates.test.ts +24 -0
  57. package/src/console/server/gates.ts +10 -30
  58. package/src/console/server/index.ts +1 -0
  59. package/src/console/server/live.test.ts +26 -1
  60. package/src/console/server/live.ts +4 -2
  61. package/src/console/server/operator-db.ts +2 -0
  62. package/src/console/server/state.ts +29 -55
  63. package/src/console/server/store.test.ts +17 -0
  64. package/src/console/server/store.ts +43 -1
  65. package/src/console/ui-next/dist/assets/access-page-CXVWWMmD.js +4 -0
  66. package/src/console/ui-next/dist/assets/{agent-disclosure-C0X1fbWF.js → agent-disclosure-BP0Y0Sux.js} +1 -1
  67. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
  68. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
  69. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
  70. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
  71. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
  72. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
  73. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
  74. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
  75. package/src/console/ui-next/dist/assets/flows-page-XDpAJO8f.js +1 -0
  76. package/src/console/ui-next/dist/assets/{highlighted-json-CS_O8L-r.js → highlighted-json-Awq7gYdu.js} +1 -1
  77. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
  78. package/src/console/ui-next/dist/assets/index-BBj2QJCu.js +66 -0
  79. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
  80. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
  81. package/src/console/ui-next/dist/assets/observability-page-BFaay44m.js +4 -0
  82. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
  83. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
  84. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
  85. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
  86. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +1 -0
  87. package/src/console/ui-next/dist/assets/store-page-CS5-aETQ.js +41 -0
  88. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
  89. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +55 -0
  90. package/src/console/ui-next/dist/assets/units-page-Ca2Z-E52.js +1 -0
  91. package/src/console/ui-next/dist/assets/{use-vault-list-BLbZhzyF.js → use-vault-list-uk4WVboC.js} +1 -1
  92. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
  93. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
  94. package/src/console/ui-next/dist/assets/vault-page-BmOeAFwg.js +2 -0
  95. package/src/console/ui-next/dist/index.html +11 -9
  96. package/src/console/ui-next/index.html +3 -3
  97. package/src/console/ui-next/src/client.ts +167 -0
  98. package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
  99. package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
  100. package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
  101. package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
  102. package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
  103. package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
  104. package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
  105. package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
  106. package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
  107. package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
  108. package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
  109. package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
  110. package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
  111. package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
  112. package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
  113. package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
  114. package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
  115. package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
  116. package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
  117. package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
  118. package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
  119. package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
  120. package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
  121. package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
  122. package/src/console/ui-next/src/features/flows/fixture.ts +0 -1
  123. package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
  124. package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +5 -1
  125. package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +15 -4
  126. package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
  127. package/src/console/ui-next/src/lib/document-meta.ts +2 -1
  128. package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
  129. package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
  130. package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
  131. package/src/console/ui-next/src/lib/shortcut.ts +3 -0
  132. package/src/console/ui-next/src/router.tsx +10 -1
  133. package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
  134. package/src/console/ui-next/ui-next-seed-manifest-surface.ts +2 -9
  135. package/src/console/ui-next/ui-next-seed-manifest.ts +0 -1
  136. package/src/console/ui-next/ui-next-seed-store.ts +43 -15
  137. package/src/console/xss.gate.test.ts +1 -0
  138. package/src/drivers/index.ts +2 -0
  139. package/src/drivers/journal-postgres.ts +12 -3
  140. package/src/drivers/pg-rls.ts +26 -2
  141. package/src/drivers/pg-vault-rls.ts +68 -0
  142. package/src/drivers/signal-engine.ts +69 -15
  143. package/src/drivers/signal-live-iter.ts +65 -0
  144. package/src/drivers/signal-nats.ts +2 -1
  145. package/src/drivers/signal-postgres.ts +95 -12
  146. package/src/drivers/signal-redis.ts +2 -1
  147. package/src/drivers/signal-retention.ts +64 -0
  148. package/src/drivers/signal-types.ts +35 -5
  149. package/src/elements/clock/declare.ts +64 -2
  150. package/src/elements/clock/reconcile.ts +98 -25
  151. package/src/elements/clock/runtime.ts +13 -2
  152. package/src/elements/clock.test.ts +28 -0
  153. package/src/elements/clock.ts +9 -1
  154. package/src/elements/gate/declare.ts +2 -0
  155. package/src/elements/gate/permissions.ts +12 -0
  156. package/src/elements/gate.ts +6 -1
  157. package/src/elements/signal/declare.ts +44 -10
  158. package/src/elements/signal/delivery-modes.test.ts +90 -4
  159. package/src/elements/signal/order-lifecycle.test.ts +20 -4
  160. package/src/elements/signal/runtime.ts +42 -0
  161. package/src/elements/signal.test.ts +21 -8
  162. package/src/elements/signal.ts +1 -1
  163. package/src/elements/store/cache.ts +1 -1
  164. package/src/elements/store/declare.ts +7 -0
  165. package/src/elements/store/rls-identity.test.ts +29 -0
  166. package/src/elements/store/rls-identity.ts +3 -0
  167. package/src/elements/store/runtime.ts +14 -0
  168. package/src/elements/store/schema-decl.ts +162 -44
  169. package/src/elements/store/schema-tenant.ts +41 -0
  170. package/src/elements/store/sql-rls-isolation.test.ts +39 -0
  171. package/src/elements/store/sql-select.types.test.ts +71 -0
  172. package/src/elements/store/sql-session.ts +54 -32
  173. package/src/elements/store.ts +5 -0
  174. package/src/elements/vault/builtin-adapter.ts +15 -2
  175. package/src/elements/vault/declare.ts +8 -0
  176. package/src/elements/vault/runtime.ts +7 -0
  177. package/src/elements/vault/sql-rls-isolation.test.ts +145 -0
  178. package/src/elements/vault/storage.ts +9 -0
  179. package/src/elements/vault/test-helpers.ts +2 -1
  180. package/src/i18n/catalogs/ar.ts +19 -0
  181. package/src/i18n/catalogs/en.ts +19 -0
  182. package/src/index.ts +2 -0
  183. package/src/kernel/adopt-routes.ts +56 -8
  184. package/src/kernel/api-key-host-persist.test.ts +143 -0
  185. package/src/kernel/app-tenant.ts +122 -0
  186. package/src/kernel/app.ts +196 -57
  187. package/src/kernel/auth-resolve.ts +13 -5
  188. package/src/kernel/boot-bind/clock.ts +9 -3
  189. package/src/kernel/boot.test.ts +4 -4
  190. package/src/kernel/boot.ts +2 -0
  191. package/src/kernel/budget.test.ts +1 -1
  192. package/src/kernel/clock-durable.ts +8 -0
  193. package/src/kernel/clock-per-tenant-name.ts +5 -0
  194. package/src/kernel/clock-reconcile.ts +8 -0
  195. package/src/kernel/errors-live-resume.ts +15 -0
  196. package/src/kernel/errors-tenant.ts +29 -0
  197. package/src/kernel/errors.registry.test.ts +31 -3
  198. package/src/kernel/errors.ts +43 -3
  199. package/src/kernel/flow.ts +26 -5
  200. package/src/kernel/fx-auth-keys.test.ts +88 -0
  201. package/src/kernel/fx-auth-keys.ts +203 -0
  202. package/src/kernel/fx-auth-tenants.test.ts +87 -0
  203. package/src/kernel/fx-auth-tenants.ts +286 -0
  204. package/src/kernel/fx-live-stream.ts +149 -0
  205. package/src/kernel/fx-live.test.ts +157 -0
  206. package/src/kernel/fx-runtime.ts +15 -0
  207. package/src/kernel/fx-tenant-store.ts +213 -0
  208. package/src/kernel/fx.test.ts +108 -1
  209. package/src/kernel/fx.ts +206 -28
  210. package/src/kernel/hooks.ts +2 -2
  211. package/src/kernel/http-resource.ts +9 -18
  212. package/src/kernel/index.ts +2 -0
  213. package/src/kernel/journal.ts +12 -0
  214. package/src/kernel/live-http.test.ts +78 -0
  215. package/src/kernel/live-http.ts +114 -0
  216. package/src/kernel/live-resume.test.ts +125 -0
  217. package/src/kernel/on.ts +51 -0
  218. package/src/kernel/pipeline-api-key.test.ts +192 -7
  219. package/src/kernel/pipeline-tenant.ts +49 -0
  220. package/src/kernel/pipeline.test.ts +1 -1
  221. package/src/kernel/pipeline.ts +41 -4
  222. package/src/kernel/resource-mount.test.ts +10 -27
  223. package/src/kernel/tenant-resolve.test.ts +101 -0
  224. package/src/kernel/tenant-resolve.ts +124 -0
  225. package/src/kernel/tenant-roles.test.ts +87 -0
  226. package/src/kernel/triggers.ts +59 -21
  227. package/src/manifest/diff.test.ts +11 -2
  228. package/src/manifest/diff.ts +53 -11
  229. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  230. package/src/manifest/fixtures/skyport.manifest.json +0 -1
  231. package/src/manifest/types.ts +58 -6
  232. package/src/release/build-lib.ts +14 -1
  233. package/src/release/limits.ts +2 -2
  234. package/src/release/measure.ts +55 -1
  235. package/src/client/live-gap.test.ts +0 -35
  236. package/src/console/ui-next/dist/assets/cache-glyph-F1FI122b.js +0 -1
  237. package/src/console/ui-next/dist/assets/call-pii-button-DEDCl_j3.js +0 -1
  238. package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +0 -1
  239. package/src/console/ui-next/dist/assets/confirm-sheet-3ptDzXbz.js +0 -1
  240. package/src/console/ui-next/dist/assets/explorer-empty-BvYrygyO.js +0 -1
  241. package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +0 -1
  242. package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +0 -1
  243. package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +0 -66
  244. package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +0 -2
  245. package/src/console/ui-next/dist/assets/link-COeyggt-.js +0 -1
  246. package/src/console/ui-next/dist/assets/observability-page-HK9-BO8z.js +0 -4
  247. package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +0 -18
  248. package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +0 -1
  249. package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
  250. package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +0 -1
  251. package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +0 -1
  252. package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +0 -41
  253. package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +0 -2
  254. package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +0 -54
  255. package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +0 -1
  256. package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +0 -2
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Access page URL search — selected key + sheet action.
3
+ */
4
+
5
+ import { useNavigate, useSearch } from "@tanstack/react-router";
6
+ import { useCallback } from "react";
7
+
8
+ /** Sheet opened from the URL. */
9
+ export type AccessAction = "create" | "edit" | "refresh" | "revoke" | "rotate";
10
+
11
+ /** Search params for `/access`. */
12
+ export interface AccessSearch {
13
+ readonly q?: string;
14
+ readonly key?: string;
15
+ readonly action?: AccessAction;
16
+ }
17
+
18
+ /**
19
+ * Validate Access search params from the router.
20
+ *
21
+ * @param search - Raw search object
22
+ */
23
+ export function validateAccessSearch(search: Record<string, unknown>): AccessSearch {
24
+ const q = typeof search.q === "string" && search.q.length > 0 ? search.q : undefined;
25
+ const key = typeof search.key === "string" && search.key.length > 0 ? search.key : undefined;
26
+ const action =
27
+ search.action === "create" ||
28
+ search.action === "edit" ||
29
+ search.action === "refresh" ||
30
+ search.action === "revoke" ||
31
+ search.action === "rotate"
32
+ ? search.action
33
+ : undefined;
34
+ return {
35
+ ...(q !== undefined ? { q } : {}),
36
+ ...(key !== undefined ? { key } : {}),
37
+ ...(action !== undefined ? { action } : {}),
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Read + write the Access page selection from the URL.
43
+ */
44
+ export function useAccessSelection(): {
45
+ readonly query: string;
46
+ readonly selectedKey: string | null;
47
+ readonly action: AccessAction | null;
48
+ readonly setQuery: (q: string) => void;
49
+ readonly setSelectedKey: (key: string | null) => void;
50
+ readonly setAction: (action: AccessAction | null) => void;
51
+ } {
52
+ const search = useSearch({ strict: false }) as AccessSearch;
53
+ const navigate = useNavigate();
54
+
55
+ const query = typeof search.q === "string" ? search.q : "";
56
+ const selectedKey = typeof search.key === "string" ? search.key : null;
57
+ const action = search.action ?? null;
58
+
59
+ const patch = useCallback(
60
+ (next: Partial<AccessSearch>) => {
61
+ void navigate({
62
+ to: ".",
63
+ search: (prev: Record<string, unknown>) => validateAccessSearch({ ...prev, ...next }),
64
+ replace: true,
65
+ });
66
+ },
67
+ [navigate],
68
+ );
69
+
70
+ return {
71
+ query,
72
+ selectedKey,
73
+ action,
74
+ setQuery: (q) => {
75
+ patch({ q: q.length > 0 ? q : undefined });
76
+ },
77
+ setSelectedKey: (key) => {
78
+ patch({ key: key ?? undefined, action: undefined });
79
+ },
80
+ setAction: (next) => {
81
+ patch({ action: next ?? undefined });
82
+ },
83
+ };
84
+ }
@@ -18,6 +18,7 @@ describe("sanitizeReturnTo", () => {
18
18
  expect(sanitizeReturnTo("/vault?name=STRIPE_KEY&action=rotate")).toBe(
19
19
  "/vault?name=STRIPE_KEY&action=rotate",
20
20
  );
21
+ expect(sanitizeReturnTo("/access?key=key_1")).toBe("/access?key=key_1");
21
22
  expect(sanitizeReturnTo("/observability?window=7d")).toBe("/observability?window=7d");
22
23
  expect(sanitizeReturnTo("/monitoring?window=7d")).toBe("/observability?window=7d");
23
24
  });
@@ -107,6 +108,10 @@ describe("afterAuthLocation", () => {
107
108
  to: "/vault",
108
109
  search: { name: "STRIPE_KEY", action: "rotate-master" },
109
110
  });
111
+ expect(afterAuthLocation("/access?key=key_1")).toEqual({
112
+ to: "/access",
113
+ search: { key: "key_1" },
114
+ });
110
115
  expect(afterAuthLocation("/observability?window=7d&run=r1")).toEqual({
111
116
  to: "/observability",
112
117
  search: { window: "7d", run: "r1" },
@@ -2,7 +2,7 @@
2
2
  * Post-auth return path — keep the operator on the module they were in
3
3
  * after a session expires, instead of always landing on `/overview`.
4
4
  *
5
- * Only `/overview`, `/flows`, `/store`, `/vault`, and `/observability` (plus their
5
+ * Only `/overview`, `/flows`, `/store`, `/vault`, `/access`, and `/observability` (plus their
6
6
  * search) are legal. `/monitoring` is rewritten to `/observability`. Anything else
7
7
  * is dropped so `?next=` cannot be an open redirect.
8
8
  */
@@ -15,12 +15,20 @@ import {
15
15
  type ObservabilitySearch,
16
16
  } from "../observability/state/observability-selection.ts";
17
17
  import { validateVaultSearch, type VaultSearch } from "../vault/state/vault-selection.ts";
18
+ import { validateAccessSearch, type AccessSearch } from "../access/state/access-selection.ts";
18
19
 
19
20
  /** Default shell module when no safe return path is present. */
20
21
  export const DEFAULT_AFTER_AUTH = "/overview" as const;
21
22
 
22
23
  /** Shell pathnames that may be restored after login. */
23
- const SHELL_PATHS = new Set(["/overview", "/flows", "/store", "/vault", "/observability"]);
24
+ const SHELL_PATHS = new Set([
25
+ "/overview",
26
+ "/flows",
27
+ "/store",
28
+ "/vault",
29
+ "/access",
30
+ "/observability",
31
+ ]);
24
32
 
25
33
  /** Retired live module — rewritten to {@link CANONICAL_OBSERVABILITY}. */
26
34
  const LEGACY_MONITORING = "/monitoring";
@@ -40,6 +48,7 @@ export type AfterAuthLocation =
40
48
  | { readonly to: "/flows"; readonly search: UnitsSearch }
41
49
  | { readonly to: "/store"; readonly search: StoreSearch }
42
50
  | { readonly to: "/vault"; readonly search: VaultSearch }
51
+ | { readonly to: "/access"; readonly search: AccessSearch }
43
52
  | { readonly to: "/observability"; readonly search: ObservabilitySearch };
44
53
 
45
54
  /**
@@ -111,6 +120,9 @@ export function afterAuthLocation(value: unknown): AfterAuthLocation {
111
120
  if (url.pathname === "/vault") {
112
121
  return { to: "/vault", search: validateVaultSearch(raw) };
113
122
  }
123
+ if (url.pathname === "/access") {
124
+ return { to: "/access", search: validateAccessSearch(raw) };
125
+ }
114
126
  if (url.pathname === CANONICAL_OBSERVABILITY) {
115
127
  return { to: "/observability", search: validateObservabilitySearch(raw) };
116
128
  }
@@ -123,6 +135,7 @@ export type AfterAuthNavigate = {
123
135
  (opts: { to: "/flows"; search: UnitsSearch }): unknown;
124
136
  (opts: { to: "/store"; search: StoreSearch }): unknown;
125
137
  (opts: { to: "/vault"; search: VaultSearch }): unknown;
138
+ (opts: { to: "/access"; search: AccessSearch }): unknown;
126
139
  (opts: { to: "/observability"; search: ObservabilitySearch }): unknown;
127
140
  };
128
141
 
@@ -144,6 +157,9 @@ export function goAfterAuth(navigate: AfterAuthNavigate, value: unknown): void {
144
157
  case "/vault":
145
158
  void navigate({ to: "/vault", search: dest.search });
146
159
  return;
160
+ case "/access":
161
+ void navigate({ to: "/access", search: dest.search });
162
+ return;
147
163
  case "/observability":
148
164
  void navigate({ to: "/observability", search: dest.search });
149
165
  return;
@@ -39,7 +39,6 @@ export const FLOWS_TEST_MANIFEST: Manifest = {
39
39
  },
40
40
  "bookings.mine": {
41
41
  trigger: { http: { method: "GET", path: "/bookings" } },
42
- live: true,
43
42
  effects: { reads: ["sql:bookings"] },
44
43
  source: "src/flows/bookings/index.ts:42",
45
44
  },
@@ -6,7 +6,7 @@ import { authLabelClassName } from "@/components/auth-card";
6
6
  import { ConsoleChrome } from "@/components/console-chrome";
7
7
 
8
8
  /**
9
- * 404 for any path that is not `/`, `/overview`, `/flows`, `/store`, `/vault`, or `/observability`.
9
+ * 404 for any path that is not `/`, `/overview`, `/flows`, `/store`, `/vault`, `/access`, or `/observability`.
10
10
  */
11
11
  export function NotFoundPage() {
12
12
  return (
@@ -160,7 +160,11 @@ export function FlowContractPanel({
160
160
  <MetaPill
161
161
  icon={InternetAntenna03Icon}
162
162
  label="live"
163
- title="Live — subscribes to realtime updates"
163
+ title={
164
+ typeof row.flow.live === "string"
165
+ ? `Live — SSE for ${row.flow.live}`
166
+ : "Live — SSE signal feed"
167
+ }
164
168
  />
165
169
  ) : null}
166
170
  </FlowActivityStrip>
@@ -63,7 +63,11 @@ const TREE: readonly UnitGroup[] = [
63
63
  }),
64
64
  ]),
65
65
  group("orders", [
66
- row("orders.onPlaced", { trigger: { signal: "order-placed" }, live: true }, "once"),
66
+ row("orders.onPlaced", { trigger: { signal: "order-placed" } }, "once"),
67
+ row("orders.events", {
68
+ trigger: { http: { method: "GET", path: "/orders/:orderId/events" } },
69
+ live: "order-status",
70
+ }),
67
71
  row("orders.sync", { trigger: { cdc: { table: "orders" } }, plane: "operator" }),
68
72
  row("orders.helper", {}),
69
73
  ]),
@@ -100,14 +104,19 @@ describe("filterUnitsAdvanced", () => {
100
104
  const operators = filterUnitsAdvanced(TREE, { planes: ["operator"] });
101
105
  expect(flowIds(operators)).toEqual(["billing.reconcile", "orders.sync"]);
102
106
  const users = filterUnitsAdvanced(TREE, { planes: ["user"] });
103
- expect(flowIds(users)).toEqual(["billing.charge", "orders.onPlaced", "orders.helper"]);
107
+ expect(flowIds(users)).toEqual([
108
+ "billing.charge",
109
+ "orders.onPlaced",
110
+ "orders.events",
111
+ "orders.helper",
112
+ ]);
104
113
  });
105
114
 
106
115
  test("durable / live flags keep only flagged flows", () => {
107
116
  expect(flowIds(filterUnitsAdvanced(TREE, { durableOnly: true }))).toEqual([
108
117
  "billing.reconcile",
109
118
  ]);
110
- expect(flowIds(filterUnitsAdvanced(TREE, { liveOnly: true }))).toEqual(["orders.onPlaced"]);
119
+ expect(flowIds(filterUnitsAdvanced(TREE, { liveOnly: true }))).toEqual(["orders.events"]);
111
120
  });
112
121
 
113
122
  test("facet dimensions AND together", () => {
@@ -218,8 +227,9 @@ describe("bandUnitTree", () => {
218
227
  test("returns only populated trigger kind bands", () => {
219
228
  const bands = bandUnitTree(TREE);
220
229
  expect(bands.map((b) => b.id)).toEqual(["http", "signal", "cron", "cdc", "internal"]);
221
- expect(bands[0]!.groups.map((g) => g.unit)).toEqual(["billing"]);
230
+ expect(bands[0]!.groups.map((g) => g.unit)).toEqual(["billing", "orders"]);
222
231
  expect(bands[0]!.groups[0]!.flows.map((f) => f.id)).toEqual(["billing.charge"]);
232
+ expect(bands[0]!.groups[1]!.flows.map((f) => f.id)).toEqual(["orders.events"]);
223
233
  expect(bands[1]!.groups.map((g) => g.unit)).toEqual(["orders"]);
224
234
  expect(bands[1]!.groups[0]!.flows.map((f) => f.id)).toEqual(["orders.onPlaced"]);
225
235
  expect(bands[2]!.groups.map((g) => g.unit)).toEqual(["billing"]);
@@ -254,6 +264,7 @@ describe("bandUnitTree", () => {
254
264
  expect(unitTreeOpenKeys(bands)).toEqual([
255
265
  "band:http",
256
266
  "unit:http:billing",
267
+ "unit:http:orders",
257
268
  "band:signal",
258
269
  "unit:signal:orders",
259
270
  "band:cron",
@@ -25,6 +25,7 @@ describe("consoleDocumentTitle", () => {
25
25
  `Observability · ${CONSOLE_DOCUMENT_TITLE}`,
26
26
  );
27
27
  expect(consoleDocumentTitle("/vault")).toBe(`Vault · ${CONSOLE_DOCUMENT_TITLE}`);
28
+ expect(consoleDocumentTitle("/access")).toBe(`Access · ${CONSOLE_DOCUMENT_TITLE}`);
28
29
  expect(consoleDocumentTitle("/monitoring")).toBe(`Observability · ${CONSOLE_DOCUMENT_TITLE}`);
29
30
  });
30
31
 
@@ -7,7 +7,7 @@ export const CONSOLE_DOCUMENT_TITLE = "okengine Console";
7
7
 
8
8
  /** Operator-facing `meta name="description"` / Open Graph copy. */
9
9
  export const CONSOLE_DOCUMENT_DESCRIPTION =
10
- "Operator Console for okengine — Flows, Store, Observability, and Vault.";
10
+ "Operator Console for okengine — Flows, Store, Observability, Vault, and Access.";
11
11
 
12
12
  const PAGE_TITLES: Record<string, string> = {
13
13
  "/overview": "Overview",
@@ -15,6 +15,7 @@ const PAGE_TITLES: Record<string, string> = {
15
15
  "/store": "Store",
16
16
  "/observability": "Observability",
17
17
  "/vault": "Vault",
18
+ "/access": "Access",
18
19
  "/monitoring": "Observability",
19
20
  };
20
21
 
@@ -12,6 +12,7 @@ describe("last-module-search", () => {
12
12
  expect(isConsoleModulePath("/overview")).toBe(true);
13
13
  expect(isConsoleModulePath("/flows")).toBe(true);
14
14
  expect(isConsoleModulePath("/observability")).toBe(true);
15
+ expect(isConsoleModulePath("/access")).toBe(true);
15
16
  expect(isConsoleModulePath("/monitoring")).toBe(false);
16
17
  expect(isConsoleModulePath("/units")).toBe(false);
17
18
  expect(isConsoleModulePath("/")).toBe(false);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Remember each Console module's last URL search so sidebar hops
3
- * restore Overview / Flows / Store / Observability / Vault instead of wiping them.
3
+ * restore Overview / Flows / Store / Observability / Vault / Access instead of wiping them.
4
4
  */
5
5
 
6
6
  /** Authenticated Console modules that own URL search. */
@@ -10,6 +10,7 @@ export const CONSOLE_MODULE_PATHS = [
10
10
  "/store",
11
11
  "/observability",
12
12
  "/vault",
13
+ "/access",
13
14
  ] as const;
14
15
 
15
16
  /** Pathname for a Console module. */
@@ -21,6 +21,7 @@ describe("consoleShortcut", () => {
21
21
  expect(consoleShortcut("store")).toEqual(modChord("3"));
22
22
  expect(consoleShortcut("observability")).toEqual(modChord("4"));
23
23
  expect(consoleShortcut("vault")).toEqual(modChord("5"));
24
+ expect(consoleShortcut("access")).toEqual(modChord("6"));
24
25
  });
25
26
 
26
27
  test("digits map to module paths", () => {
@@ -29,6 +30,7 @@ describe("consoleShortcut", () => {
29
30
  expect(MODULE_DIGIT_PATH["3"]).toBe("/store");
30
31
  expect(MODULE_DIGIT_PATH["4"]).toBe("/observability");
31
32
  expect(MODULE_DIGIT_PATH["5"]).toBe("/vault");
33
+ expect(MODULE_DIGIT_PATH["6"]).toBe("/access");
32
34
  });
33
35
  });
34
36
 
@@ -63,6 +63,7 @@ export type ConsoleShortcutId =
63
63
  | "flows"
64
64
  | "store"
65
65
  | "vault"
66
+ | "access"
66
67
  | "observability";
67
68
 
68
69
  const MODULE_DIGIT: Readonly<
@@ -73,6 +74,7 @@ const MODULE_DIGIT: Readonly<
73
74
  store: "3",
74
75
  observability: "4",
75
76
  vault: "5",
77
+ access: "6",
76
78
  };
77
79
 
78
80
  /**
@@ -94,6 +96,7 @@ export const MODULE_DIGIT_PATH: Readonly<Record<string, ConsoleModulePath>> = {
94
96
  "3": "/store",
95
97
  "4": "/observability",
96
98
  "5": "/vault",
99
+ "6": "/access",
97
100
  };
98
101
 
99
102
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Console router — code-based TanStack Router (Vite SPA).
3
- * Pre-auth `/` and authenticated shell `/overview` | `/flows` | `/store` | `/vault` | `/observability`.
3
+ * Pre-auth `/` and authenticated shell `/overview` | `/flows` | `/store` | `/vault` | `/access` | `/observability`.
4
4
  * `/monitoring` is a one-shot search-preserving redirect to `/observability`.
5
5
  * Authenticated pages are `lazyRouteComponent` so Vite splits them out of the entry.
6
6
  * Any other path is a 404 — no legacy rewrites.
@@ -30,6 +30,7 @@ import { validateStoreSearch } from "./features/store/state/store-selection.ts";
30
30
  import { validateUnitsSearch } from "./features/units/state/units-selection.ts";
31
31
  import { validateObservabilitySearch } from "./features/observability/state/observability-selection.ts";
32
32
  import { validateVaultSearch } from "./features/vault/state/vault-selection.ts";
33
+ import { validateAccessSearch } from "./features/access/state/access-selection.ts";
33
34
  import { DocumentTitle } from "./components/document-title.tsx";
34
35
  import { ShellLayout } from "./components/shell/shell-layout.tsx";
35
36
 
@@ -120,6 +121,13 @@ const vaultRoute = createRoute({
120
121
  component: lazyRouteComponent(() => import("./features/vault/vault-page.tsx"), "VaultPage"),
121
122
  });
122
123
 
124
+ const accessRoute = createRoute({
125
+ getParentRoute: () => authenticatedRoute,
126
+ path: "/access",
127
+ validateSearch: validateAccessSearch,
128
+ component: lazyRouteComponent(() => import("./features/access/access-page.tsx"), "AccessPage"),
129
+ });
130
+
123
131
  const observabilityRoute = createRoute({
124
132
  getParentRoute: () => authenticatedRoute,
125
133
  path: "/observability",
@@ -146,6 +154,7 @@ const routeTree = rootRoute.addChildren([
146
154
  flowsRoute,
147
155
  storeRoute,
148
156
  vaultRoute,
157
+ accessRoute,
149
158
  observabilityRoute,
150
159
  monitoringRedirectRoute,
151
160
  ]),
@@ -7,6 +7,7 @@
7
7
  import { AUTH_TABLES } from "../../auth/tables.ts";
8
8
  import { resolveAuthSchema } from "../../auth/schema.ts";
9
9
  import type { DeclaredColumn, Table } from "../../manifest/types.ts";
10
+ import { KEEL_LADDER_GRANTS, KEEL_SCOPES } from "../server/dev-identities.ts";
10
11
 
11
12
  const PII_COLUMNS = new Set([
12
13
  "email",
@@ -163,9 +164,63 @@ export const UI_NEXT_SEED_APP_SYSTEM_TABLES: Record<string, Table> = {
163
164
  const NOW = 1_723_622_400_000;
164
165
  const LATER = NOW + 30 * 24 * 60 * 60 * 1000;
165
166
 
166
- const PEOPLE = [
167
- { id: "user_demo", email: "demo@example.com", name: "Demo User", status: "active" },
168
- { id: "user_member", email: "member@example.com", name: "Member", status: "active" },
167
+ /** Ten-rung owner → guest ladder (demo + member keep historic ids). */
168
+ const LADDER = [
169
+ {
170
+ id: "user_demo",
171
+ email: "demo@example.com",
172
+ name: "Demo User",
173
+ status: "active",
174
+ role: "owner",
175
+ },
176
+ { id: "user_admin", email: "admin@example.com", name: "Admin", status: "active", role: "admin" },
177
+ {
178
+ id: "user_workspace",
179
+ email: "workspace@example.com",
180
+ name: "Workspace Admin",
181
+ status: "active",
182
+ role: "workspace_admin",
183
+ },
184
+ {
185
+ id: "user_pm",
186
+ email: "pm@example.com",
187
+ name: "Project Manager",
188
+ status: "active",
189
+ role: "project_manager",
190
+ },
191
+ { id: "user_lead", email: "lead@example.com", name: "Lead", status: "active", role: "lead" },
192
+ {
193
+ id: "user_dev",
194
+ email: "developer@example.com",
195
+ name: "Developer",
196
+ status: "active",
197
+ role: "developer",
198
+ },
199
+ {
200
+ id: "user_writer",
201
+ email: "writer@example.com",
202
+ name: "Contributor",
203
+ status: "active",
204
+ role: "contributor",
205
+ },
206
+ {
207
+ id: "user_member",
208
+ email: "member@example.com",
209
+ name: "Member",
210
+ status: "active",
211
+ role: "member",
212
+ },
213
+ {
214
+ id: "user_commenter",
215
+ email: "commenter@example.com",
216
+ name: "Commenter",
217
+ status: "active",
218
+ role: "commenter",
219
+ },
220
+ { id: "user_guest", email: "guest@example.com", name: "Guest", status: "active", role: "guest" },
221
+ ] as const;
222
+
223
+ const STORY = [
169
224
  { id: "idn_aria", email: "aria@keel.dev", name: "Aria Chen", status: "active" },
170
225
  { id: "idn_ben", email: "ben@keel.dev", name: "Ben Okonkwo", status: "active" },
171
226
  { id: "idn_cai", email: "cai@keel.dev", name: "Cai Moreno", status: "active" },
@@ -185,6 +240,8 @@ const PEOPLE = [
185
240
  { id: "idn_zio", email: "zio@keel.dev", name: "Zio Hart", status: "active" },
186
241
  ] as const;
187
242
 
243
+ const PEOPLE = [...LADDER, ...STORY];
244
+
188
245
  /**
189
246
  * Seeded rows for app-plane system tables (never secret values).
190
247
  */
@@ -209,49 +266,43 @@ export const UI_NEXT_SEED_APP_SYSTEM_ROWS: Readonly<Record<string, readonly obje
209
266
  updated_at: NOW,
210
267
  })),
211
268
  [AUTH_TABLES.identityRoles]: [
212
- ...PEOPLE.map((p) => ({
269
+ ...LADDER.filter((p) => p.role !== "guest").map((p) => ({
270
+ id: `ir_${p.id}_${p.role}`,
271
+ identity_id: p.id,
272
+ role_id: `role_${p.role}`,
273
+ })),
274
+ ...STORY.map((p) => ({
213
275
  id: `ir_${p.id}_member`,
214
276
  identity_id: p.id,
215
277
  role_id: "role_member",
216
278
  })),
217
- { id: "ir_idn_aria_staff", identity_id: "idn_aria", role_id: "role_staff" },
218
- { id: "ir_user_demo_staff", identity_id: "user_demo", role_id: "role_staff" },
219
- ],
220
- [AUTH_TABLES.roles]: [
221
- { id: "role_member", name: "member", plane: "user", description: "Signed-in workspace member" },
222
- { id: "role_staff", name: "staff", plane: "user", description: "May accept triage" },
223
- {
224
- id: "role_issue_write",
225
- name: "issue:write",
226
- plane: "user",
227
- description: "May create and update issues",
228
- },
229
- {
230
- id: "role_team_admin",
231
- name: "team:admin",
232
- plane: "user",
233
- description: "May create teams and close cycles",
234
- },
235
- ],
236
- [AUTH_TABLES.roleGrants]: [
237
- { id: "rg_1", role_id: "role_member", action: "member" },
238
- { id: "rg_2", role_id: "role_staff", action: "member" },
239
- { id: "rg_3", role_id: "role_staff", action: "triage:accept" },
240
- { id: "rg_4", role_id: "role_issue_write", action: "issue:write" },
241
- { id: "rg_5", role_id: "role_issue_write", action: "project:admin" },
242
- { id: "rg_6", role_id: "role_issue_write", action: "comment:write" },
243
- { id: "rg_7", role_id: "role_issue_write", action: "files:write" },
244
- { id: "rg_8", role_id: "role_team_admin", action: "team:admin" },
245
- { id: "rg_9", role_id: "role_team_admin", action: "member:admin" },
246
- { id: "rg_10", role_id: "role_team_admin", action: "webhook:admin" },
279
+ { id: "ir_idn_aria_pm", identity_id: "idn_aria", role_id: "role_project_manager" },
247
280
  ],
281
+ [AUTH_TABLES.roles]: Object.keys(KEEL_LADDER_GRANTS).map((role) => ({
282
+ id: `role_${role}`,
283
+ name: role,
284
+ plane: "user",
285
+ description:
286
+ role === "owner"
287
+ ? "Full workspace authorization"
288
+ : role === "guest"
289
+ ? "Least authorization — no write scopes"
290
+ : `Keel ${role.replaceAll("_", " ")}`,
291
+ })),
292
+ [AUTH_TABLES.roleGrants]: Object.entries(KEEL_LADDER_GRANTS).flatMap(([role, actions]) =>
293
+ actions.map((action, i) => ({
294
+ id: `rg_${role}_${i + 1}`,
295
+ role_id: `role_${role}`,
296
+ action,
297
+ })),
298
+ ),
248
299
  [AUTH_TABLES.apiKeys]: [
249
300
  {
250
301
  id: "key_keel_ci",
251
302
  plane: "user",
252
303
  hash: "sha256$keel_ci",
253
304
  name: "Keel CI",
254
- scopes: "member,issue:write",
305
+ scopes: "member,task:write",
255
306
  expires_at: null,
256
307
  created_at: NOW,
257
308
  last_used_at: NOW - 3_600_000,
@@ -279,7 +330,7 @@ export const UI_NEXT_SEED_APP_SYSTEM_ROWS: Readonly<Record<string, readonly obje
279
330
  created_at: NOW,
280
331
  expires_at: LATER,
281
332
  last_active_at: NOW,
282
- scopes: "member,issue:write",
333
+ scopes: KEEL_LADDER_GRANTS.project_manager.join(","),
283
334
  audience: "oke-app",
284
335
  },
285
336
  {
@@ -291,7 +342,7 @@ export const UI_NEXT_SEED_APP_SYSTEM_ROWS: Readonly<Record<string, readonly obje
291
342
  created_at: NOW,
292
343
  expires_at: LATER,
293
344
  last_active_at: NOW,
294
- scopes: "member,issue:write",
345
+ scopes: KEEL_SCOPES.join(","),
295
346
  audience: "oke-app",
296
347
  },
297
348
  ],
@@ -81,7 +81,7 @@ function httpFlow(spec: {
81
81
  path: string;
82
82
  gates?: readonly string[];
83
83
  plane?: "user" | "operator";
84
- live?: boolean;
84
+ live?: string;
85
85
  durable?: boolean;
86
86
  cache?: boolean | string;
87
87
  in?: JsonSchema;
@@ -94,7 +94,7 @@ function httpFlow(spec: {
94
94
  trigger: { http: { method: spec.method, path: spec.path } },
95
95
  plane: spec.plane ?? "user",
96
96
  ...(spec.gates ? { gates: [...spec.gates] } : {}),
97
- ...(spec.live ? { live: true } : {}),
97
+ ...(spec.live ? { live: spec.live } : {}),
98
98
  ...(spec.durable ? { durable: true } : {}),
99
99
  ...(spec.cache !== undefined ? { cache: spec.cache } : {}),
100
100
  ...(spec.in ? { in: spec.in } : {}),
@@ -118,7 +118,6 @@ function crud(spec: {
118
118
  readGates?: readonly string[];
119
119
  writeGates?: readonly string[];
120
120
  skip?: readonly ("list" | "get" | "create" | "update" | "delete")[];
121
- liveList?: boolean;
122
121
  createIn?: JsonSchema;
123
122
  updateIn?: JsonSchema;
124
123
  createEmits?: readonly string[];
@@ -133,7 +132,6 @@ function crud(spec: {
133
132
  method: "GET",
134
133
  path: spec.collection,
135
134
  gates: read,
136
- live: spec.liveList,
137
135
  in: KEEL_LIST_IN,
138
136
  out: KEEL_LIST_OUT,
139
137
  effects: { reads: [spec.table] },
@@ -365,7 +363,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
365
363
  readGates: MEMBER,
366
364
  writeGates: COMMENT_WRITE,
367
365
  skip: ["create"],
368
- liveList: true,
369
366
  updateIn: COMMENT_IN,
370
367
  sourceDir: "src/flows/comments",
371
368
  }),
@@ -386,7 +383,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
386
383
  readGates: MEMBER,
387
384
  writeGates: PROJECT_ADMIN,
388
385
  skip: ["create"],
389
- liveList: true,
390
386
  sourceDir: "src/flows/projects",
391
387
  }),
392
388
  "projects.archive": httpFlow({
@@ -491,7 +487,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
491
487
  item: "/spaces/:id",
492
488
  readGates: MEMBER,
493
489
  writeGates: PROJECT_ADMIN,
494
- liveList: true,
495
490
  createIn: {
496
491
  type: "object",
497
492
  required: ["key", "name"],
@@ -610,7 +605,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
610
605
  readGates: MEMBER,
611
606
  writeGates: MEMBER,
612
607
  skip: ["create"],
613
- liveList: true,
614
608
  sourceDir: "src/flows/inbox",
615
609
  }),
616
610
 
@@ -618,7 +612,6 @@ export const KEEL_SURFACE_FLOWS: Record<string, Flow> = {
618
612
  method: "GET",
619
613
  path: "/me/tasks",
620
614
  gates: MEMBER,
621
- live: true,
622
615
  in: KEEL_LIST_IN,
623
616
  out: KEEL_LIST_OUT,
624
617
  effects: { reads: ["sql:tasks", "sql:task_assignees"] },
@@ -114,7 +114,6 @@ export const UI_NEXT_SEEDED_MANIFEST: Manifest = {
114
114
  },
115
115
  "tasks.list": {
116
116
  trigger: { http: { method: "GET", path: "/tasks" } },
117
- live: true,
118
117
  plane: "user",
119
118
  gates: ["member"],
120
119
  in: KEEL_LIST_IN,