@yourrealm/sdk 0.0.0-alpha.1378.ce03c8dc

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 (230) hide show
  1. package/LICENSE-EXCEPTION +26 -0
  2. package/README.md +34 -0
  3. package/dist/chunk-3R6KEFOI.js +22 -0
  4. package/dist/chunk-67BDAMYX.js +376 -0
  5. package/dist/chunk-AITGS27R.js +139 -0
  6. package/dist/chunk-CMAEHQ4F.js +6 -0
  7. package/dist/chunk-MLKGABMK.js +9 -0
  8. package/dist/chunk-O3UIK34B.js +1430 -0
  9. package/dist/chunk-VHTUSZUX.js +176 -0
  10. package/dist/cli.js +9006 -0
  11. package/dist/docker.js +54 -0
  12. package/dist/index.js +1053 -0
  13. package/dist/ingress.js +436 -0
  14. package/dist/jsx-runtime.js +2 -0
  15. package/dist/preact.js +22 -0
  16. package/dist/service-url.js +11 -0
  17. package/dist/services.js +213 -0
  18. package/dist/test.js +5069 -0
  19. package/dist/ui.js +254 -0
  20. package/dist/worker-protocol.js +89 -0
  21. package/package.json +112 -0
  22. package/src/app-icon.ts +74 -0
  23. package/src/apply.ts +52 -0
  24. package/src/bundle-ui.ts +211 -0
  25. package/src/bundle.ts +546 -0
  26. package/src/cli/build-dev-landing.ts +84 -0
  27. package/src/cli/build-install-panel.ts +90 -0
  28. package/src/cli/build-runtime.ts +133 -0
  29. package/src/cli/build.ts +63 -0
  30. package/src/cli/dev.ts +107 -0
  31. package/src/cli/down.ts +60 -0
  32. package/src/cli/main.ts +56 -0
  33. package/src/cli/test.ts +61 -0
  34. package/src/cli/validate.ts +37 -0
  35. package/src/compile.ts +100 -0
  36. package/src/create-app.ts +982 -0
  37. package/src/deep-merge.ts +33 -0
  38. package/src/dev/fixture.ts +48 -0
  39. package/src/dev/install-handler.ts +383 -0
  40. package/src/dev/server.ts +968 -0
  41. package/src/dev/stack.ts +108 -0
  42. package/src/dev/state.ts +159 -0
  43. package/src/docker.ts +96 -0
  44. package/src/errors.ts +61 -0
  45. package/src/factory.ts +481 -0
  46. package/src/garage.ts +56 -0
  47. package/src/generator/hash.ts +6 -0
  48. package/src/generator/resolve-refs.ts +207 -0
  49. package/src/grants-builder.ts +100 -0
  50. package/src/grants.ts +260 -0
  51. package/src/home-api.ts +172 -0
  52. package/src/home-cookie.ts +10 -0
  53. package/src/home-registry.ts +120 -0
  54. package/src/home.ts +187 -0
  55. package/src/host-registry.ts +143 -0
  56. package/src/http-header.ts +20 -0
  57. package/src/identifiers.ts +49 -0
  58. package/src/index.ts +66 -0
  59. package/src/info.ts +64 -0
  60. package/src/ini.ts +50 -0
  61. package/src/json.ts +14 -0
  62. package/src/jsx-runtime/index.ts +5 -0
  63. package/src/keys.ts +29 -0
  64. package/src/mysql.ts +56 -0
  65. package/src/pack-build.ts +108 -0
  66. package/src/pack.ts +115 -0
  67. package/src/peer-registry.ts +121 -0
  68. package/src/peer-world.ts +117 -0
  69. package/src/permission-meta.ts +116 -0
  70. package/src/permissions.ts +108 -0
  71. package/src/pkg-root.ts +34 -0
  72. package/src/postgres.ts +79 -0
  73. package/src/preact/index.ts +27 -0
  74. package/src/redis.ts +43 -0
  75. package/src/ref.ts +271 -0
  76. package/src/remote-registry.ts +33 -0
  77. package/src/required-host-kinds.ts +110 -0
  78. package/src/resolve-context.ts +111 -0
  79. package/src/resolve.ts +471 -0
  80. package/src/runtime/docker/compose/compose-schema.ts +118 -0
  81. package/src/runtime/docker/compose/convert-to-docker-compose.ts +278 -0
  82. package/src/runtime/docker/compose/default-hardening.ts +21 -0
  83. package/src/runtime/docker/compose/effects.ts +107 -0
  84. package/src/runtime/docker/compose/index.ts +17 -0
  85. package/src/runtime/docker/compose/labels.ts +39 -0
  86. package/src/runtime/docker/compose/types.ts +78 -0
  87. package/src/runtime/docker/home-proxy-server/index.ts +177 -0
  88. package/src/runtime/docker/home-proxy-server/protocol.ts +53 -0
  89. package/src/runtime/docker/ingress/build-config.ts +819 -0
  90. package/src/runtime/docker/ingress/index.ts +16 -0
  91. package/src/runtime/docker/ingress/types.ts +182 -0
  92. package/src/runtime/docker/log-frames.ts +121 -0
  93. package/src/runtime/docker/logs-server/index.ts +366 -0
  94. package/src/runtime/docker/spawn-server/context-key.ts +82 -0
  95. package/src/runtime/docker/spawn-server/index.ts +492 -0
  96. package/src/runtime/docker/spawn-server/protocol.ts +70 -0
  97. package/src/runtime/docker/spawn-server/spec.ts +122 -0
  98. package/src/runtime/docker/stats-server/disks.ts +161 -0
  99. package/src/runtime/docker/stats-server/index.ts +322 -0
  100. package/src/runtime/home-client/index.ts +71 -0
  101. package/src/runtime/log-line.ts +24 -0
  102. package/src/runtime/logs-client/index.ts +175 -0
  103. package/src/runtime/spawn-client/index.ts +93 -0
  104. package/src/runtime/spawn-client/kaniko.ts +79 -0
  105. package/src/runtime/stats-client/index.ts +65 -0
  106. package/src/runtime/stats-sample.ts +95 -0
  107. package/src/safe-json.ts +37 -0
  108. package/src/sandbox.ts +1209 -0
  109. package/src/schema/build-apps.ts +38 -0
  110. package/src/schema/home.ts +217 -0
  111. package/src/schema/host.ts +792 -0
  112. package/src/schema/inputs.ts +26 -0
  113. package/src/schema/nodes.ts +75 -0
  114. package/src/schema/peer.ts +400 -0
  115. package/src/schema/public-domain.ts +116 -0
  116. package/src/schema/remote.ts +74 -0
  117. package/src/security.ts +594 -0
  118. package/src/serialize.ts +21 -0
  119. package/src/service-definition.ts +284 -0
  120. package/src/service-url.ts +64 -0
  121. package/src/service.ts +39 -0
  122. package/src/services/index.ts +5 -0
  123. package/src/shared.ts +65 -0
  124. package/src/speculative-silo.ts +96 -0
  125. package/src/stable-secret.ts +14 -0
  126. package/src/surfaces.ts +264 -0
  127. package/src/tailwind-bin.ts +56 -0
  128. package/src/test/boot.ts +192 -0
  129. package/src/test/compile.ts +501 -0
  130. package/src/test/describe.ts +117 -0
  131. package/src/test/index.ts +11 -0
  132. package/src/test/ingress-fixture.ts +37 -0
  133. package/src/test/types.ts +318 -0
  134. package/src/trusted-headers.ts +79 -0
  135. package/src/types.ts +126 -0
  136. package/src/ui/components/aside.tsx +96 -0
  137. package/src/ui/components/badge.tsx +42 -0
  138. package/src/ui/components/button.tsx +59 -0
  139. package/src/ui/components/card.tsx +106 -0
  140. package/src/ui/components/checkbox.tsx +37 -0
  141. package/src/ui/components/code-block.tsx +29 -0
  142. package/src/ui/components/combobox.tsx +119 -0
  143. package/src/ui/components/config-field.tsx +284 -0
  144. package/src/ui/components/dialog.tsx +137 -0
  145. package/src/ui/components/domain-editor.tsx +317 -0
  146. package/src/ui/components/domain-list.ts +205 -0
  147. package/src/ui/components/dropdown-menu.tsx +73 -0
  148. package/src/ui/components/empty-state.tsx +51 -0
  149. package/src/ui/components/highlight.ts +59 -0
  150. package/src/ui/components/icons.tsx +101 -0
  151. package/src/ui/components/image-posture.tsx +85 -0
  152. package/src/ui/components/input.tsx +28 -0
  153. package/src/ui/components/label.tsx +16 -0
  154. package/src/ui/components/line-chart.tsx +113 -0
  155. package/src/ui/components/page.tsx +39 -0
  156. package/src/ui/components/permission-row.tsx +262 -0
  157. package/src/ui/components/permission-section.tsx +51 -0
  158. package/src/ui/components/pie-chart.tsx +245 -0
  159. package/src/ui/components/schema-block.tsx +60 -0
  160. package/src/ui/components/separator.tsx +42 -0
  161. package/src/ui/components/sheet.tsx +130 -0
  162. package/src/ui/components/sidebar.tsx +650 -0
  163. package/src/ui/components/silo-picker.tsx +510 -0
  164. package/src/ui/components/skeleton.tsx +13 -0
  165. package/src/ui/components/sonner.tsx +33 -0
  166. package/src/ui/components/switch.tsx +58 -0
  167. package/src/ui/components/table-view.ts +219 -0
  168. package/src/ui/components/table.tsx +506 -0
  169. package/src/ui/components/tabs.tsx +59 -0
  170. package/src/ui/components/timeago.tsx +84 -0
  171. package/src/ui/components/tooltip.tsx +56 -0
  172. package/src/ui/components.json +21 -0
  173. package/src/ui/dev/dev-shell.tsx +145 -0
  174. package/src/ui/home/home-grid.tsx +177 -0
  175. package/src/ui/hooks/use-mobile.ts +19 -0
  176. package/src/ui/index.ts +189 -0
  177. package/src/ui/install/adapter.ts +193 -0
  178. package/src/ui/install/deploy-targets-picker.tsx +185 -0
  179. package/src/ui/install/install-app-panel.tsx +912 -0
  180. package/src/ui/install/install-error.tsx +62 -0
  181. package/src/ui/install/perms-inactive.ts +50 -0
  182. package/src/ui/install/required-fields.ts +37 -0
  183. package/src/ui/install/tabs-form.tsx +557 -0
  184. package/src/ui/tokens.css +279 -0
  185. package/src/ui/utils.ts +78 -0
  186. package/src/ui-error.ts +139 -0
  187. package/src/ui-runtime/dev-landing-entry.tsx +258 -0
  188. package/src/ui-runtime/index.ts +83 -0
  189. package/src/ui-runtime/install-entry.tsx +119 -0
  190. package/src/ui-runtime/mount.ts +367 -0
  191. package/src/ui-runtime/runtime.css +56 -0
  192. package/src/ui-surface.ts +451 -0
  193. package/src/valkey.ts +43 -0
  194. package/src/worker/extract-tar.ts +125 -0
  195. package/src/worker/identity.ts +183 -0
  196. package/src/worker/index.ts +16 -0
  197. package/src/worker/public-ip.ts +92 -0
  198. package/src/worker/queue.ts +54 -0
  199. package/src/worker/reconcile.ts +127 -0
  200. package/src/worker/start-worker.ts +227 -0
  201. package/src/worker/ui-proxy.ts +335 -0
  202. package/src/worker/uplink.ts +523 -0
  203. package/src/worker/validate.ts +34 -0
  204. package/src/worker/verify-relay.ts +102 -0
  205. package/src/worker-protocol/app-dirs.ts +94 -0
  206. package/src/worker-protocol/home-proxy-paths.ts +76 -0
  207. package/src/worker-protocol/index.ts +55 -0
  208. package/src/worker-protocol/ingress.ts +166 -0
  209. package/src/worker-protocol/logs-socket-path.ts +50 -0
  210. package/src/worker-protocol/networks.ts +39 -0
  211. package/src/worker-protocol/node-version.ts +70 -0
  212. package/src/worker-protocol/silo-paths.ts +114 -0
  213. package/src/worker-protocol/spawn-paths.ts +165 -0
  214. package/src/worker-protocol/stats-socket-path.ts +40 -0
  215. package/src/worker-protocol/update-files.ts +77 -0
  216. package/src/worker-protocol/uplink/constants.ts +86 -0
  217. package/src/worker-protocol/uplink/errors.ts +61 -0
  218. package/src/worker-protocol/uplink/frame.ts +114 -0
  219. package/src/worker-protocol/uplink/identity.ts +40 -0
  220. package/src/worker-protocol/uplink/index.ts +63 -0
  221. package/src/worker-protocol/uplink/methods.ts +462 -0
  222. package/src/worker-protocol/uplink/mux.ts +646 -0
  223. package/src/worker-protocol/uplink/protocol.ts +11 -0
  224. package/src/worker-protocol/uplink/rpc.ts +309 -0
  225. package/src/worker-protocol/uplink/session.ts +540 -0
  226. package/src/worker-protocol/uplink/test-support.ts +188 -0
  227. package/src/worker-protocol/worker-mount.ts +27 -0
  228. package/src/worker-protocol/worker-state.ts +190 -0
  229. package/src/xml.ts +49 -0
  230. package/src/yaml.ts +15 -0
