@astrale-os/sdk 0.2.0 → 0.3.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 (160) hide show
  1. package/LICENSE +202 -0
  2. package/dist/auth/check.d.ts +1 -2
  3. package/dist/auth/check.d.ts.map +1 -1
  4. package/dist/auth/check.js +2 -6
  5. package/dist/auth/check.js.map +1 -1
  6. package/dist/auth/index.d.ts +2 -0
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js +1 -0
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/auth/issuer-mint.d.ts +53 -0
  11. package/dist/auth/issuer-mint.d.ts.map +1 -0
  12. package/dist/auth/issuer-mint.js +35 -0
  13. package/dist/auth/issuer-mint.js.map +1 -0
  14. package/dist/auth/kernel-client.d.ts +8 -2
  15. package/dist/auth/kernel-client.d.ts.map +1 -1
  16. package/dist/auth/kernel-client.js +22 -46
  17. package/dist/auth/kernel-client.js.map +1 -1
  18. package/dist/cli/dotenv.d.ts +8 -0
  19. package/dist/cli/dotenv.d.ts.map +1 -1
  20. package/dist/cli/dotenv.js +18 -0
  21. package/dist/cli/dotenv.js.map +1 -1
  22. package/dist/cli/index.d.ts +1 -1
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +1 -1
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/log.d.ts +11 -0
  27. package/dist/cli/log.d.ts.map +1 -0
  28. package/dist/cli/log.js +17 -0
  29. package/dist/cli/log.js.map +1 -0
  30. package/dist/cli/publish.d.ts +35 -0
  31. package/dist/cli/publish.d.ts.map +1 -0
  32. package/dist/cli/publish.js +271 -0
  33. package/dist/cli/publish.js.map +1 -0
  34. package/dist/cli/run.d.ts +17 -1
  35. package/dist/cli/run.d.ts.map +1 -1
  36. package/dist/cli/run.js +85 -101
  37. package/dist/cli/run.js.map +1 -1
  38. package/dist/cli/spec.d.ts.map +1 -1
  39. package/dist/cli/spec.js +1 -0
  40. package/dist/cli/spec.js.map +1 -1
  41. package/dist/config/define-domain.d.ts +79 -2
  42. package/dist/config/define-domain.d.ts.map +1 -1
  43. package/dist/config/define-domain.js +58 -1
  44. package/dist/config/define-domain.js.map +1 -1
  45. package/dist/config/index.d.ts +1 -1
  46. package/dist/config/index.d.ts.map +1 -1
  47. package/dist/define/remote-function.d.ts +23 -3
  48. package/dist/define/remote-function.d.ts.map +1 -1
  49. package/dist/define/remote-function.js +1 -1
  50. package/dist/define/remote-function.js.map +1 -1
  51. package/dist/deploy/check.d.ts +6 -6
  52. package/dist/deploy/check.d.ts.map +1 -1
  53. package/dist/deploy/check.js +1 -1
  54. package/dist/deploy/check.js.map +1 -1
  55. package/dist/deploy/index.d.ts +0 -1
  56. package/dist/deploy/index.d.ts.map +1 -1
  57. package/dist/deploy/index.js +0 -1
  58. package/dist/deploy/index.js.map +1 -1
  59. package/dist/deploy/meta.d.ts +11 -0
  60. package/dist/deploy/meta.d.ts.map +1 -1
  61. package/dist/deploy/meta.js +23 -0
  62. package/dist/deploy/meta.js.map +1 -1
  63. package/dist/dispatch/dispatcher.d.ts +17 -4
  64. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  65. package/dist/dispatch/dispatcher.js +15 -3
  66. package/dist/dispatch/dispatcher.js.map +1 -1
  67. package/dist/dispatch/execute.d.ts +6 -2
  68. package/dist/dispatch/execute.d.ts.map +1 -1
  69. package/dist/dispatch/execute.js +1 -0
  70. package/dist/dispatch/execute.js.map +1 -1
  71. package/dist/dispatch/identity.d.ts +11 -10
  72. package/dist/dispatch/identity.d.ts.map +1 -1
  73. package/dist/dispatch/identity.js +4 -3
  74. package/dist/dispatch/identity.js.map +1 -1
  75. package/dist/dispatch/index.d.ts +1 -1
  76. package/dist/dispatch/index.d.ts.map +1 -1
  77. package/dist/dispatch/index.js +1 -1
  78. package/dist/dispatch/index.js.map +1 -1
  79. package/dist/dispatch/self.d.ts +25 -37
  80. package/dist/dispatch/self.d.ts.map +1 -1
  81. package/dist/dispatch/self.js +60 -30
  82. package/dist/dispatch/self.js.map +1 -1
  83. package/dist/domain/build-spec.d.ts.map +1 -1
  84. package/dist/domain/build-spec.js +15 -3
  85. package/dist/domain/build-spec.js.map +1 -1
  86. package/dist/domain/define.d.ts +17 -32
  87. package/dist/domain/define.d.ts.map +1 -1
  88. package/dist/domain/define.js +12 -31
  89. package/dist/domain/define.js.map +1 -1
  90. package/dist/domain/extend-functions.d.ts +13 -9
  91. package/dist/domain/extend-functions.d.ts.map +1 -1
  92. package/dist/domain/extend-functions.js +28 -13
  93. package/dist/domain/extend-functions.js.map +1 -1
  94. package/dist/index.d.ts +7 -3
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +12 -4
  97. package/dist/index.js.map +1 -1
  98. package/dist/method/class.d.ts +2 -2
  99. package/dist/method/class.d.ts.map +1 -1
  100. package/dist/method/class.js.map +1 -1
  101. package/dist/method/context.d.ts +18 -3
  102. package/dist/method/context.d.ts.map +1 -1
  103. package/dist/method/index.d.ts +2 -2
  104. package/dist/method/index.d.ts.map +1 -1
  105. package/dist/method/index.js.map +1 -1
  106. package/dist/method/single.d.ts +25 -38
  107. package/dist/method/single.d.ts.map +1 -1
  108. package/dist/method/single.js +1 -11
  109. package/dist/method/single.js.map +1 -1
  110. package/dist/server/auxiliary-routes.d.ts.map +1 -1
  111. package/dist/server/auxiliary-routes.js +13 -2
  112. package/dist/server/auxiliary-routes.js.map +1 -1
  113. package/dist/server/create.d.ts +2 -15
  114. package/dist/server/create.d.ts.map +1 -1
  115. package/dist/server/create.js +15 -51
  116. package/dist/server/create.js.map +1 -1
  117. package/dist/server/domain-entry.d.ts +10 -0
  118. package/dist/server/domain-entry.d.ts.map +1 -1
  119. package/dist/server/domain-entry.js +2 -0
  120. package/dist/server/domain-entry.js.map +1 -1
  121. package/dist/server/index.d.ts +0 -1
  122. package/dist/server/index.d.ts.map +1 -1
  123. package/dist/server/index.js +0 -1
  124. package/dist/server/index.js.map +1 -1
  125. package/package.json +10 -8
  126. package/src/auth/check.ts +2 -6
  127. package/src/auth/index.ts +2 -0
  128. package/src/auth/issuer-mint.ts +75 -0
  129. package/src/auth/kernel-client.ts +31 -63
  130. package/src/cli/dotenv.ts +20 -0
  131. package/src/cli/index.ts +1 -1
  132. package/src/cli/log.ts +18 -0
  133. package/src/cli/publish.ts +341 -0
  134. package/src/cli/run.ts +92 -113
  135. package/src/cli/spec.ts +1 -0
  136. package/src/config/define-domain.ts +158 -5
  137. package/src/config/index.ts +1 -1
  138. package/src/define/remote-function.ts +47 -7
  139. package/src/deploy/check.ts +7 -7
  140. package/src/deploy/index.ts +0 -1
  141. package/src/deploy/meta.ts +25 -0
  142. package/src/dispatch/dispatcher.ts +32 -9
  143. package/src/dispatch/execute.ts +7 -2
  144. package/src/dispatch/identity.ts +11 -10
  145. package/src/dispatch/index.ts +1 -1
  146. package/src/dispatch/self.ts +117 -41
  147. package/src/domain/build-spec.ts +15 -1
  148. package/src/domain/define.ts +26 -38
  149. package/src/domain/extend-functions.ts +35 -15
  150. package/src/index.ts +29 -5
  151. package/src/method/class.ts +11 -7
  152. package/src/method/context.ts +19 -3
  153. package/src/method/index.ts +8 -2
  154. package/src/method/single.ts +35 -42
  155. package/src/server/auxiliary-routes.ts +13 -2
  156. package/src/server/create.ts +15 -51
  157. package/src/server/domain-entry.ts +12 -0
  158. package/src/server/index.ts +0 -1
  159. package/src/deploy/hash-spec.ts +0 -31
  160. package/src/server/worker-meta.ts +0 -25
