@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.
- package/LICENSE +202 -0
- package/dist/auth/check.d.ts +1 -2
- package/dist/auth/check.d.ts.map +1 -1
- package/dist/auth/check.js +2 -6
- package/dist/auth/check.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/issuer-mint.d.ts +53 -0
- package/dist/auth/issuer-mint.d.ts.map +1 -0
- package/dist/auth/issuer-mint.js +35 -0
- package/dist/auth/issuer-mint.js.map +1 -0
- package/dist/auth/kernel-client.d.ts +8 -2
- package/dist/auth/kernel-client.d.ts.map +1 -1
- package/dist/auth/kernel-client.js +22 -46
- package/dist/auth/kernel-client.js.map +1 -1
- package/dist/cli/dotenv.d.ts +8 -0
- package/dist/cli/dotenv.d.ts.map +1 -1
- package/dist/cli/dotenv.js +18 -0
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/log.d.ts +11 -0
- package/dist/cli/log.d.ts.map +1 -0
- package/dist/cli/log.js +17 -0
- package/dist/cli/log.js.map +1 -0
- package/dist/cli/publish.d.ts +35 -0
- package/dist/cli/publish.d.ts.map +1 -0
- package/dist/cli/publish.js +271 -0
- package/dist/cli/publish.js.map +1 -0
- package/dist/cli/run.d.ts +17 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +85 -101
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/spec.d.ts.map +1 -1
- package/dist/cli/spec.js +1 -0
- package/dist/cli/spec.js.map +1 -1
- package/dist/config/define-domain.d.ts +79 -2
- package/dist/config/define-domain.d.ts.map +1 -1
- package/dist/config/define-domain.js +58 -1
- package/dist/config/define-domain.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/define/remote-function.d.ts +23 -3
- package/dist/define/remote-function.d.ts.map +1 -1
- package/dist/define/remote-function.js +1 -1
- package/dist/define/remote-function.js.map +1 -1
- package/dist/deploy/check.d.ts +6 -6
- package/dist/deploy/check.d.ts.map +1 -1
- package/dist/deploy/check.js +1 -1
- package/dist/deploy/check.js.map +1 -1
- package/dist/deploy/index.d.ts +0 -1
- package/dist/deploy/index.d.ts.map +1 -1
- package/dist/deploy/index.js +0 -1
- package/dist/deploy/index.js.map +1 -1
- package/dist/deploy/meta.d.ts +11 -0
- package/dist/deploy/meta.d.ts.map +1 -1
- package/dist/deploy/meta.js +23 -0
- package/dist/deploy/meta.js.map +1 -1
- package/dist/dispatch/dispatcher.d.ts +17 -4
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +15 -3
- package/dist/dispatch/dispatcher.js.map +1 -1
- package/dist/dispatch/execute.d.ts +6 -2
- package/dist/dispatch/execute.d.ts.map +1 -1
- package/dist/dispatch/execute.js +1 -0
- package/dist/dispatch/execute.js.map +1 -1
- package/dist/dispatch/identity.d.ts +11 -10
- package/dist/dispatch/identity.d.ts.map +1 -1
- package/dist/dispatch/identity.js +4 -3
- package/dist/dispatch/identity.js.map +1 -1
- package/dist/dispatch/index.d.ts +1 -1
- package/dist/dispatch/index.d.ts.map +1 -1
- package/dist/dispatch/index.js +1 -1
- package/dist/dispatch/index.js.map +1 -1
- package/dist/dispatch/self.d.ts +25 -37
- package/dist/dispatch/self.d.ts.map +1 -1
- package/dist/dispatch/self.js +60 -30
- package/dist/dispatch/self.js.map +1 -1
- package/dist/domain/build-spec.d.ts.map +1 -1
- package/dist/domain/build-spec.js +15 -3
- package/dist/domain/build-spec.js.map +1 -1
- package/dist/domain/define.d.ts +17 -32
- package/dist/domain/define.d.ts.map +1 -1
- package/dist/domain/define.js +12 -31
- package/dist/domain/define.js.map +1 -1
- package/dist/domain/extend-functions.d.ts +13 -9
- package/dist/domain/extend-functions.d.ts.map +1 -1
- package/dist/domain/extend-functions.js +28 -13
- package/dist/domain/extend-functions.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/method/class.d.ts +2 -2
- package/dist/method/class.d.ts.map +1 -1
- package/dist/method/class.js.map +1 -1
- package/dist/method/context.d.ts +18 -3
- package/dist/method/context.d.ts.map +1 -1
- package/dist/method/index.d.ts +2 -2
- package/dist/method/index.d.ts.map +1 -1
- package/dist/method/index.js.map +1 -1
- package/dist/method/single.d.ts +25 -38
- package/dist/method/single.d.ts.map +1 -1
- package/dist/method/single.js +1 -11
- package/dist/method/single.js.map +1 -1
- package/dist/server/auxiliary-routes.d.ts.map +1 -1
- package/dist/server/auxiliary-routes.js +13 -2
- package/dist/server/auxiliary-routes.js.map +1 -1
- package/dist/server/create.d.ts +2 -15
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/create.js +15 -51
- package/dist/server/create.js.map +1 -1
- package/dist/server/domain-entry.d.ts +10 -0
- package/dist/server/domain-entry.d.ts.map +1 -1
- package/dist/server/domain-entry.js +2 -0
- package/dist/server/domain-entry.js.map +1 -1
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +10 -8
- package/src/auth/check.ts +2 -6
- package/src/auth/index.ts +2 -0
- package/src/auth/issuer-mint.ts +75 -0
- package/src/auth/kernel-client.ts +31 -63
- package/src/cli/dotenv.ts +20 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/log.ts +18 -0
- package/src/cli/publish.ts +341 -0
- package/src/cli/run.ts +92 -113
- package/src/cli/spec.ts +1 -0
- package/src/config/define-domain.ts +158 -5
- package/src/config/index.ts +1 -1
- package/src/define/remote-function.ts +47 -7
- package/src/deploy/check.ts +7 -7
- package/src/deploy/index.ts +0 -1
- package/src/deploy/meta.ts +25 -0
- package/src/dispatch/dispatcher.ts +32 -9
- package/src/dispatch/execute.ts +7 -2
- package/src/dispatch/identity.ts +11 -10
- package/src/dispatch/index.ts +1 -1
- package/src/dispatch/self.ts +117 -41
- package/src/domain/build-spec.ts +15 -1
- package/src/domain/define.ts +26 -38
- package/src/domain/extend-functions.ts +35 -15
- package/src/index.ts +29 -5
- package/src/method/class.ts +11 -7
- package/src/method/context.ts +19 -3
- package/src/method/index.ts +8 -2
- package/src/method/single.ts +35 -42
- package/src/server/auxiliary-routes.ts +13 -2
- package/src/server/create.ts +15 -51
- package/src/server/domain-entry.ts +12 -0
- package/src/server/index.ts +0 -1
- package/src/deploy/hash-spec.ts +0 -31
- package/src/server/worker-meta.ts +0 -25
package/src/method/single.ts
CHANGED
|
@@ -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 {
|
|
12
|
+
import type { TypedSelf } from '../dispatch/self'
|
|
23
13
|
import type { KernelForAuth, RemoteContext } from './context'
|
|
24
14
|
|
|
25
15
|
/**
|
|
26
|
-
* Remote
|
|
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
|
|
66
|
-
*
|
|
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
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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 :
|
|
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
|
|
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 (`
|
|
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
|
package/src/server/create.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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>({
|
package/src/server/index.ts
CHANGED
|
@@ -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'
|
package/src/deploy/hash-spec.ts
DELETED
|
@@ -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
|
-
}
|