@astrale-os/sdk 0.1.7 → 0.1.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"worker-entry.d.ts","sourceRoot":"","sources":["../../src/server/worker-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAMlD,KAAK,OAAO,GAAG;IAAE,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAAE,CAAA;AAGxE,MAAM,WAAW,iBAAiB,CAAC,KAAK;IACtC;;;;OAIG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,KAAK,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAC7D;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1D,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;IACxD;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;IACtE;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CACP,GAAG,EAAE,KAAK,EACV,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,KACb,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAA;IACzD;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAA;CAC3D;AAED,MAAM,WAAW,WAAW,CAAC,KAAK;IAChC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAM/D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE;IAClC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,GAAG,SAAS,CAAA;IACnD,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CACrD,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,SAAS,CAiBvF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CA0E7F"}
1
+ {"version":3,"file":"worker-entry.d.ts","sourceRoot":"","sources":["../../src/server/worker-entry.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,43 @@
24
24
  import { createRemoteServer } from './create';
25
25
  import { requireEnv } from './require-env';
26
26
  import { canonicalizeServingUrl } from './serving-url';
27
+ /**
28
+ * Choose where an OUTBOUND subrequest to `u` is routed — or `null` to let the
29
+ * real `fetch` handle it. Order matters and same-origin wins FIRST: a call to
30
+ * this worker's OWN serving origin is a self-dispatch, not an edge fetch — and
31
+ * the caller's `routeSubrequest` policy may itself match our own host (e.g. an
32
+ * `isInstanceHost` predicate matches every `*.svc.astrale.ai`, ours included),
33
+ * so it must never get first look at a same-origin call.
34
+ *
35
+ * • same-origin + SELF binding present → the SELF fetcher: a fresh same-script
36
+ * invocation. The normal Cloudflare path — a Worker can't fetch its own
37
+ * hostname over the edge, so it re-enters itself through the binding.
38
+ * • same-origin + NO SELF binding → the cached app, dispatched IN-PROCESS. A
39
+ * Workers-for-Platforms dispatch-namespace tenant can't service-bind to
40
+ * itself (its script name is platform-renamed), so it has no SELF binding; an
41
+ * in-process dispatch reaches the same script with no edge hop and no binding.
42
+ * `App` and the SELF `Fetcher` share the `{ fetch(request) }` shape, so the
43
+ * caller drives both identically.
44
+ * • else, the caller's `routeSubrequest` policy matched → its fetcher.
45
+ * • else → `null`: passthrough to the real network fetch.
46
+ *
47
+ * Pure (no closure over instance state) so the routing decision is unit-testable
48
+ * without standing up a real app.
49
+ */
50
+ export function selectSubrequestTarget(u, ctx) {
51
+ // Same-origin self-subrequest → the same script (SELF binding, else in-process).
52
+ for (const cached of ctx.apps) {
53
+ if (cached.origin === u.origin)
54
+ return ctx.self ?? cached.app;
55
+ }
56
+ // Caller policy (e.g. a platform router on the same zone) → its fetcher.
57
+ if (ctx.routeSubrequest && ctx.routeEnv) {
58
+ const via = ctx.routeSubrequest(u, ctx.routeEnv);
59
+ if (via)
60
+ return via;
61
+ }
62
+ return null;
63
+ }
27
64
  /**
28
65
  * The request origin as the CLIENT reached it — i.e. the origin a fallback
29
66
  * serving URL (and therefore the `iss`) may be derived from. Behind a
@@ -104,31 +141,26 @@ export function createWorkerEntry(config) {
104
141
  apps.set(url, { origin: new URL(url).origin, app });
105
142
  return app;
106
143
  }
107
- // A Worker can't fetch its own hostname (SELF), nor — on Cloudflare — a
144
+ // A Worker can't fetch its own hostname over the edge, nor — on Cloudflare — a
108
145
  // same-zone hostname routed to another Worker (the `routeSubrequest` case).
109
146
  // When either is configured, override `globalThis.fetch` to redirect those
110
- // subrequests through the caller's fetcher. Workers with neither get no global
111
- // mutation. (A self-issued credential's JWKS is resolved in-memory by the
112
- // verifier, not fetched see `auth/verify.ts` so no self-JWKS shim is needed.)
147
+ // subrequests through the right target (see `selectSubrequestTarget`). Workers
148
+ // with neither selfBinding nor routeSubrequest get no global mutation. (A
149
+ // self-issued credential's JWKS is resolved in-memory by the verifier, not
150
+ // fetched — see `auth/verify.ts` — so no self-JWKS shim is needed.)
113
151
  if (config.selfBinding || config.routeSubrequest) {
114
152
  const originalFetch = globalThis.fetch;
115
153
  globalThis.fetch = (async (input, init) => {
116
154
  const href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
117
155
  try {
118
- const u = new URL(href);
119
- // same-origin → SELF (a Worker can't fetch its own hostname)
120
- if (self && apps.size > 0) {
121
- for (const cached of apps.values()) {
122
- if (cached.origin === u.origin)
123
- return self.fetch(new Request(input, init));
124
- }
125
- }
126
- // caller policy → its fetcher (e.g. a platform router on the same zone)
127
- if (config.routeSubrequest && routeEnv) {
128
- const via = config.routeSubrequest(u, routeEnv);
129
- if (via)
130
- return via.fetch(new Request(input, init));
131
- }
156
+ const target = selectSubrequestTarget(new URL(href), {
157
+ self,
158
+ apps: apps.values(),
159
+ routeEnv,
160
+ routeSubrequest: config.routeSubrequest,
161
+ });
162
+ if (target)
163
+ return target.fetch(new Request(input, init));
132
164
  }
133
165
  catch {
134
166
  // non-absolute URL — fall through to the original fetch
@@ -1 +1 @@
1
- {"version":3,"file":"worker-entry.js","sourceRoot":"","sources":["../../src/server/worker-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AA2DtD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,GAAQ,EAAE,OAAgB;IACrD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAA;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;IAC1D,mFAAmF;IACnF,MAAM,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC7D,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAA;AAChE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,MAAM,CAAQ,IAI7B;IACC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,CAAA;IACzB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACjC,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAA;QAC9B,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,SAAS,CAAA;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAA;QACnC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;YAC1C,OAAO,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC,CAAA;QAC9E,CAAC;QACD,oEAAoE;QACpE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAA;QACvD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;QAC5D,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;IACvD,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAQ,MAAgC;IACvE,6EAA6E;IAC7E,yEAAyE;IACzE,wEAAwE;IACxE,0EAA0E;IAC1E,qEAAqE;IACrE,uDAAuD;IACvD,MAAM,eAAe,GAAG,CAAC,CAAA;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAwC,CAAA;IAC5D,IAAI,IAAI,GAAmB,IAAI,CAAA;IAC/B,IAAI,QAAQ,GAAiB,IAAI,CAAA;IAEjC,SAAS,MAAM,CAAC,GAAW,EAAE,GAAU;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,GAAG,CAAA;QAC7B,MAAM,EAAE,GAAG,EAAE,GAAG,kBAAkB,CAAQ,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACjE,IAAI,IAAI,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;YACvC,IAAI,MAAM,KAAK,SAAS;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC/C,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QACnD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,wEAAwE;IACxE,4EAA4E;IAC5E,2EAA2E;IAC3E,+EAA+E;IAC/E,0EAA0E;IAC1E,kFAAkF;IAClF,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QACjD,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAA;QACtC,UAAU,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,KAAwB,EAAE,IAAkB,EAAE,EAAE;YACzE,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAA;YAC9F,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;gBACvB,6DAA6D;gBAC7D,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;oBAC1B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;wBACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;4BAAE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;oBAC7E,CAAC;gBACH,CAAC;gBACD,wEAAwE;gBACxE,IAAI,MAAM,CAAC,eAAe,IAAI,QAAQ,EAAE,CAAC;oBACvC,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;oBAC/C,IAAI,GAAG;wBAAE,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;gBACrD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;YACD,OAAO,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QACnC,CAAC,CAAiB,CAAA;IACpB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAE,GAAU;YACtC,IAAI,MAAM,CAAC,WAAW;gBAAE,IAAI,KAAK,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,IAAI,CAAA;YAChE,IAAI,MAAM,CAAC,eAAe;gBAAE,QAAQ,GAAG,GAAG,CAAA;YAC1C,4DAA4D;YAC5D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;YACnF,IAAI,MAAM,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;gBAChC,wEAAwE;gBACxE,2EAA2E;gBAC3E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;gBAC7D,IAAI,OAAO,KAAK,SAAS;oBAAE,OAAO,OAAO,CAAA;YAC3C,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU;gBAC3B,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,UAAW,EAAE,OAAO,CAAC,CAAC;gBAC5D,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,oDAAoD,CAAC,CAAA;YACvF,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAA;YACvC,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;YACxF,OAAO,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAC3C,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"worker-entry.js","sourceRoot":"","sources":["../../src/server/worker-entry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Astrale Remote Domain SDK - Define and deploy domains as standalone Hono servers",
5
5
  "keywords": [
6
6
  "astrale",
package/src/cli/run.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The `astrale-domain` CLI — dev | build | deploy.
2
+ * The `astrale-domain` CLI — dev | build | deploy | publish.
3
3
  *
4
4
  * Thin by design: it reads `astrale.config.ts`, resolves `envs[<env>] → params`
5
5
  * via the adapter, loads the env's secrets file, then drives `adapter.watch`
@@ -18,14 +18,17 @@
18
18
  * astrale-domain prod # = deploy prod
19
19
  * astrale-domain deploy <env> # any env key
20
20
  * astrale-domain build # rebuild spec only (placeholder URL)
21
- * astrale-domain publish [env] # = deploy [prod] then register the URL
21
+ * astrale-domain publish [env] # register the deployed URL in the admin catalog (NO deploy)
22
22
  *
23
- * `publish` (and the `--publish` tail-flag on deploy) deploys, then registers
24
- * the resulting URL in the admin catalog by SHELLING OUT to the operator CLI
25
- * (`astrale domain publish --origin --name --public-url`). Auth lives entirely
26
- * in that CLI this build tool never touches credentials; it just hands off the
27
- * fresh URL it alone knows. Requires `astrale` on PATH (the same CLI the deploy
28
- * footer already points you at for `domain install`).
23
+ * `publish` registers a domain's ALREADY-deployed URL in the admin catalog by
24
+ * SHELLING OUT to the operator CLI (`astrale domain publish --origin --name
25
+ * --public-url`); it does NOT deploy — run `prod` / `deploy <env>` first, or use
26
+ * `deploy --publish` to deploy AND register in one step. Standalone publish
27
+ * defaults the registered address to `https://<origin>` (what every fleet domain
28
+ * serves at); pass `--public-url` for workers.dev / split-host deploys. Auth
29
+ * lives entirely in the operator CLI — this build tool never touches
30
+ * credentials. Requires `astrale` on PATH (the same CLI the deploy footer
31
+ * already points you at for `domain install`).
29
32
  */
30
33
 
31
34
  import { spawn } from 'node:child_process'
@@ -44,19 +47,21 @@ import { buildProjectSpec } from './spec'
44
47
  const CONFIG_NAMES = ['astrale.config.ts', 'astrale.config.js', 'astrale.config.mjs']
45
48
 
46
49
  type ParsedArgs = {
47
- command: 'dev' | 'build' | 'deploy'
50
+ command: 'dev' | 'build' | 'deploy' | 'publish'
48
51
  env: string
49
52
  watch: boolean
50
53
  /** `--port <n>` (dev only) — overrides the env's local dev port. */
51
54
  port?: number
52
55
  /** `--host <url>` (dev only) — public URL of a tunnel/proxy front: binds 0.0.0.0 + pins WORKER_URL. */
53
56
  host?: string
54
- /** Register the deployed URL in the admin catalog (the `publish` command / `--publish` flag). */
57
+ /** `--publish` tail-flag on deploy/prod: ALSO register the freshly-deployed URL (deploy + register). */
55
58
  publish?: boolean
56
- /** `--name <slug>` — registry name to publish under (default: package.json `name`). */
59
+ /** `--name <slug>` — registry name to publish under (default: the origin's first label, e.g. `ai-gateway`). */
57
60
  name?: string
58
61
  /** `--install-by-default` — mark the published domain for install on every new instance. */
59
62
  installByDefault?: boolean
63
+ /** `--public-url <url>` — (publish only) the address the catalog points at (default: `https://<origin>`). */
64
+ publicUrl?: string
60
65
  }
61
66
 
62
67
  export async function run(argv: readonly string[]): Promise<number> {
@@ -131,6 +136,25 @@ export async function run(argv: readonly string[]): Promise<number> {
131
136
  return 0
132
137
  }
133
138
 
139
+ if (parsed.command === 'publish') {
140
+ // Register the already-deployed URL in the admin catalog — NO deploy. The
141
+ // public address defaults to `https://<origin>` (the canonical custom-domain
142
+ // prod URL — what every fleet domain serves at); `--public-url` overrides it
143
+ // for workers.dev / split-host deploys. No adapter, secrets, or params: this
144
+ // only points the registry at an address the author already deployed.
145
+ const pkg = readPackageMeta(projectDir)
146
+ const name = parsed.name ?? def.origin.split('.')[0] ?? def.origin
147
+ const url = parsed.publicUrl ?? `https://${def.origin}`
148
+ info(`registering ${def.origin} → ${url} in the admin catalog (no deploy)`)
149
+ return await publishToAdmin({
150
+ origin: def.origin,
151
+ name,
152
+ url,
153
+ ...(pkg.description ? { description: pkg.description } : {}),
154
+ ...(parsed.installByDefault ? { installByDefault: true } : {}),
155
+ })
156
+ }
157
+
134
158
  const adapter = def.adapter as DomainAdapter<unknown>
135
159
  // CLI param overrides — applied over the env's params here AND re-applied by
136
160
  // the config hot-regen path, which re-resolves `adapter.params` from the
@@ -184,9 +208,10 @@ export async function run(argv: readonly string[]): Promise<number> {
184
208
  if (parsed.publish) {
185
209
  const pkg = readPackageMeta(projectDir)
186
210
  // The registry `name` is a short catalog slug, distinct from the FQDN-like
187
- // `origin`. It isn't in the domain definition, so default to the project's
188
- // package.json `name`, then the origin's first label; `--name` overrides.
189
- const name = parsed.name ?? pkg.name ?? def.origin.split('.')[0] ?? def.origin
211
+ // `origin` AND from the npm package name (`@scope/…` for a published domain —
212
+ // illegal as a catalog slug). Default to the origin's first label (the fleet
213
+ // convention, e.g. `ai-gateway`); `--name` overrides.
214
+ const name = parsed.name ?? def.origin.split('.')[0] ?? def.origin
190
215
  return await publishToAdmin({
191
216
  origin: def.origin,
192
217
  name,
@@ -539,6 +564,7 @@ export function parseArgs(argv: readonly string[]): ParsedArgs {
539
564
  let port: number | undefined
540
565
  let host: string | undefined
541
566
  let name: string | undefined
567
+ let publicUrl: string | undefined
542
568
  const cleaned: string[] = []
543
569
  for (let i = 0; i < rest.length; i++) {
544
570
  const a = rest[i]!
@@ -560,6 +586,12 @@ export function parseArgs(argv: readonly string[]): ParsedArgs {
560
586
  if (!name) throw new Error('--name needs a value (the registry slug to publish under)')
561
587
  } else if (a.startsWith('--name=')) {
562
588
  name = a.slice('--name='.length)
589
+ } else if (a === '--public-url') {
590
+ publicUrl = rest[++i]
591
+ if (!publicUrl)
592
+ throw new Error('--public-url needs a value (the address the catalog points at)')
593
+ } else if (a.startsWith('--public-url=')) {
594
+ publicUrl = a.slice('--public-url='.length)
563
595
  } else {
564
596
  cleaned.push(a)
565
597
  }
@@ -590,13 +622,15 @@ export function parseArgs(argv: readonly string[]): ParsedArgs {
590
622
  return { command: 'deploy', env, watch, ...(publish ? { publish } : {}), ...pub }
591
623
  }
592
624
  case 'publish':
593
- // Sugar for `deploy [prod] --publish`: deploy, then register the URL.
625
+ // Register the ALREADY-deployed URL in the admin catalog NO deploy.
626
+ // (Use `deploy [env] --publish` to deploy AND register in one step.) The
627
+ // public address defaults to `https://<origin>`; `--public-url` overrides.
594
628
  return {
595
- command: 'deploy',
629
+ command: 'publish',
596
630
  env: positionals[0] ?? 'prod',
597
631
  watch: false,
598
- publish: true,
599
632
  ...pub,
633
+ ...(publicUrl !== undefined ? { publicUrl } : {}),
600
634
  }
601
635
  case 'build':
602
636
  // Spec-only — no env resolution happens on this path.
@@ -664,11 +698,12 @@ function printUsage(msg?: string): void {
664
698
  ` astrale-domain dev # deploy dev --watch (hot-reload, prints URL)\n` +
665
699
  ` astrale-domain prod # deploy prod\n` +
666
700
  ` astrale-domain deploy <env> # deploy any env key (--watch optional)\n` +
667
- ` astrale-domain publish [env] # deploy [prod], then register the URL in the admin catalog\n` +
701
+ ` astrale-domain publish [env] # register the already-deployed URL in the admin catalog (NO deploy)\n` +
668
702
  ` astrale-domain build # rebuild the diagnostic spec only\n` +
669
703
  `\nFlags:\n` +
670
- ` --publish # on deploy/prod: register the deployed URL (= the publish command)\n` +
671
- ` --name <slug> # registry name to publish under (default: package.json name)\n` +
704
+ ` --publish # on deploy/prod: ALSO register the deployed URL (deploy + register)\n` +
705
+ ` --name <slug> # registry name to publish under (default: the origin's first label)\n` +
706
+ ` --public-url <url> # (publish) address the catalog points at (default: https://<origin>)\n` +
672
707
  ` --install-by-default # mark the published domain for install on every new instance\n` +
673
708
  `\n publish shells out to \`astrale domain publish\` (needs \`astrale\` on PATH).\n\n`,
674
709
  )
@@ -19,24 +19,28 @@
19
19
  import type { AuthPolicy, FunctionBinding } from '@astrale-os/kernel-api/routed'
20
20
  import type { FnMap } from '@astrale-os/kernel-client'
21
21
  import type { BoundClientSessionView } from '@astrale-os/kernel-client/session'
22
- import type { AuthContext } from '@astrale-os/kernel-core'
23
22
  import type { Context } from 'hono'
24
23
  import type { z } from 'zod'
25
24
 
26
25
  import type { CallRemoteFn } from '../dispatch/call-remote'
27
- import type { KernelForAuth } from '../method/context'
26
+ import type { AuthForPolicy, KernelForAuth } from '../method/context'
28
27
 
29
28
  export type RemoteFunctionContext<
30
29
  TParams,
31
30
  TDeps = unknown,
32
31
  TKernel = BoundClientSessionView<FnMap> | null,
32
+ TAuth extends AuthPolicy = AuthPolicy,
33
33
  > = {
34
34
  /** Validated params (Zod-checked against `inputSchema`). */
35
35
  params: TParams
36
36
  /** Hono request context — escape hatch for headers, raw body, etc. */
37
37
  c: Context
38
- /** Resolved auth context. `null` when `auth: 'public'` or absent. */
39
- auth: AuthContext | null
38
+ /**
39
+ * Resolved auth context. Its nullability follows the function's `auth`
40
+ * policy: non-null for the default `'required'`, `... | null` for
41
+ * `'optional'`, and `null` for `'public'`.
42
+ */
43
+ auth: AuthForPolicy<TAuth>
40
44
  /** Typed dependency container injected at server startup. */
41
45
  deps: TDeps
42
46
  /**
@@ -96,18 +100,19 @@ export type RemoteFunctionDef<
96
100
  binding?: FunctionBinding
97
101
  /**
98
102
  * Authentication policy. Defaults to `'required'`. Captured as a literal type
99
- * so it drives {@link KernelForAuth} on the `execute`/`authorize` context:
100
- * omit it (or `'required'`) `ctx.kernel` non-null; `'optional'` → `… | null`;
101
- * `'public'` `null` (webhooks reach the graph via `ctx.selfKernel`).
103
+ * so it drives `ctx.auth` and {@link KernelForAuth} on the
104
+ * `execute`/`authorize` context: omit it (or `'required'`) makes both
105
+ * non-null; `'optional'` widens them to `... | null`; `'public'` makes them
106
+ * `null` (webhooks reach the graph via `ctx.selfKernel`).
102
107
  */
103
108
  auth?: TAuth
104
109
  /** Optional pre-execute authorization. Throw to deny. */
105
110
  authorize?: (
106
- ctx: RemoteFunctionContext<TParams, TDeps, KernelForAuth<TAuth>>,
111
+ ctx: RemoteFunctionContext<TParams, TDeps, KernelForAuth<TAuth>, TAuth>,
107
112
  ) => void | Promise<void>
108
113
  /** The function body. May be async. */
109
114
  execute: (
110
- ctx: RemoteFunctionContext<TParams, TDeps, KernelForAuth<TAuth>>,
115
+ ctx: RemoteFunctionContext<TParams, TDeps, KernelForAuth<TAuth>, TAuth>,
111
116
  ) => TResult | Promise<TResult>
112
117
  /** Optional human-readable description. */
113
118
  description?: string
@@ -21,11 +21,12 @@
21
21
  import type { AuthPolicy, FunctionBinding } from '@astrale-os/kernel-api/routed'
22
22
  import type { FnMap } from '@astrale-os/kernel-client'
23
23
  import type { BoundClientSessionView } from '@astrale-os/kernel-client/session'
24
- import type { AuthContext } from '@astrale-os/kernel-core'
25
24
  import type { EdgeEndpoint } from '@astrale-os/kernel-dsl'
26
25
  import type { Context } from 'hono'
27
26
 
28
- export type ViewRenderContext<TDeps = unknown> = {
27
+ import type { AuthForPolicy } from '../method/context'
28
+
29
+ export type ViewRenderContext<TDeps = unknown, TAuth extends AuthPolicy = AuthPolicy> = {
29
30
  /** Hono request context — read params, headers, query, etc. */
30
31
  c: Context
31
32
  /**
@@ -34,8 +35,12 @@ export type ViewRenderContext<TDeps = unknown> = {
34
35
  * the binding has no placeholders.
35
36
  */
36
37
  params: Record<string, string>
37
- /** Resolved auth context. `null` when `auth: 'public'` or absent. */
38
- auth: AuthContext | null
38
+ /**
39
+ * Resolved auth context. Its nullability follows the View's `auth` policy:
40
+ * non-null for `'required'`, `... | null` for `'optional'`, and `null` for
41
+ * `'public'`.
42
+ */
43
+ auth: AuthForPolicy<TAuth>
39
44
  /** Typed dependency container injected at server startup. */
40
45
  deps: TDeps
41
46
  /**
@@ -48,7 +53,7 @@ export type ViewRenderContext<TDeps = unknown> = {
48
53
  selfKernel: (kernelUrl?: string) => Promise<BoundClientSessionView<FnMap>>
49
54
  }
50
55
 
51
- export type ViewDef<TDeps = unknown> = {
56
+ export type ViewDef<TDeps = unknown, TAuth extends AuthPolicy = AuthPolicy> = {
52
57
  /**
53
58
  * Override the binding (URL + route shape). When absent, SDK defaults to
54
59
  * `{ remoteUrl: ${url}/<viewsFolder>/<slug> }`. The HTTP verb (GET for
@@ -68,18 +73,18 @@ export type ViewDef<TDeps = unknown> = {
68
73
  */
69
74
  mount?: string
70
75
  /** Authentication policy. Defaults to `'required'`. */
71
- auth?: AuthPolicy
76
+ auth?: TAuth
72
77
  /**
73
78
  * Optional pre-render authorization. Runs after auth resolution; throw to
74
79
  * deny. SDK wraps as 403.
75
80
  */
76
- authorize?: (ctx: ViewRenderContext<TDeps>) => void | Promise<void>
81
+ authorize?: (ctx: ViewRenderContext<TDeps, TAuth>) => void | Promise<void>
77
82
  /**
78
83
  * Render the iframe response. May return a redirect, a proxy, or inline
79
84
  * HTML. When omitted, no worker route is mounted — the binding URL
80
85
  * points elsewhere (CDN, external service).
81
86
  */
82
- render?: (ctx: ViewRenderContext<TDeps>) => Response | Promise<Response>
87
+ render?: (ctx: ViewRenderContext<TDeps, TAuth>) => Response | Promise<Response>
83
88
  /**
84
89
  * Optional target(s) the View attaches to via `view_for` edge(s). Typically
85
90
  * `selfOf(SomeClass)` to bind to a class meta-node, or a `CorePath`
@@ -96,6 +101,8 @@ export type ViewDef<TDeps = unknown> = {
96
101
  * `defineRemoteDomain` consumes the typed shape and the author retains
97
102
  * full type inference on `render` / `authorize`.
98
103
  */
99
- export function defineView<TDeps = unknown>(def: ViewDef<TDeps>): ViewDef<TDeps> {
104
+ export function defineView<TDeps = unknown, TAuth extends AuthPolicy = 'required'>(
105
+ def: ViewDef<TDeps, TAuth>,
106
+ ): ViewDef<TDeps, TAuth> {
100
107
  return def
101
108
  }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // ─── Method authoring ────────────────────────────────────────────────────
2
2
  export type {
3
+ AuthForPolicy,
3
4
  Kernel,
4
5
  KernelForAuth,
5
6
  RemoteContext,
@@ -43,11 +43,32 @@ export type KernelForAuth<TAuth extends AuthPolicy> = TAuth extends 'public'
43
43
  ? Kernel | null
44
44
  : Kernel
45
45
 
46
- export type RemoteContext<TParams, TSelf, TDeps, TKernel = Kernel | null> = {
46
+ /**
47
+ * The resolved auth context a handler receives, derived from its declared
48
+ * `auth` policy. Mirrors {@link KernelForAuth}: required methods have a
49
+ * verified auth context, optional/public methods encode the nullable cases.
50
+ */
51
+ export type AuthForPolicy<TAuth extends AuthPolicy> = TAuth extends 'public'
52
+ ? null
53
+ : TAuth extends 'optional'
54
+ ? AuthContext | null
55
+ : AuthContext
56
+
57
+ export type RemoteContext<
58
+ TParams,
59
+ TSelf,
60
+ TDeps,
61
+ TKernel = Kernel | null,
62
+ TAuth extends AuthPolicy = AuthPolicy,
63
+ > = {
47
64
  /** Validated params (Zod-checked against the method's `inputSchema`). */
48
65
  params: TParams
49
- /** Auth context resolved from the inbound delegation credential. `null` for public or unauthenticated optional methods. */
50
- auth: AuthContext | null
66
+ /**
67
+ * Auth context resolved from the inbound delegation credential. Its
68
+ * nullability follows the method's `auth` policy: non-null for the default
69
+ * `'required'`, `... | null` for `'optional'`, and `null` for `'public'`.
70
+ */
71
+ auth: AuthForPolicy<TAuth>
51
72
  /** Bound node instance — `undefined` for static methods. */
52
73
  self: TSelf
53
74
  /** Typed dependency container injected at server startup. */
@@ -1,4 +1,4 @@
1
- export type { Kernel, KernelForAuth, RemoteContext } from './context'
1
+ export type { AuthForPolicy, Kernel, KernelForAuth, RemoteContext } from './context'
2
2
  export type { RemoteHandler, AnyRemoteHandler, MethodImpl } from './single'
3
3
  export { remoteMethod } from './single'
4
4
  export type { ClassMethodsImpl, InterfaceMethodsImpl, SchemaMethodsImpl } from './class'
@@ -38,7 +38,7 @@ import type { KernelForAuth, RemoteContext } from './context'
38
38
  export type RemoteHandler<TParams, TResult, TSelf, TDeps, TAuth extends AuthPolicy = 'required'> = {
39
39
  /** The handler body. May be async or an async generator (for `output: 'stream'`). */
40
40
  execute?: (
41
- ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>>,
41
+ ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>, TAuth>,
42
42
  ) => TResult | Promise<TResult> | AsyncGenerator<TResult>
43
43
  /**
44
44
  * Optional REST binding — attaches a native HTTP route to this method.
@@ -55,9 +55,10 @@ export type RemoteHandler<TParams, TResult, TSelf, TDeps, TAuth extends AuthPoli
55
55
  description?: string
56
56
  /**
57
57
  * Authentication policy. Defaults to `'required'` when absent. Captured as a
58
- * literal type so it drives {@link KernelForAuth} on the `execute`/`authorize`
59
- * context: omit it (or set `'required'`) and `ctx.kernel` is non-null;
60
- * `'optional'` widens it to `… | null`; `'public'` makes it `null`.
58
+ * literal type so it drives `ctx.auth` and {@link KernelForAuth} on the
59
+ * `execute`/`authorize` context: omit it (or set `'required'`) and both
60
+ * `ctx.auth` and `ctx.kernel` are non-null; `'optional'` widens them to
61
+ * `... | null`; `'public'` makes both `null`.
61
62
  */
62
63
  auth?: TAuth
63
64
  /**
@@ -75,7 +76,7 @@ export type RemoteHandler<TParams, TResult, TSelf, TDeps, TAuth extends AuthPoli
75
76
  * additive ergonomic gating on top, not a replacement.
76
77
  */
77
78
  authorize?: (
78
- ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>>,
79
+ ctx: RemoteContext<TParams, TSelf, TDeps, KernelForAuth<TAuth>, TAuth>,
79
80
  ) => void | Promise<void>
80
81
  }
81
82
 
@@ -31,6 +31,54 @@ import { canonicalizeServingUrl } from './serving-url'
31
31
  type Fetcher = { fetch(request: Request): Response | Promise<Response> }
32
32
  type App = { fetch(request: Request): Response | Promise<Response> }
33
33
 
34
+ /** A built app cached by its serving URL: `origin` for same-origin matching,
35
+ * `app` for in-process self-dispatch. */
36
+ type CachedApp = { origin: string; app: App }
37
+
38
+ /**
39
+ * Choose where an OUTBOUND subrequest to `u` is routed — or `null` to let the
40
+ * real `fetch` handle it. Order matters and same-origin wins FIRST: a call to
41
+ * this worker's OWN serving origin is a self-dispatch, not an edge fetch — and
42
+ * the caller's `routeSubrequest` policy may itself match our own host (e.g. an
43
+ * `isInstanceHost` predicate matches every `*.svc.astrale.ai`, ours included),
44
+ * so it must never get first look at a same-origin call.
45
+ *
46
+ * • same-origin + SELF binding present → the SELF fetcher: a fresh same-script
47
+ * invocation. The normal Cloudflare path — a Worker can't fetch its own
48
+ * hostname over the edge, so it re-enters itself through the binding.
49
+ * • same-origin + NO SELF binding → the cached app, dispatched IN-PROCESS. A
50
+ * Workers-for-Platforms dispatch-namespace tenant can't service-bind to
51
+ * itself (its script name is platform-renamed), so it has no SELF binding; an
52
+ * in-process dispatch reaches the same script with no edge hop and no binding.
53
+ * `App` and the SELF `Fetcher` share the `{ fetch(request) }` shape, so the
54
+ * caller drives both identically.
55
+ * • else, the caller's `routeSubrequest` policy matched → its fetcher.
56
+ * • else → `null`: passthrough to the real network fetch.
57
+ *
58
+ * Pure (no closure over instance state) so the routing decision is unit-testable
59
+ * without standing up a real app.
60
+ */
61
+ export function selectSubrequestTarget<TDeps>(
62
+ u: URL,
63
+ ctx: {
64
+ self: Fetcher | null
65
+ apps: Iterable<CachedApp>
66
+ routeEnv: TDeps | null
67
+ routeSubrequest?: (url: URL, env: TDeps) => Fetcher | null | undefined
68
+ },
69
+ ): { fetch(request: Request): Response | Promise<Response> } | null {
70
+ // Same-origin self-subrequest → the same script (SELF binding, else in-process).
71
+ for (const cached of ctx.apps) {
72
+ if (cached.origin === u.origin) return ctx.self ?? cached.app
73
+ }
74
+ // Caller policy (e.g. a platform router on the same zone) → its fetcher.
75
+ if (ctx.routeSubrequest && ctx.routeEnv) {
76
+ const via = ctx.routeSubrequest(u, ctx.routeEnv)
77
+ if (via) return via
78
+ }
79
+ return null
80
+ }
81
+
34
82
  export interface WorkerEntryConfig<TDeps> {
35
83
  /**
36
84
  * Build the `createRemoteServer` config for the resolved serving `url`. Called
@@ -152,7 +200,7 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
152
200
  // every alternation. The bound caps abuse via attacker-minted Host /
153
201
  // X-Forwarded-Proto values on that same fallback path.
154
202
  const MAX_CACHED_APPS = 4
155
- const apps = new Map<string, { origin: string; app: App }>()
203
+ const apps = new Map<string, CachedApp>()
156
204
  let self: Fetcher | null = null
157
205
  let routeEnv: TDeps | null = null
158
206
 
@@ -168,29 +216,25 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
168
216
  return app
169
217
  }
170
218
 
171
- // A Worker can't fetch its own hostname (SELF), nor — on Cloudflare — a
219
+ // A Worker can't fetch its own hostname over the edge, nor — on Cloudflare — a
172
220
  // same-zone hostname routed to another Worker (the `routeSubrequest` case).
173
221
  // When either is configured, override `globalThis.fetch` to redirect those
174
- // subrequests through the caller's fetcher. Workers with neither get no global
175
- // mutation. (A self-issued credential's JWKS is resolved in-memory by the
176
- // verifier, not fetched see `auth/verify.ts` so no self-JWKS shim is needed.)
222
+ // subrequests through the right target (see `selectSubrequestTarget`). Workers
223
+ // with neither selfBinding nor routeSubrequest get no global mutation. (A
224
+ // self-issued credential's JWKS is resolved in-memory by the verifier, not
225
+ // fetched — see `auth/verify.ts` — so no self-JWKS shim is needed.)
177
226
  if (config.selfBinding || config.routeSubrequest) {
178
227
  const originalFetch = globalThis.fetch
179
228
  globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
180
229
  const href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url
181
230
  try {
182
- const u = new URL(href)
183
- // same-origin → SELF (a Worker can't fetch its own hostname)
184
- if (self && apps.size > 0) {
185
- for (const cached of apps.values()) {
186
- if (cached.origin === u.origin) return self.fetch(new Request(input, init))
187
- }
188
- }
189
- // caller policy → its fetcher (e.g. a platform router on the same zone)
190
- if (config.routeSubrequest && routeEnv) {
191
- const via = config.routeSubrequest(u, routeEnv)
192
- if (via) return via.fetch(new Request(input, init))
193
- }
231
+ const target = selectSubrequestTarget(new URL(href), {
232
+ self,
233
+ apps: apps.values(),
234
+ routeEnv,
235
+ routeSubrequest: config.routeSubrequest,
236
+ })
237
+ if (target) return target.fetch(new Request(input, init))
194
238
  } catch {
195
239
  // non-absolute URL — fall through to the original fetch
196
240
  }