@@ -1,14 +1,4 @@
1
- /**
2
- * Authoring a single remote method.
3
- *
4
- * `RemoteHandler` is the typed shape an author writes for ONE method:
5
- * an `execute` body plus optional REST `route` (using the kernel's
6
- * canonical `RouteBinding`) and optional anchor `remoteUrl`.
7
- *
8
- * `MethodImpl` resolves params/result/self from a schema for a given
9
- * `(class, method)` pair. `remoteMethod` is the identity helper an author
10
- * calls per method to get full inference without writing generics by hand.
11
- */
1
+ /** Authoring helpers for one schema-bound remote method. */
12
2
 
13
3
  import type { AuthPolicy, RouteBinding } from '@astrale-os/kernel-api/routed'
14
4
  import type {
@@ -19,21 +9,12 @@ import type {
19
9
  } from '@astrale-os/kernel-core/domain'
20
10
  import type { Schema } from '@astrale-os/kernel-dsl'
21
11
 
22
- import type { SelfResult } from '../dispatch/self'
12
+ import type { TypedSelf } from '../dispatch/self'
23
13
  import type { KernelForAuth, RemoteContext } from './context'
24
14
 
25
15
  /**
26
- * Remote function handler execute with full typed context.
27
- *
28
- * `execute` is **optional** at the type level: a handler may be a pure
29
- * binding stub (only `remoteUrl` / `route` set) when authored as a spec-side
30
- * declaration that never runs locally — the kernel resolver short-circuits
31
- * via `binding.remoteUrl` before reaching the handler body. The worker-side
32
- * declaration that actually serves the call MUST provide `execute`.
33
- *
34
- * Runtime guard: if dispatch routes to a handler that has no `execute`, the
35
- * dispatcher throws — that's a programmer error indicating a stub leaked into
36
- * a code path that was supposed to execute it.
16
+ * Remote method handler. `execute` is optional only for binding stubs; any
17
+ * handler that the dispatcher actually reaches must provide it.
37
18
  */
38
19
  export type RemoteHandler<TParams, TResult, TSelf, TDeps, TAuth extends AuthPolicy = 'required'> = {
39
20
  /** The handler body. May be async or an async generator (for `output: 'stream'`). */
@@ -62,30 +43,34 @@ export type RemoteHandler<TParams, TResult, TSelf, TDeps, TAuth extends AuthPoli
62
43
  */
63
44
  auth?: TAuth
64
45
  /**
65
- * Optional pre-execute authorization check. Runs after auth resolution and
66
- * `_self` resolution, before `execute`. Throw any error to deny the call —
67
- * the SDK wraps it as `AuthorizationDeniedError` (mapped to wire-level
68
- * `PERMISSION_DENIED`).
69
- *
70
- * Use for fine-grained checks the kernel's bit-level perms can't express
71
- * (e.g. "only the project lead can addMember"). For straight bit-level
72
- * checks, prefer the helpers in `@astrale-os/sdk` (`assertPerm`,
73
- * `requireOwnership`).
74
- *
75
- * The kernel still enforces `has_perm` independently — `authorize` is
76
- * additive ergonomic gating on top, not a replacement.
46
+ * Optional business authorization after auth/_self resolution. Kernel
47
+ * has_perm checks remain authoritative; this is additive gating.
77
48
  */
78
49
  authorize?: (
79
50
  ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>, TAuth>,
80
51
  ) => void | Promise<void>
81
52
  }
82
53
 
83
- // `TAuth = AuthPolicy` (the full union, not the `'required'` default) keeps this
84
- // maximally permissive: it accepts a handler of ANY declared policy, and its
85
- // `ctx.kernel` widens to `BoundClientSessionView<FnMap> | null`. Used by the
86
- // dispatcher/index where the concrete policy is erased.
87
- // oxlint-disable-next-line no-explicit-any
88
- export type AnyRemoteHandler = RemoteHandler<any, any, any, any, AuthPolicy>
54
+ // Post-binding indexes erase the concrete auth policy; authoring remains checked
55
+ // at `remoteMethod`, while dispatcher/build-spec only need a policy-neutral bag.
56
+ // oxlint-disable no-explicit-any
57
+ export type AnyRemoteHandler = Omit<
58
+ RemoteHandler<any, any, any, any, AuthPolicy>,
59
+ 'authorize' | 'execute'
60
+ > & {
61
+ execute?: (ctx: any) => any
62
+ authorize?: (ctx: any) => void | Promise<void>
63
+ }
64
+ // oxlint-enable no-explicit-any
65
+
66
+ // Compile-time guard: every auth policy must fit the erased handler bag.
67
+ type _AssertTrueH<T extends true> = T
68
+ type _HandlerFitsBag<D> = D extends AnyRemoteHandler ? true : false
69
+ export type _MethodPolicyBagGuards = [
70
+ _AssertTrueH<_HandlerFitsBag<RemoteHandler<unknown, unknown, unknown, unknown, 'public'>>>,
71
+ _AssertTrueH<_HandlerFitsBag<RemoteHandler<unknown, unknown, unknown, unknown, 'optional'>>>,
72
+ _AssertTrueH<_HandlerFitsBag<RemoteHandler<unknown, unknown, unknown, unknown, 'required'>>>,
73
+ ]
89
74
 
90
75
  /**
91
76
  * Fully typed method implementation — resolves params/result/self from the
@@ -104,9 +89,17 @@ export type MethodImpl<
104
89
  self: unknown
105
90
  isStatic: infer St
106
91
  }
107
- ? RemoteHandler<P, R, St extends true ? undefined : SelfResult, TDeps, TAuth>
92
+ ? RemoteHandler<P, R, St extends true ? undefined : TypedSelf<S['classes'][K], S>, TDeps, TAuth>
108
93
  : never
109
94
 
95
+ /**
96
+ * `MethodParams` / `MethodResult` — a handler types its `params` as
97
+ * `MethodParams<typeof schema, 'Def', 'method'>` and its return as
98
+ * `MethodResult<…>`, so it never re-declares (and can't drift from) the schema.
99
+ * Resolved from whichever def — CLASS or INTERFACE — declares the method.
100
+ */
101
+ export type { MethodParams, MethodResult } from '@astrale-os/kernel-core/domain'
102
+
110
103
  type ImplementableMethodName<S extends Schema, K extends MethodClassKeys<S> & string> = (
111
104
  | OwnMethodKeys<S, K>
112
105
  | NonSealedMethodKeys<S, K>
@@ -39,6 +39,7 @@ import type { ViewDef } from '../define/view'
39
39
  import type { CallRemoteFn } from '../dispatch/call-remote'
40
40
  import type { AuxIdentityMap } from '../dispatch/identity'
41
41
 
42
+ import { makeIssuerMint } from '../auth/issuer-mint'
42
43
  import { makeSelfKernel } from '../auth/kernel-client'
43
44
  import { resolveInboundAuth } from '../auth/resolve'
44
45
  import { runAuthorize } from '../dispatch/authorize'
@@ -136,7 +137,17 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
136
137
  if (!validation.ok) {
137
138
  throw new SdkValidationError(validation.issues as SdkValidationError['issues'])
138
139
  }
139
- const ctx = { params: validation.data, c, auth, deps, kernel, callRemote, selfKernel }
140
+ const issuer = makeIssuerMint(identity)
141
+ const ctx = {
142
+ params: validation.data,
143
+ c,
144
+ auth,
145
+ deps,
146
+ kernel,
147
+ issuer,
148
+ callRemote,
149
+ selfKernel,
150
+ }
140
151
  if (def.authorize) await runAuthorize(def.authorize, ctx)
141
152
  const result = await def.execute(ctx)
142
153
  const outValidation = validateResult(def.outputSchema, result)
@@ -216,7 +227,7 @@ function mountEntry(args: MountEntryArgs): void {
216
227
  )
217
228
  }
218
229
  // Local-dev requests target literal `localhost`, but bindings reference a
219
- // logical host (`dist.localhost`) — so only enforce a Host-header match
230
+ // logical host (`example.localhost`) — so only enforce a Host-header match
220
231
  // when the binding has actual placeholders to extract.
221
232
  const hostMatcher =
222
233
  parsed.hostPlaceholders.length > 0 ? compileHostMatcher(parsed.hostPattern) : null
@@ -1,19 +1,6 @@
1
1
  /**
2
- * `createRemoteServer` the SDK's entry point for running a remote domain.
3
- *
4
- * Identity is per-function: the dispatcher signs `iss` = the worker's serving
5
- * URL (`effectiveIssuer`, decoupled from the addressing `origin`) and `sub` =
6
- * the origin-addressed function path on each dispatch.
7
- *
8
- * Composes:
9
- * methods ← Map keyed by BoundMethod.ref (built by dispatch/resolve)
10
- * effectiveIssuer ← config.issuer ?? config.url
11
- * dispatcher ← SdkDispatcher(compiled, methods, deps, privateKey)
12
- * jwks ← derivePublicJwk(privateKey), keyed by effectiveIssuer
13
- * /meta ← provenance endpoint (sdkCommit, schemaHash, domainName)
14
- * auxiliary routes ← view / remote-function handlers from defineRemoteDomain
15
- * app ← createKernelApp(dispatcher, contracts, host, jwks, transports, ...)
16
- * start ← startNodeServer(app, port)
2
+ * Remote-domain server entry point. The worker serving URL is the issuer; each
3
+ * method/view/function signs with its own semantic subject.
17
4
  */
18
5
 
19
6
  import type { JwksKeys } from '@astrale-os/kernel-server'
@@ -45,21 +32,18 @@ import { canonicalizeServingUrl } from './serving-url'
45
32
 
46
33
  export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): RemoteServer {
47
34
  const methods = buildMethodIndex(config.domain.methods)
48
- // The worker's identity (`iss`) is its full serving URL (base path included,
49
- // trailing slash stripped) — decoupled from the addressing `origin` slug. One
50
- // canonical value drives outbound signing, the JWKS issuer, `/meta`, and the
51
- // install credential. Must equal the URL the kernel fetched the domain at.
35
+ // Canonical serving URL drives signing, JWKS, /meta, and install credentials.
52
36
  const iss = canonicalizeServingUrl(config.url)
53
37
 
54
- // Re-materialize the domain with the real serving url (`iss`) so the aux
55
- // View/Function `binding.remoteUrl` resolve to this host — the define-time
56
- // placeholder is discarded. `compiled`/`auxiliary` drive everything below;
57
- // `iss` is the single source for both bindings and identity.
38
+ // Stamp aux bindings against the same URL used as issuer.
58
39
  const { compiled, auxiliary } = materializeRemoteDomain(config.domain, iss)
59
40
 
60
41
  const dispatcher = new SdkDispatcher<TDeps>({
61
42
  compiled,
62
43
  methods,
44
+ // The source schema drives the rich typed `self` handed to non-static
45
+ // handlers (`self.node()` → BoundNode<D>, typed `self.call`).
46
+ schema: compiled.$.schema,
63
47
  deps: config.deps,
64
48
  privateKey: config.privateKey,
65
49
  issuer: iss,
@@ -74,12 +58,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
74
58
  loadOwnKeys: async () => [publicJwk],
75
59
  }
76
60
 
77
- // `/meta`'s `schemaHash` is computed from the LIVE domain graph (lazy +
78
- // cached). `hashInstallGraph` is id-independent/deterministic, so an
79
- // independent build (a deploy script) produces the SAME hash — `deployCheck`
80
- // can compare its expected value against this and detect genuine schema drift.
81
- // An explicit `config.meta.schemaHash` still wins as an override for
82
- // offline/pinned spec producers, but normal deploys omit it.
61
+ // Normal /meta schemaHash is the deterministic install-graph hash; explicit overrides win.
83
62
  let cachedSchemaHash: Promise<string> | null = null
84
63
  const resolveSchemaHash = (): Promise<string> =>
85
64
  config.meta?.schemaHash !== undefined
@@ -93,6 +72,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
93
72
  iss,
94
73
  sdkCommit: config.meta?.sdkCommit,
95
74
  domainName: config.meta?.domainName ?? compiled.$.origin,
75
+ ...(config.domain.manifest ? { manifest: config.domain.manifest } : {}),
96
76
  }
97
77
  hostApp.get('/meta', async (c) =>
98
78
  c.json(MetaSchema.parse({ ...metaBase, schemaHash: await resolveSchemaHash() })),
@@ -116,23 +96,16 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
116
96
  return c.json({ error: 'Install denied', message: (err as Error).message }, 403)
117
97
  }
118
98
 
119
- // Build the install graph. `buildInstallGraph` re-materializes the domain
120
- // with the serving url (`iss`), so every `binding.remoteUrl` points at this
121
- // host — the same single value as the credential's `iss` below. The kernel
122
- // hashes exactly this graph; no install-time rewrite.
99
+ // The signed graph is built for this serving URL; the kernel verifies it as-is.
123
100
  const graph = buildInstallGraph(config.domain, iss)
124
101
  const graphHash = await hashInstallGraph(graph)
125
102
  const origin = compiled.$.origin
126
- // `postInstall` + `requires` ride in BOTH the signed credential claims and
127
- // the bundle body, and the kernel rejects any bundle field that disagrees
128
- // with its signed claim — so derive them once and reuse for both.
103
+ // Signed claims and bundle fields must agree exactly.
129
104
  const bundleExtras = {
130
105
  ...(config.postInstall ? { postInstall: config.postInstall } : {}),
131
106
  ...(config.requires && config.requires.length > 0 ? { requires: config.requires } : {}),
132
107
  }
133
- // The credential's `iss` is the worker's serving URL (`iss`, computed above)
134
- // — the kernel pins it to the URL it fetched the domain at and verifies this
135
- // credential against that issuer's JWKS.
108
+ // Install credential issuer is the serving URL the kernel fetched.
136
109
  const credential = await signInstallCredential({
137
110
  privateKey: config.privateKey,
138
111
  issuer: iss,
@@ -155,12 +128,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
155
128
  // the aux routes (mountAuxiliaryRoutes) so both honor the same policy.
156
129
  const cors = config.cors ?? { origin: '*' }
157
130
 
158
- // Worker-side wires for defineView / defineRemoteFunction. Mounted before
159
- // the kernel catch-all. Each aux route gets its own per-slug identity
160
- // (issuer + key + the aux node's AbsolutePath as subject) so outbound
161
- // `kernel.call(...)` from a handler signs with that path — matching the
162
- // identity the install-time `subs` claim registered for the node, the
163
- // same way Methods work.
131
+ // Aux routes mount before the kernel catch-all and sign with their member subjects.
164
132
  if (auxiliary) {
165
133
  const auxIdentities = buildAuxIdentityMap(compiled, config.privateKey, iss)
166
134
  mountAuxiliaryRoutes<TDeps>({
@@ -191,10 +159,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
191
159
 
192
160
  return {
193
161
  app,
194
- // The canonical serving URL = the worker's `iss` identity. Exposed so a
195
- // worker that also seeds `Identity.iss` on graph nodes (e.g. recruitment's
196
- // self-seed) stamps the SAME canonical value the dispatcher signs with —
197
- // never the raw env.WORKER_URL — so the kernel's exact-match lookup resolves.
162
+ // Expose the canonical issuer, not the raw env URL, for graph identity stamping.
198
163
  iss,
199
164
  async start(port?: number): Promise<RemoteServerHandle> {
200
165
  const nodeStartModule = './start'
@@ -230,8 +195,7 @@ async function signInstallCredential(args: {
230
195
  const header = typeof kid === 'string' ? { alg, kid } : { alg }
231
196
  const key = await importJWK(args.privateKey, alg)
232
197
 
233
- // `postInstall` + `requires` are signed (not just carried in the bundle) so a
234
- // MITM can't retarget the system-authority hook or forge dependencies
198
+ // postInstall/requires are signed, not just copied into the bundle.
235
199
  return new SignJWT({
236
200
  subs: args.subs,
237
201
  nonce: args.nonce,
@@ -18,6 +18,7 @@
18
18
  import type { Core, Schema } from '@astrale-os/kernel-dsl'
19
19
  import type { Hono } from 'hono'
20
20
 
21
+ import type { DomainManifest } from '../config/define-domain'
21
22
  import type { AnyRemoteFunctionDef, ViewDef } from '../define'
22
23
  import type { SchemaMethodsImpl } from '../method'
23
24
  import type { RemoteServerConfig } from './config'
@@ -47,8 +48,17 @@ export interface DomainWorkerEntryConfig<S extends Schema, TEnv, TDeps> {
47
48
  requires?: readonly string[]
48
49
  /** Typed colon-path the kernel calls once as __SYSTEM__ after install. */
49
50
  postInstall?: string
51
+ /**
52
+ * Physical tree path for the Domain node (absolute, e.g. `/domains/acme`);
53
+ * default `/domains/<origin>`. Rides in on the `...domain` spread from
54
+ * `defineDomain`; declared here so it is captured (not silently dropped like a
55
+ * spread prop absent from this type) and forwarded to `defineRemoteDomain`.
56
+ */
57
+ path?: string
50
58
  /** Provenance stamped on `/meta`. */
51
59
  meta?: RemoteServerConfig<TDeps>['meta']
60
+ /** Presentation metadata (logo / initial view) stamped on `/meta`. */
61
+ manifest?: DomainManifest
52
62
 
53
63
  /**
54
64
  * Map the worker `env` to the handler dependency container. Defaults to
@@ -84,6 +94,8 @@ export function domainWorkerEntry<TEnv, TDeps = TEnv>() {
84
94
  ...(config.core ? { core: config.core } : {}),
85
95
  ...(config.views ? { views: config.views } : {}),
86
96
  ...(config.functions ? { remoteFunctions: config.functions } : {}),
97
+ ...(config.manifest ? { manifest: config.manifest } : {}),
98
+ ...(config.path ? { path: config.path } : {}),
87
99
  })
88
100
 
89
101
  return createWorkerEntry<TEnv>({
@@ -8,5 +8,4 @@ export { assets, createWorkerEntry } from './worker-entry'
8
8
  export type { WorkerEntry, WorkerEntryConfig } from './worker-entry'
9
9
  export { domainWorkerEntry } from './domain-entry'
10
10
  export type { DomainWorkerEntryConfig } from './domain-entry'
11
- export { workerMeta } from './worker-meta'
12
11
  export { startNodeServer } from './start'
@@ -1,31 +0,0 @@
1
- import { createHash } from 'node:crypto'
2
- import { readFileSync } from 'node:fs'
3
-
4
- const HASH_ALGORITHM = 'sha256'
5
-
6
- /**
7
- * Hash a `spec.json` file deterministically. Only the schema payload
8
- * (`nodes` + `edges`) is hashed — the `meta` block carries `builtAt`
9
- * timestamps that would poison drift detection.
10
- *
11
- * Returns `sha256:<hex>`.
12
- */
13
- export function hashSpecFile(path: string): string {
14
- const raw = readFileSync(path, 'utf-8')
15
- const parsed = JSON.parse(raw) as { nodes?: unknown; edges?: unknown }
16
- const canonical = canonicalJson({ nodes: parsed.nodes ?? [], edges: parsed.edges ?? [] })
17
- return `${HASH_ALGORITHM}:${createHash(HASH_ALGORITHM).update(canonical).digest('hex')}`
18
- }
19
-
20
- function canonicalJson(value: unknown): string {
21
- if (value === null || typeof value !== 'object') return JSON.stringify(value)
22
- if (Array.isArray(value)) return '[' + value.map(canonicalJson).join(',') + ']'
23
- const keys = Object.keys(value as Record<string, unknown>).sort()
24
- return (
25
- '{' +
26
- keys
27
- .map((k) => JSON.stringify(k) + ':' + canonicalJson((value as Record<string, unknown>)[k]))
28
- .join(',') +
29
- '}'
30
- )
31
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Build the `/meta` provenance block for a domain worker: the build-injected
3
- * `SDK_COMMIT` / `SCHEMA_HASH` globals plus the domain name. A field is omitted
4
- * when its global isn't defined.
5
- *
6
- * `SDK_COMMIT` / `SCHEMA_HASH` are `--define`'d into the worker bundle at build
7
- * time; the bundler applies that text replacement to this inlined helper too, so
8
- * reading them here behaves exactly as the per-worker block this replaces.
9
- */
10
- declare const SDK_COMMIT: string | undefined
11
- declare const SCHEMA_HASH: string | undefined
12
-
13
- export function workerMeta(domainName: string): {
14
- sdkCommit?: string
15
- schemaHash?: string
16
- domainName: string
17
- } {
18
- const sdkCommit = typeof SDK_COMMIT === 'string' ? SDK_COMMIT : undefined
19
- const schemaHash = typeof SCHEMA_HASH === 'string' ? SCHEMA_HASH : undefined
20
- return {
21
- ...(sdkCommit ? { sdkCommit } : {}),
22
- ...(schemaHash ? { schemaHash } : {}),
23
- domainName,
24
- }
25
- }