@@ -0,0 +1,172 @@
1
+ // Shared request/response contract for the control-plane calls a granted app
2
+ // makes over its `home-proxy.sock` broker. The schemas live here once and are
3
+ // imported by both sides — the realm.ts app (client, via the worker's
4
+ // `callHome`) and the Home route that serves the path — so the wire shape can't
5
+ // drift. The app supplies the sub-path; Home serves it under `/api-access`:
6
+ // - `setImage` (POST /set-image): re-pin a `build:` app's service to a new
7
+ // digest.
8
+ // - preview install/uninstall (POST/DELETE /template/:t/preview[/:k]): stamp
9
+ // or tear down a template's `role: preview` child, keyed (templateId,
10
+ // previewKey), both parts riding the path. Install creates the child
11
+ // pinless; setImage pins it.
12
+ // - `registry` upsert (PUT /registry): upsert a pull credential for the
13
+ // caller or a child it created. `host` stays in the body because it may
14
+ // carry a `:port`, which the broker's path sanitizer forbids in a segment.
15
+
16
+ import { z } from "zod";
17
+
18
+ /** App-facing sub-path for setImage. Home serves it under `/api-access`. */
19
+ export const SET_IMAGE_PATH = "/set-image";
20
+
21
+ // A lowercase OCI repository path: slash-separated components, each alnum with
22
+ // single `.` / `_` / `-` separators. No scheme, no `..`, no leading/trailing
23
+ // slash, no empty segment. Mirrors the registry's REPO_PATTERN so the
24
+ // host-anchored ref Home builds (`<body.host>/<repo>@<digest>`) always parses.
25
+ const OCI_REPO =
26
+ /^[a-z0-9]+(?:(?:\.|_|__|-+)[a-z0-9]+)*(?:\/[a-z0-9]+(?:(?:\.|_|__|-+)[a-z0-9]+)*)*$/;
27
+
28
+ // A pinned digest: `sha256:` + 64 lowercase hex. Tags / floating refs are
29
+ // rejected — the only thing setImage can pin is a content-addressed digest.
30
+ const SHA256_DIGEST = /^sha256:[0-9a-f]{64}$/;
31
+
32
+ // A registry host: hostname with optional `:port` (e.g. `zot:5000`,
33
+ // `1.2.3.15:5000`, `ghcr.io`). Shared by `setImage` and the registry upsert so
34
+ // the host a caller supplies in either body parses the same way. No scheme, no
35
+ // path — the port is why `host` can't be a path segment (the broker's
36
+ // SAFE_SUBPATH excludes `:`).
37
+ const REGISTRY_HOST = /^[a-zA-Z0-9.-]+(:[0-9]+)?$/;
38
+
39
+ /**
40
+ * Body of a `setImage` call. `appId` + `service` name the target build app's
41
+ * service; `host` + `repo` + `digest` build the pull ref. `host` is the
42
+ * caller's own registry host — supplied here, but Home validates it belongs to
43
+ * the caller's own gated subdomain space, so the constructed ref can only ever
44
+ * point at the caller's own registry.
45
+ */
46
+ export const setImageBodySchema = z
47
+ .object({
48
+ appId: z.string().min(1),
49
+ service: z.string().min(1),
50
+ host: z.string().regex(REGISTRY_HOST, "host must be a registry hostname with optional :port"),
51
+ repo: z.string().min(1).regex(OCI_REPO, "repo must be a lowercase OCI path"),
52
+ digest: z.string().regex(SHA256_DIGEST, "digest must be sha256:<64 hex>"),
53
+ })
54
+ .strict();
55
+
56
+ export type SetImageBody = z.infer<typeof setImageBodySchema>;
57
+
58
+ /** Success shape: echoes back the target the pin landed on. */
59
+ export const setImageResponseSchema = z
60
+ .object({
61
+ ok: z.literal(true),
62
+ appId: z.string(),
63
+ service: z.string(),
64
+ digest: z.string(),
65
+ })
66
+ .strict();
67
+
68
+ export type SetImageResponse = z.infer<typeof setImageResponseSchema>;
69
+
70
+ // ---------------------------------------------------------------------------
71
+ // Preview install / uninstall (scope: builder)
72
+ // ---------------------------------------------------------------------------
73
+
74
+ // A preview key is a single DNS label: 1-63 chars, lowercase alnum + internal
75
+ // hyphens, no edge hyphen. NOT `nameSchema` — that requires a leading letter,
76
+ // which rejects a numeric PR key like "42". Safe to fold into a subdomain.
77
+ const PREVIEW_KEY = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
78
+
79
+ /** A preview key (DNS label): the per-PR half of a `(templateId, previewKey)`. */
80
+ export const previewKeySchema = z
81
+ .string()
82
+ .regex(PREVIEW_KEY, "previewKey must be a DNS label (lowercase alnum + internal hyphens, ≤63)");
83
+
84
+ // Path builders, not flat consts: both key parts ride the path. `templateId`
85
+ // is appId-shaped and `previewKey` is a DNS label — both colon-free — so they
86
+ // drop in as segments. This lets a caller rebuild the URL from `(template,
87
+ // key)` with no stored appId (Forge's PR-close GC relies on that).
88
+
89
+ /** POST target — the preview collection under a template. */
90
+ export function previewCollectionPath(templateId: string): string {
91
+ return `/template/${templateId}/preview`;
92
+ }
93
+
94
+ /** DELETE target — one preview under a template. */
95
+ export function previewItemPath(templateId: string, previewKey: string): string {
96
+ return `/template/${templateId}/preview/${previewKey}`;
97
+ }
98
+
99
+ /**
100
+ * Body of a preview install (`POST /template/:templateId/preview`). The
101
+ * template is in the path; the only payload is the `previewKey`. No repo /
102
+ * digest — the child is created pinless and `setImage` pins it.
103
+ */
104
+ export const previewInstallBodySchema = z.object({ previewKey: previewKeySchema }).strict();
105
+
106
+ export type PreviewInstallBody = z.infer<typeof previewInstallBodySchema>;
107
+
108
+ /**
109
+ * Success shape of a preview install. `appId` is the child row's id; `created`
110
+ * is `false` on the idempotent hit (a row already existed for this
111
+ * `(templateId, previewKey)`), `true` when a new child was stamped.
112
+ */
113
+ export const previewInstallResponseSchema = z
114
+ .object({
115
+ ok: z.literal(true),
116
+ appId: z.string(),
117
+ created: z.boolean(),
118
+ })
119
+ .strict();
120
+
121
+ export type PreviewInstallResponse = z.infer<typeof previewInstallResponseSchema>;
122
+
123
+ /**
124
+ * Success shape of a preview uninstall (`DELETE
125
+ * /template/:templateId/preview/:previewKey`, no body). `removed` is the child
126
+ * row's id.
127
+ */
128
+ export const previewUninstallResponseSchema = z
129
+ .object({ ok: z.literal(true), removed: z.string() })
130
+ .strict();
131
+
132
+ export type PreviewUninstallResponse = z.infer<typeof previewUninstallResponseSchema>;
133
+
134
+ // ---------------------------------------------------------------------------
135
+ // Registry credential upsert (scope: builder)
136
+ // ---------------------------------------------------------------------------
137
+
138
+ /** PUT target for the registry-credential upsert. */
139
+ export const REGISTRY_PATH = "/registry";
140
+
141
+ /**
142
+ * Body of a registry upsert (`PUT /registry`). `appId` is required — a global
143
+ * (NULL-app) credential is deliberately unrepresentable here; this surface only
144
+ * writes self-or-lineage scoped creds. `host` carries the registry host (may
145
+ * include `:port`); `username` / `password` are the Basic credential.
146
+ */
147
+ export const registryUpsertBodySchema = z
148
+ .object({
149
+ appId: z.string().min(1),
150
+ host: z.string().regex(REGISTRY_HOST, "host must be a registry hostname with optional :port"),
151
+ username: z.string().min(1),
152
+ password: z.string().min(1),
153
+ })
154
+ .strict();
155
+
156
+ export type RegistryUpsertBody = z.infer<typeof registryUpsertBodySchema>;
157
+
158
+ /**
159
+ * Success shape of a registry upsert. `id` is the credential row id; `host` is
160
+ * the normalized host it was stored under; `created` is `false` when an
161
+ * existing row was updated.
162
+ */
163
+ export const registryUpsertResponseSchema = z
164
+ .object({
165
+ ok: z.literal(true),
166
+ id: z.string(),
167
+ host: z.string(),
168
+ created: z.boolean(),
169
+ })
170
+ .strict();
171
+
172
+ export type RegistryUpsertResponse = z.infer<typeof registryUpsertResponseSchema>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Prefix of Home's own better-auth cookies (`realm-home.session_token`, …).
3
+ *
4
+ * Home sets it (`platform/home/src/server/auth.ts`) and the caddy app's
5
+ * cookie filter matches it, so the filter guards exactly Home's session and
6
+ * never an app's cookies — better-auth's default `better-auth.*` names
7
+ * included. Keep it free of regex metacharacters: caddy interpolates it into
8
+ * RE2 patterns.
9
+ */
10
+ export const HOME_COOKIE_PREFIX = "realm-home";
@@ -0,0 +1,120 @@
1
+ // Per-kind Home-side mechanics for `home.*` permissions — the keyless
2
+ // control-plane namespace.
3
+ //
4
+ // `home` is a first-class namespace, not `host.homeApi` sugar: like
5
+ // `peers`/`remote` it is Home-resolved and never worker-advertised, so it stays
6
+ // out of the host worker-capability gate. But like `host` it carries data (the
7
+ // granted scopes) and resolves to a runtime value (the per-app proxy socket),
8
+ // so it gets its own small registry mirroring `host-registry.ts`. Each factory
9
+ // carries its kind + hooks + meta as non-enumerable symbol properties (set by
10
+ // makeHomeable), and `deriveHomeRegistry(home)` builds the runtime map from a
11
+ // namespace import.
12
+ //
13
+ // `iterateUses` is always `() => []`: home kinds emit no elevated uses, since
14
+ // the grant alone authorizes the socket mount and Home's eval graph exempts it.
15
+ // It stays for symmetry with HostHooks.
16
+
17
+ import type { z } from "zod";
18
+ import type { HomeService } from "./resolve.ts";
19
+ import type { ResolveArgs } from "./resolve-context.ts";
20
+ import type { Builder } from "./shared.ts";
21
+ import {
22
+ tagPermissionMeta,
23
+ type PermissionMeta,
24
+ type PermissionMetaTagged,
25
+ } from "./permission-meta.ts";
26
+
27
+ // Loose constraint used inside this module to bound K on `Homeable` /
28
+ // `makeHomeable`. The closed `HomePermissionKind` lives in permissions.ts —
29
+ // kept separate so this module doesn't import schema/home.ts (a type-level
30
+ // cycle through factory.ts → shared.ts → permissions.ts → schema/home.ts).
31
+ type HomeKindShape = `home.${string}`;
32
+
33
+ export interface HomeHooks<TUse = unknown, TValue = unknown> {
34
+ iterateUses(svc: HomeService): Iterable<TUse>;
35
+ toValue(data: TUse): TValue;
36
+ /**
37
+ * Resolve against Home's eval-time world instead of the grant alone.
38
+ * Mirrors `HostHooks.resolve` — see resolve-context.ts. `home.apiAccess`
39
+ * uses it for the per-app proxy socket, which depends on the app, not the
40
+ * grant.
41
+ */
42
+ resolve?(args: ResolveArgs<TUse>): TValue;
43
+ /**
44
+ * Validator for admin-supplied grant data, used by `sanitizeGrants`.
45
+ * Falls back to the factory schema when omitted — appropriate for kinds
46
+ * where the admin grants exactly what the factory accepts (the scope
47
+ * array for `home.apiAccess`).
48
+ */
49
+ grantSchema?: z.ZodType;
50
+ }
51
+
52
+ const HOME_KIND = Symbol("realm.home.kind");
53
+ const HOME_HOOKS = Symbol("realm.home.hooks");
54
+
55
+ export interface Homeable<K extends HomeKindShape = HomeKindShape, V = unknown> {
56
+ readonly [HOME_KIND]: K;
57
+ readonly [HOME_HOOKS]: HomeHooks<unknown, V>;
58
+ }
59
+
60
+ function isHomeable(value: unknown): value is Homeable {
61
+ return (
62
+ (typeof value === "function" || (typeof value === "object" && value !== null)) &&
63
+ HOME_KIND in (value as object) &&
64
+ HOME_HOOKS in (value as object)
65
+ );
66
+ }
67
+
68
+ function tagHomeable<T extends object, K extends HomeKindShape, V>(
69
+ target: T,
70
+ kind: K,
71
+ hooks: HomeHooks<unknown, V>,
72
+ meta: PermissionMeta,
73
+ ): T & Homeable<K, V> & PermissionMetaTagged<K> {
74
+ Object.defineProperty(target, HOME_KIND, { value: kind, enumerable: false });
75
+ Object.defineProperty(target, HOME_HOOKS, { value: hooks, enumerable: false });
76
+ return tagPermissionMeta(target, kind, meta) as T & Homeable<K, V> & PermissionMetaTagged<K>;
77
+ }
78
+
79
+ /**
80
+ * Bind an inner Builder (`basePermission`) and return a factory-builder that
81
+ * produces tagged home-permission factories. Pure — no side effects on any
82
+ * registry. Mirrors `makeHostable`.
83
+ *
84
+ * const homeable = makeHomeable(basePermission);
85
+ * export const apiAccess = homeable("home.apiAccess", z.array(...), { ... }, meta);
86
+ *
87
+ * If `hooks.grantSchema` is omitted, the factory schema validates
88
+ * admin-supplied grant data.
89
+ */
90
+ export function makeHomeable<BaseR extends object, KindUnion extends HomeKindShape>(
91
+ inner: Builder<BaseR, "perm", KindUnion>,
92
+ ) {
93
+ return <S extends z.ZodType, TUse, TValue, K extends KindUnion>(
94
+ kind: K,
95
+ schema: S,
96
+ hooks: HomeHooks<TUse, TValue>,
97
+ meta: PermissionMeta,
98
+ ) => {
99
+ const factory = inner(kind, schema);
100
+ const enriched: HomeHooks<TUse, TValue> = {
101
+ ...hooks,
102
+ grantSchema: hooks.grantSchema ?? schema,
103
+ };
104
+ return tagHomeable(factory, kind, enriched as HomeHooks<unknown, TValue>, meta);
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Walk a namespace import (`import * as home from "./schema/home.ts"`) and
110
+ * build a kind→hooks map from every Homeable export.
111
+ */
112
+ export function deriveHomeRegistry(
113
+ mod: Record<string, unknown>,
114
+ ): ReadonlyMap<HomeKindShape, HomeHooks> {
115
+ const reg = new Map<HomeKindShape, HomeHooks>();
116
+ for (const value of Object.values(mod)) {
117
+ if (isHomeable(value)) reg.set(value[HOME_KIND], value[HOME_HOOKS]);
118
+ }
119
+ return reg;
120
+ }
package/src/home.ts ADDED
@@ -0,0 +1,187 @@
1
+ // Home-side primitives. Server eval and dashboard islands import from
2
+ // here. Kept off `@yourrealm/sdk`'s default entry so realm.ts bundles
3
+ // don't pull these through barrel re-exports. Also kept off `/compile`
4
+ // so islands can use the lightweight types/primitives without dragging
5
+ // esbuild + quickjs into the browser bundle.
6
+
7
+ // Error type re-export — Home catches it from bundle/sandbox.
8
+ export { RealmCompileError, SiloRequiresError, formatIssue } from "./errors.ts";
9
+ export type { CompileIssue } from "./errors.ts";
10
+
11
+ // The eval-time world a permission resolver reads from. Home builds one per
12
+ // app in graph.ts; the SDK test harness builds the same shape from fixtures.
13
+ export type {
14
+ ResolveArgs,
15
+ ResolveContext,
16
+ ResolveSilo,
17
+ SiloPublication,
18
+ } from "./resolve-context.ts";
19
+
20
+ // Resolver inputs/outputs Home walks at recompute + image-pin time.
21
+ export type { HomeApp, HomeService } from "./resolve.ts";
22
+
23
+ // Header-brokering manifest sampled from `getTrustedHeaders` - Home reads it
24
+ // off resolved state to compute per-request identity headers on every
25
+ // Home-authenticated hop into the app (the ForwardAuth verify hop and the
26
+ // UI surface loader/action hop), through the shared resolver below.
27
+ export type { TrustedHeaderSource, TrustedHeadersManifest } from "./resolve.ts";
28
+ export { resolveTrustedHeaders, stampHeaders } from "./trusted-headers.ts";
29
+ export type { TrustedHeaderViewer } from "./trusted-headers.ts";
30
+
31
+ // `host.management()` resolved shape — its resolver reads the identity off
32
+ // the eval context (`home_state` + the running worker).
33
+ export type { ManagementValue } from "./schema/host.ts";
34
+
35
+ // `host.spawn()` resolved shape — its resolver builds the per-app paths at
36
+ // eval, and `collectAllowed` reads them to exempt the socket/scratch/artifacts
37
+ // paths from the absolute-mount gate.
38
+ export type { SpawnValue } from "./worker-protocol/spawn-paths.ts";
39
+
40
+ // `host.silo()` — the author's declared affordances (`SiloGrants`, the
41
+ // descriptor's `data`) and the resolved shape its resolver builds from the Silo
42
+ // record (`SiloResolved`, whose `volume` the app mounts verbatim).
43
+ // `siloRequiresUnmetMessage` is the shared guided text for a require-map the
44
+ // Silo can't satisfy — Home eval and the SDK harness emit it byte-identically.
45
+ export { siloRequiresUnmetMessage } from "./schema/host.ts";
46
+ export type {
47
+ SiloGrants,
48
+ SiloHostPath,
49
+ SiloRequireUnmet,
50
+ SiloResolved,
51
+ SiloVolume,
52
+ } from "./schema/host.ts";
53
+
54
+ // Generic config helper Home uses to merge config + resolved permission
55
+ // values before handing them to the sandbox.
56
+ export { deepMergeConfig } from "./deep-merge.ts";
57
+
58
+ // SerializablePermission — shape coming out of the sandbox. Home UI
59
+ // indexes by it; server eval threads it through the gate.
60
+ export type { SerializablePermission } from "./sandbox.ts";
61
+
62
+ // Grant + use shapes. Home enforces via the path-aware checkAppGrants in
63
+ // security.ts, which builds on collectUses + hostHooks.
64
+ export {
65
+ toValue,
66
+ resolveValue,
67
+ collectUses,
68
+ collectHomeApiScopes,
69
+ hostHooks,
70
+ homeHooks,
71
+ isHostGrant,
72
+ isHomeGrant,
73
+ } from "./grants.ts";
74
+ export type {
75
+ HomeGrant,
76
+ Grant,
77
+ GrantOf,
78
+ Grants,
79
+ GrantsOf,
80
+ HostGrant,
81
+ PeerGrant,
82
+ RemoteGrant,
83
+ Use,
84
+ ValueOf,
85
+ } from "./grants.ts";
86
+
87
+ // Permission kinds (closed sets, derived from schema/* factories).
88
+ export type {
89
+ HomePermissionKind,
90
+ HostPermissionKind,
91
+ WorkerCapability,
92
+ PeerPermissionKind,
93
+ RemotePermissionKind,
94
+ } from "./permissions.ts";
95
+
96
+ // Host + home registry hooks (per-kind iterateUses / toValue / grantSchema).
97
+ export type { HostHooks } from "./host-registry.ts";
98
+ export type { HomeHooks } from "./home-registry.ts";
99
+
100
+ // `home.apiAccess()` resolved shape — its resolver builds `socket` from the
101
+ // app's per-app proxy path at eval time.
102
+ export type { HomeApiAccessValue } from "./schema/home.ts";
103
+
104
+ // Peer-world helpers + types (per-kind extract/resolve hooks, world
105
+ // state aggregation, allow-list extraction for ref resolution).
106
+ export {
107
+ extractPeerEntries,
108
+ resolvePeerValue,
109
+ isPeerGranted,
110
+ isBuildAppEntry,
111
+ peerHooks,
112
+ peerEntries,
113
+ } from "./peer-world.ts";
114
+ export type { BuildAppWorldEntry, WorldEntry, WorldState } from "./peer-world.ts";
115
+ export type { AllowedProvider, PeerHooks } from "./peer-registry.ts";
116
+
117
+ // Peer entry types — also on the author barrel because realm.ts can
118
+ // reference them in `getDesiredState`, but Home eval walks them too so
119
+ // they live here as well.
120
+ export type {
121
+ BackupStrategy,
122
+ BackupTarget,
123
+ HostPortEntry,
124
+ NetworkStrategy,
125
+ PeerHostPath,
126
+ RoutedAppEntry,
127
+ VolumeStrategy,
128
+ } from "./schema/peer.ts";
129
+
130
+ // Identifier schemas + Network brand — Home eval references them when
131
+ // building world state, validating image refs, etc.
132
+ export { nameSchema, domainSchema, networkSchema, hostKey } from "./identifiers.ts";
133
+ export type { Network } from "./identifiers.ts";
134
+
135
+ // The public-domain grant value. Home parses stored grants through this to
136
+ // recover the hosts an app claims, which is also how each `domain_claim` key
137
+ // inherits the schema's hostname normalization instead of repeating it.
138
+ export { publicDomainValueSchema } from "./schema/public-domain.ts";
139
+ export type { PublicDomainGrantData } from "./schema/public-domain.ts";
140
+
141
+ // Security primitives — write-time sanitize + runtime path-aware gate.
142
+ export {
143
+ HOST_DIRECTORY_BLOCKLIST,
144
+ isValidHostDirectoryPath,
145
+ normalizeHostPath,
146
+ pathsOverlap,
147
+ grantsSchema,
148
+ sanitizeGrants,
149
+ checkAppGrants,
150
+ checkPermissionAppKinds,
151
+ checkPublicDomains,
152
+ isUnderBaseDomain,
153
+ checkSystemAppRoutes,
154
+ siloMountKey,
155
+ } from "./security.ts";
156
+ export type {
157
+ GrantViolation,
158
+ EvalErrorViolation,
159
+ WorkerCapabilityViolation,
160
+ AppKindViolation,
161
+ RoutingViolation,
162
+ Violation,
163
+ GrantIssue,
164
+ SanitizeResult,
165
+ } from "./security.ts";
166
+
167
+ // Per-kind presentation metadata + closed-set kind list.
168
+ export {
169
+ PERMISSION_META,
170
+ PERMISSION_KINDS,
171
+ HOST_PERMISSION_KINDS,
172
+ WORKER_CAPABILITIES,
173
+ } from "./permissions.ts";
174
+ export type { PermissionMeta } from "./permissions.ts";
175
+ export type { PermissionKind } from "./permissions.ts";
176
+
177
+ // Required-vs-optional host-kind partition. Home eval uses it to drop
178
+ // workers that don't advertise an app's required kinds; the install
179
+ // panel uses it to gray out optional kinds no worker can satisfy.
180
+ export { partitionHostKinds, stateRequiresVolumes } from "./required-host-kinds.ts";
181
+ export type { PartitionedHostKinds } from "./required-host-kinds.ts";
182
+
183
+ // Pure grant-building helper shared between the server install path
184
+ // and the dashboard islands. Kept off the deeper barrels so client
185
+ // islands can use it without dragging drizzle / db / sandbox into the
186
+ // browser bundle.
187
+ export { descriptorsToGrants } from "./grants-builder.ts";
@@ -0,0 +1,143 @@
1
+ // Per-kind Home-side mechanics for host permissions.
2
+ //
3
+ // Each host factory carries its kind + hooks + meta as non-enumerable symbol
4
+ // properties (set by makeHostable / tagHostable), and the runtime registry is
5
+ // derived from a namespace import (`deriveHostRegistry(host)`) — same pattern
6
+ // as peer-registry.ts. The hooks bundle what grants.ts and Home need per kind:
7
+ // `iterateUses` walks a resolved service and yields each elevated thing it does
8
+ // of this kind (a port bind, a directory mount), `toValue` projects a grant's
9
+ // data into the value the SDK hands to `getDesiredState`, and the optional
10
+ // `resolve` / `grantSchema` are documented on HostHooks below. Per-kind
11
+ // presentation strings travel as a separate `meta` argument and land via
12
+ // tagPermissionMeta, picked up by derivePermissionMeta in permissions.ts.
13
+ //
14
+ // `ValueOf<G>` in grants.ts is the type-level dual of toValue, derived by
15
+ // walking `typeof host` and reading `Hostable<K, V>` off each export's tag —
16
+ // V is the inferred return type of `toValue`, threaded through makeHostable.
17
+
18
+ import type { z } from "zod";
19
+ import type { HomeService } from "./resolve.ts";
20
+ import type { ResolveArgs } from "./resolve-context.ts";
21
+ import type { Builder } from "./shared.ts";
22
+ import {
23
+ tagPermissionMeta,
24
+ type PermissionMeta,
25
+ type PermissionMetaTagged,
26
+ } from "./permission-meta.ts";
27
+
28
+ // Loose constraint used inside this module to bound K on `Hostable` /
29
+ // `makeHostable`. The closed `HostPermissionKind` lives in permissions.ts —
30
+ // kept separate so this module doesn't import schema/host.ts, which would form
31
+ // a type-level cycle through factory.ts → shared.ts → permissions.ts →
32
+ // schema/host.ts.
33
+ type HostKindShape = `host.${string}`;
34
+
35
+ export interface HostHooks<TUse = unknown, TValue = unknown> {
36
+ iterateUses(svc: HomeService): Iterable<TUse>;
37
+ toValue(data: TUse): TValue;
38
+ /**
39
+ * Resolve against Home's eval-time world instead of the grant alone.
40
+ *
41
+ * `toValue` handles the kinds where the grant already holds the answer.
42
+ * Declare `resolve` for the kinds where it doesn't - a Silo id means
43
+ * nothing without the Silo record, so `host.silo` reads it off
44
+ * `args.ctx`. When present it replaces `toValue` on the resolution path;
45
+ * `toValue` stays as the type-level source for `ValueOf`.
46
+ *
47
+ * Synchronous by construction: the context carries data, never a lookup
48
+ * that could await. See resolve-context.ts.
49
+ */
50
+ resolve?(args: ResolveArgs<TUse>): TValue;
51
+ /**
52
+ * Validator for admin-supplied grant data, used by `sanitizeGrants` /
53
+ * `grantsSchema`. Falls back to the factory schema when omitted —
54
+ * appropriate for kinds where the admin grants exactly what the
55
+ * factory accepts (port, capability, etc.). Override per-kind when
56
+ * grant data has a different shape than the factory's author input
57
+ * (e.g. `host.silo`: factory takes the asked flags but grants carry
58
+ * absolute paths plus a blocklist).
59
+ */
60
+ grantSchema?: z.ZodType;
61
+ }
62
+
63
+ const HOST_KIND = Symbol("realm.host.kind");
64
+ const HOST_HOOKS = Symbol("realm.host.hooks");
65
+
66
+ export interface Hostable<K extends HostKindShape = HostKindShape, V = unknown> {
67
+ readonly [HOST_KIND]: K;
68
+ readonly [HOST_HOOKS]: HostHooks<unknown, V>;
69
+ }
70
+
71
+ function isHostable(value: unknown): value is Hostable {
72
+ return (
73
+ (typeof value === "function" || (typeof value === "object" && value !== null)) &&
74
+ HOST_KIND in (value as object) &&
75
+ HOST_HOOKS in (value as object)
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Attach kind + hooks + meta to an arbitrary value (mirrors tagPeerable
81
+ * for the rare wrapper case). Almost every host kind goes through
82
+ * makeHostable directly, but the helper is here for symmetry.
83
+ */
84
+ function tagHostable<T extends object, K extends HostKindShape, V>(
85
+ target: T,
86
+ kind: K,
87
+ hooks: HostHooks<unknown, V>,
88
+ meta: PermissionMeta,
89
+ ): T & Hostable<K, V> & PermissionMetaTagged<K> {
90
+ Object.defineProperty(target, HOST_KIND, { value: kind, enumerable: false });
91
+ Object.defineProperty(target, HOST_HOOKS, { value: hooks, enumerable: false });
92
+ return tagPermissionMeta(target, kind, meta) as T & Hostable<K, V> & PermissionMetaTagged<K>;
93
+ }
94
+
95
+ /**
96
+ * Bind an inner Builder (typically `basePermission`) and return a
97
+ * factory-builder that produces tagged host-permission factories. Pure —
98
+ * no side effects on any registry. The runtime registry is derived from
99
+ * a namespace import via `deriveHostRegistry`.
100
+ *
101
+ * Composes with the factory mixins: `makeHostable(basePermission)` reads
102
+ * the same shape as `makeOptional(makeReasonable(makePermRecord))`.
103
+ *
104
+ * const hostable = makeHostable(basePermission);
105
+ * export const port = hostable("host.port", z.number()..., {
106
+ * iterateUses, toValue,
107
+ * });
108
+ *
109
+ * If `hooks.grantSchema` is omitted, the factory schema is used to
110
+ * validate admin-supplied grant data — appropriate for kinds where
111
+ * authors and admins supply the same shape.
112
+ */
113
+ export function makeHostable<BaseR extends object, KindUnion extends HostKindShape>(
114
+ inner: Builder<BaseR, "perm", KindUnion>,
115
+ ) {
116
+ return <S extends z.ZodType, TUse, TValue, K extends KindUnion>(
117
+ kind: K,
118
+ schema: S,
119
+ hooks: HostHooks<TUse, TValue>,
120
+ meta: PermissionMeta,
121
+ ) => {
122
+ const factory = inner(kind, schema);
123
+ const enriched: HostHooks<TUse, TValue> = {
124
+ ...hooks,
125
+ grantSchema: hooks.grantSchema ?? schema,
126
+ };
127
+ return tagHostable(factory, kind, enriched as HostHooks<unknown, TValue>, meta);
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Walk a namespace import (e.g. `import * as host from "./schema/host.ts"`)
133
+ * and build a kind→hooks map from every Hostable export.
134
+ */
135
+ export function deriveHostRegistry(
136
+ mod: Record<string, unknown>,
137
+ ): ReadonlyMap<HostKindShape, HostHooks> {
138
+ const reg = new Map<HostKindShape, HostHooks>();
139
+ for (const value of Object.values(mod)) {
140
+ if (isHostable(value)) reg.set(value[HOST_KIND], value[HOST_HOOKS]);
141
+ }
142
+ return reg;
143
+ }
@@ -0,0 +1,20 @@
1
+ // What HTTP allows in a header name and value, as one definition.
2
+ //
3
+ // Two places need it and must not drift. The worker's `/ui-proxy` enforces it
4
+ // at request time and rejects the WHOLE call on a violation, so a single bad
5
+ // character takes down every loader and action into an app. `getTrustedHeaders`
6
+ // sampling enforces the same rule at compile time, so such an app fails to
7
+ // install with a message naming the header instead of going dark in the field.
8
+ //
9
+ // Pure regex literals, no imports: this module is reachable from the author
10
+ // barrel and therefore loads inside QuickJS.
11
+
12
+ /** RFC 7230 `token`: what may appear in a header field name. */
13
+ export const HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/;
14
+
15
+ /**
16
+ * Printable ASCII, the safe subset of a header field value. Excludes CR and LF
17
+ * (response splitting) and everything above 0x7e, which has no interoperable
18
+ * encoding in a header.
19
+ */
20
+ export const HEADER_VALUE_RE = /^[\x20-\x7e]*$/;