@astrale-os/kernel-server 0.5.0-beta.1 → 0.5.0-beta.11

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 (178) hide show
  1. package/dist/capture.d.ts +0 -1
  2. package/dist/capture.js +0 -1
  3. package/dist/cors/cors.d.ts +0 -1
  4. package/dist/cors/cors.js +0 -1
  5. package/dist/cors/index.d.ts +0 -1
  6. package/dist/cors/index.js +0 -1
  7. package/dist/cors/types.d.ts +0 -1
  8. package/dist/cors/types.js +0 -1
  9. package/dist/errors/availability.d.ts +3 -4
  10. package/dist/errors/availability.js +4 -5
  11. package/dist/errors/classify.d.ts +2 -3
  12. package/dist/errors/classify.js +4 -217
  13. package/dist/errors/index.d.ts +1 -2
  14. package/dist/errors/index.js +1 -2
  15. package/dist/errors/request-body.d.ts +0 -1
  16. package/dist/errors/request-body.js +0 -1
  17. package/dist/errors/response-body.d.ts +0 -1
  18. package/dist/errors/response-body.js +0 -1
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +2 -2
  21. package/dist/invocation/context.d.ts +2 -3
  22. package/dist/invocation/context.js +2 -4
  23. package/dist/invocation/identity.d.ts +9 -0
  24. package/dist/invocation/identity.js +11 -0
  25. package/dist/invocation/index.d.ts +3 -2
  26. package/dist/invocation/index.js +2 -1
  27. package/dist/invocation/publication-route.d.ts +13 -0
  28. package/dist/invocation/publication-route.js +44 -0
  29. package/dist/invocation/types.d.ts +38 -10
  30. package/dist/invocation/types.js +0 -1
  31. package/dist/issuer/exchange.d.ts +12 -0
  32. package/dist/issuer/exchange.js +136 -0
  33. package/dist/issuer/index.d.ts +1 -1
  34. package/dist/issuer/index.js +0 -1
  35. package/dist/issuer/routes.d.ts +3 -3
  36. package/dist/issuer/routes.js +25 -14
  37. package/dist/issuer/types.d.ts +2 -2
  38. package/dist/issuer/types.js +0 -1
  39. package/dist/lifecycle/active.d.ts +0 -1
  40. package/dist/lifecycle/active.js +0 -1
  41. package/dist/lifecycle/index.d.ts +0 -1
  42. package/dist/lifecycle/index.js +0 -1
  43. package/dist/publication/delivery/handler.d.ts +9 -0
  44. package/dist/{installation → publication/delivery}/handler.js +29 -28
  45. package/dist/publication/delivery/index.d.ts +2 -0
  46. package/dist/publication/delivery/index.js +1 -0
  47. package/dist/publication/delivery/types.d.ts +11 -0
  48. package/dist/publication/delivery/types.js +1 -0
  49. package/dist/publication/index.d.ts +1 -1
  50. package/dist/publication/index.js +1 -1
  51. package/dist/publication/snapshot.d.ts +5 -3
  52. package/dist/publication/snapshot.js +14 -7
  53. package/dist/publication/table.d.ts +4 -0
  54. package/dist/publication/table.js +16 -0
  55. package/dist/publication/types.d.ts +5 -2
  56. package/dist/publication/types.js +0 -1
  57. package/dist/routes/index.d.ts +0 -1
  58. package/dist/routes/index.js +0 -1
  59. package/dist/routes/routes.d.ts +6 -5
  60. package/dist/routes/routes.js +51 -41
  61. package/dist/server.d.ts +0 -1
  62. package/dist/server.js +56 -16
  63. package/dist/transport/body.d.ts +0 -1
  64. package/dist/transport/body.js +0 -1
  65. package/dist/transport/codecs.d.ts +1 -2
  66. package/dist/transport/codecs.js +1 -2
  67. package/dist/transport/credential.d.ts +1 -2
  68. package/dist/transport/credential.js +1 -2
  69. package/dist/transport/http/handler.d.ts +2 -1
  70. package/dist/transport/http/handler.js +47 -37
  71. package/dist/transport/http/index.d.ts +0 -1
  72. package/dist/transport/http/index.js +0 -1
  73. package/dist/transport/http/render.d.ts +13 -11
  74. package/dist/transport/http/render.js +50 -39
  75. package/dist/transport/index.d.ts +0 -1
  76. package/dist/transport/index.js +0 -1
  77. package/dist/transport/router.d.ts +0 -1
  78. package/dist/transport/router.js +0 -1
  79. package/dist/transport/stream-error.d.ts +2 -3
  80. package/dist/transport/stream-error.js +3 -4
  81. package/dist/transport/types.d.ts +1 -2
  82. package/dist/transport/types.js +0 -1
  83. package/dist/transport/websocket/index.d.ts +0 -1
  84. package/dist/transport/websocket/index.js +0 -1
  85. package/dist/transport/websocket/session.d.ts +4 -3
  86. package/dist/transport/websocket/session.js +46 -39
  87. package/dist/transport/websocket/upgrade.d.ts +2 -1
  88. package/dist/transport/websocket/upgrade.js +2 -2
  89. package/dist/types.d.ts +13 -10
  90. package/dist/types.js +0 -1
  91. package/package.json +23 -11
  92. package/dist/capture.d.ts.map +0 -1
  93. package/dist/capture.js.map +0 -1
  94. package/dist/cors/cors.d.ts.map +0 -1
  95. package/dist/cors/cors.js.map +0 -1
  96. package/dist/cors/index.d.ts.map +0 -1
  97. package/dist/cors/index.js.map +0 -1
  98. package/dist/cors/types.d.ts.map +0 -1
  99. package/dist/cors/types.js.map +0 -1
  100. package/dist/errors/availability.d.ts.map +0 -1
  101. package/dist/errors/availability.js.map +0 -1
  102. package/dist/errors/classify.d.ts.map +0 -1
  103. package/dist/errors/classify.js.map +0 -1
  104. package/dist/errors/index.d.ts.map +0 -1
  105. package/dist/errors/index.js.map +0 -1
  106. package/dist/errors/request-body.d.ts.map +0 -1
  107. package/dist/errors/request-body.js.map +0 -1
  108. package/dist/errors/response-body.d.ts.map +0 -1
  109. package/dist/errors/response-body.js.map +0 -1
  110. package/dist/index.d.ts.map +0 -1
  111. package/dist/index.js.map +0 -1
  112. package/dist/installation/handler.d.ts +0 -10
  113. package/dist/installation/handler.d.ts.map +0 -1
  114. package/dist/installation/handler.js.map +0 -1
  115. package/dist/installation/index.d.ts +0 -3
  116. package/dist/installation/index.d.ts.map +0 -1
  117. package/dist/installation/index.js +0 -2
  118. package/dist/installation/index.js.map +0 -1
  119. package/dist/installation/types.d.ts +0 -12
  120. package/dist/installation/types.d.ts.map +0 -1
  121. package/dist/installation/types.js +0 -2
  122. package/dist/installation/types.js.map +0 -1
  123. package/dist/invocation/context.d.ts.map +0 -1
  124. package/dist/invocation/context.js.map +0 -1
  125. package/dist/invocation/index.d.ts.map +0 -1
  126. package/dist/invocation/index.js.map +0 -1
  127. package/dist/invocation/types.d.ts.map +0 -1
  128. package/dist/invocation/types.js.map +0 -1
  129. package/dist/issuer/index.d.ts.map +0 -1
  130. package/dist/issuer/index.js.map +0 -1
  131. package/dist/issuer/routes.d.ts.map +0 -1
  132. package/dist/issuer/routes.js.map +0 -1
  133. package/dist/issuer/types.d.ts.map +0 -1
  134. package/dist/issuer/types.js.map +0 -1
  135. package/dist/lifecycle/active.d.ts.map +0 -1
  136. package/dist/lifecycle/active.js.map +0 -1
  137. package/dist/lifecycle/index.d.ts.map +0 -1
  138. package/dist/lifecycle/index.js.map +0 -1
  139. package/dist/publication/index.d.ts.map +0 -1
  140. package/dist/publication/index.js.map +0 -1
  141. package/dist/publication/snapshot.d.ts.map +0 -1
  142. package/dist/publication/snapshot.js.map +0 -1
  143. package/dist/publication/types.d.ts.map +0 -1
  144. package/dist/publication/types.js.map +0 -1
  145. package/dist/routes/index.d.ts.map +0 -1
  146. package/dist/routes/index.js.map +0 -1
  147. package/dist/routes/routes.d.ts.map +0 -1
  148. package/dist/routes/routes.js.map +0 -1
  149. package/dist/server.d.ts.map +0 -1
  150. package/dist/server.js.map +0 -1
  151. package/dist/transport/body.d.ts.map +0 -1
  152. package/dist/transport/body.js.map +0 -1
  153. package/dist/transport/codecs.d.ts.map +0 -1
  154. package/dist/transport/codecs.js.map +0 -1
  155. package/dist/transport/credential.d.ts.map +0 -1
  156. package/dist/transport/credential.js.map +0 -1
  157. package/dist/transport/http/handler.d.ts.map +0 -1
  158. package/dist/transport/http/handler.js.map +0 -1
  159. package/dist/transport/http/index.d.ts.map +0 -1
  160. package/dist/transport/http/index.js.map +0 -1
  161. package/dist/transport/http/render.d.ts.map +0 -1
  162. package/dist/transport/http/render.js.map +0 -1
  163. package/dist/transport/index.d.ts.map +0 -1
  164. package/dist/transport/index.js.map +0 -1
  165. package/dist/transport/router.d.ts.map +0 -1
  166. package/dist/transport/router.js.map +0 -1
  167. package/dist/transport/stream-error.d.ts.map +0 -1
  168. package/dist/transport/stream-error.js.map +0 -1
  169. package/dist/transport/types.d.ts.map +0 -1
  170. package/dist/transport/types.js.map +0 -1
  171. package/dist/transport/websocket/index.d.ts.map +0 -1
  172. package/dist/transport/websocket/index.js.map +0 -1
  173. package/dist/transport/websocket/session.d.ts.map +0 -1
  174. package/dist/transport/websocket/session.js.map +0 -1
  175. package/dist/transport/websocket/upgrade.d.ts.map +0 -1
  176. package/dist/transport/websocket/upgrade.js.map +0 -1
  177. package/dist/types.d.ts.map +0 -1
  178. package/dist/types.js.map +0 -1
@@ -0,0 +1,136 @@
1
+ import { inspect as inspectCredential } from '@astrale-os/kernel-core/auth/credential';
2
+ import * as errors from '@astrale-os/kernel-protocol/errors';
3
+ import * as exchangeProtocol from '@astrale-os/kernel-protocol/issuer/exchange';
4
+ import { paths as issuerPaths } from '@astrale-os/kernel-protocol/issuer/paths';
5
+ const PROVIDER_CODES = new Set([
6
+ errors.CODES.AUTH_INVALID,
7
+ errors.CODES.AUTH_EXPIRED,
8
+ errors.CODES.BACKEND_UNAVAILABLE,
9
+ errors.CODES.DEADLINE_EXCEEDED,
10
+ ]);
11
+ export function capture(input) {
12
+ if (input === null || typeof input !== 'object' || typeof input.exchange !== 'function') {
13
+ throw new TypeError('Token-exchange provider is invalid.');
14
+ }
15
+ const exchange = input.exchange;
16
+ return Object.freeze({
17
+ exchange: (request, context) => exchange.call(input, request, context),
18
+ });
19
+ }
20
+ export function mount(app, issuer, provider, cors, active, timeoutMs) {
21
+ const path = issuerPaths(issuer).tokenExchange;
22
+ if (cors.enabled) {
23
+ app.options(path, (context) => cors.preflight(context.req.raw, { methods: ['POST'], headers: ['authorization', 'accept'] }));
24
+ }
25
+ app.all(path, async (context) => {
26
+ const request = context.req.raw;
27
+ const respond = (payload, extra) => json(payload, errors.httpStatusOf(payload.error), request, cors, extra);
28
+ let admitted;
29
+ try {
30
+ admitted = exchangeProtocol.acceptHttpRequest({
31
+ method: request.method,
32
+ url: request.url,
33
+ authorization: request.headers.get('authorization'),
34
+ accept: request.headers.get('accept'),
35
+ });
36
+ }
37
+ catch (cause) {
38
+ if (cause instanceof errors.ProtocolError) {
39
+ return respond(exchangeProtocol.acceptErrorResponse({ error: cause.payload }), cause.payload.code === errors.CODES.METHOD_NOT_ALLOWED ? { allow: 'POST' } : undefined);
40
+ }
41
+ throw cause;
42
+ }
43
+ if (!active.isAccepting) {
44
+ return respond(error('BACKEND_UNAVAILABLE', 'Token exchange is unavailable.'));
45
+ }
46
+ const operation = active.begin(timeoutMs, request.signal);
47
+ if (operation === undefined) {
48
+ return respond(error('LIMIT_EXCEEDED', 'Token-exchange limit exceeded.'));
49
+ }
50
+ try {
51
+ try {
52
+ await requireEmptyBody(request, operation.signal);
53
+ }
54
+ catch (cause) {
55
+ if (operation.timedOut) {
56
+ return respond(error('DEADLINE_EXCEEDED', 'Token-exchange deadline elapsed.'));
57
+ }
58
+ if (cause instanceof errors.ProtocolError) {
59
+ return respond(exchangeProtocol.acceptErrorResponse({ error: cause.payload }));
60
+ }
61
+ throw cause;
62
+ }
63
+ let output;
64
+ try {
65
+ output = await provider.exchange(admitted, { signal: operation.signal });
66
+ }
67
+ catch (cause) {
68
+ if (operation.timedOut) {
69
+ return respond(error('DEADLINE_EXCEEDED', 'Token-exchange deadline elapsed.'));
70
+ }
71
+ if (cause instanceof errors.ProtocolError && PROVIDER_CODES.has(cause.payload.code)) {
72
+ return respond(exchangeProtocol.acceptErrorResponse({ error: cause.payload }));
73
+ }
74
+ throw cause;
75
+ }
76
+ if (operation.timedOut) {
77
+ return respond(error('DEADLINE_EXCEEDED', 'Token-exchange deadline elapsed.'));
78
+ }
79
+ const response = exchangeProtocol.acceptResponse(output);
80
+ if (inspectCredential(response.token).claims.exp !== response.expiresAt) {
81
+ throw new TypeError('Token-exchange provider returned inconsistent expiry evidence.');
82
+ }
83
+ return json(response, 200, request, cors);
84
+ }
85
+ catch {
86
+ return respond(error('INTERNAL_ERROR', 'Internal token-exchange failure.'));
87
+ }
88
+ finally {
89
+ operation.settle();
90
+ }
91
+ });
92
+ }
93
+ async function requireEmptyBody(request, signal) {
94
+ const declared = request.headers.get('content-length');
95
+ if (declared !== null && declared !== '0')
96
+ invalidRequest();
97
+ const reader = request.body?.getReader();
98
+ if (reader === undefined)
99
+ return;
100
+ const abort = () => void reader.cancel(signal.reason).catch(() => undefined);
101
+ signal.addEventListener('abort', abort, { once: true });
102
+ try {
103
+ if (signal.aborted)
104
+ throw signal.reason;
105
+ const first = await reader.read();
106
+ if (!first.done) {
107
+ await reader.cancel('token exchange requires an empty body').catch(() => undefined);
108
+ invalidRequest();
109
+ }
110
+ }
111
+ finally {
112
+ signal.removeEventListener('abort', abort);
113
+ reader.releaseLock();
114
+ }
115
+ }
116
+ function invalidRequest() {
117
+ throw new errors.ProtocolError({
118
+ code: errors.CODES.INVALID_REQUEST,
119
+ message: 'Token exchange requires an empty request body.',
120
+ });
121
+ }
122
+ function error(code, message) {
123
+ return exchangeProtocol.acceptErrorResponse({
124
+ error: { code: errors.CODES[code], message },
125
+ });
126
+ }
127
+ function json(value, status, request, cors, extra) {
128
+ const headers = new Headers(exchangeProtocol.responseHeaders());
129
+ for (const [name, value] of Object.entries(cors.response(request)))
130
+ headers.set(name, value);
131
+ if (extra !== undefined) {
132
+ for (const [name, value] of new Headers(extra))
133
+ headers.set(name, value);
134
+ }
135
+ return new Response(JSON.stringify(value), { status, headers });
136
+ }
@@ -1,3 +1,3 @@
1
1
  export { admitIssuer, issuerRouteReservations, mountIssuer, type IssuerRouteReservation, } from './routes.js';
2
+ export type * as exchange from './exchange.js';
2
3
  export type { Issuer } from './types.js';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1 @@
1
1
  export { admitIssuer, issuerRouteReservations, mountIssuer, } from './routes.js';
2
- //# sourceMappingURL=index.js.map
@@ -1,13 +1,13 @@
1
1
  import type { CorsPolicy } from '../cors/index.js';
2
2
  import type { ActiveOperations } from '../lifecycle/index.js';
3
3
  import type { ServerRouter } from '../transport/index.js';
4
+ import type { Provider } from './exchange.js';
4
5
  import type { Issuer } from './types.js';
5
6
  export interface IssuerRouteReservation {
6
7
  readonly path: string;
7
8
  readonly methods: readonly string[];
8
9
  }
9
- export declare function issuerRouteReservations(input: Issuer): readonly IssuerRouteReservation[];
10
- export declare function mountIssuer(app: ServerRouter, input: Issuer, cors: CorsPolicy, active: ActiveOperations, timeoutMs: number): void;
10
+ export declare function issuerRouteReservations(input: Issuer, tokenExchange?: boolean): readonly IssuerRouteReservation[];
11
+ export declare function mountIssuer(app: ServerRouter, input: Issuer, cors: CorsPolicy, active: ActiveOperations, timeoutMs: number, tokenExchange?: Provider): void;
11
12
  /** Admit static discovery configuration before Runtime Boot performs any durable work. */
12
13
  export declare function admitIssuer(input: Issuer): Issuer;
13
- //# sourceMappingURL=routes.d.ts.map
@@ -1,11 +1,18 @@
1
- import { issuer as issuerDiscovery } from '@astrale-os/kernel-protocol';
1
+ import { configuration } from '@astrale-os/kernel-protocol/issuer/configuration';
2
+ import { hosted } from '@astrale-os/kernel-protocol/issuer/hosted';
3
+ import { keySet } from '@astrale-os/kernel-protocol/issuer/key-set';
4
+ import { paths as issuerPaths } from '@astrale-os/kernel-protocol/issuer/paths';
2
5
  import { IssuerKeysUnavailableError } from '../errors/index.js';
3
- export function issuerRouteReservations(input) {
4
- const paths = issuerDiscovery.paths(input.issuer);
6
+ import { mount } from './exchange.js';
7
+ export function issuerRouteReservations(input, tokenExchange = false) {
8
+ const paths = issuerPaths(input.issuer);
5
9
  const reservations = [
6
10
  Object.freeze({ path: paths.configuration, methods: Object.freeze(['GET']) }),
7
11
  Object.freeze({ path: paths.keys, methods: Object.freeze(['GET']) }),
8
12
  ];
13
+ if (tokenExchange) {
14
+ reservations.push(Object.freeze({ path: paths.tokenExchange, methods: Object.freeze(['POST']) }));
15
+ }
9
16
  if (input.loadIssuerPublicKeys !== undefined) {
10
17
  const base = new URL(input.issuer).pathname.replace(/\/+$/u, '');
11
18
  reservations.push(Object.freeze({
@@ -18,16 +25,18 @@ export function issuerRouteReservations(input) {
18
25
  }
19
26
  return Object.freeze(reservations);
20
27
  }
21
- export function mountIssuer(app, input, cors, active, timeoutMs) {
28
+ export function mountIssuer(app, input, cors, active, timeoutMs, tokenExchange) {
22
29
  const issuer = input.issuer;
23
- const paths = issuerDiscovery.paths(issuer);
30
+ const paths = issuerPaths(issuer);
24
31
  const headers = (request) => ({
25
32
  ...cors.response(request),
26
33
  'cache-control': 'no-cache',
27
34
  });
28
35
  const algorithm = input.signingAlgorithm ?? 'ES256';
29
- const configuration = issuerDiscovery.configuration(issuer, [algorithm]);
30
- app.get(paths.configuration, (context) => context.json(configuration, 200, headers(context.req.raw)));
36
+ const discovery = configuration(issuer, [algorithm], {
37
+ tokenExchange: tokenExchange !== undefined,
38
+ });
39
+ app.get(paths.configuration, (context) => context.json(discovery, 200, headers(context.req.raw)));
31
40
  if (cors.enabled) {
32
41
  app.options(paths.configuration, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
33
42
  app.options(paths.keys, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
@@ -39,7 +48,7 @@ export function mountIssuer(app, input, cors, active, timeoutMs) {
39
48
  }
40
49
  try {
41
50
  const keys = await input.loadPublicKeys(operation.signal);
42
- return context.json(issuerDiscovery.keySet(keys, [algorithm]), 200, headers(context.req.raw));
51
+ return context.json(keySet(keys, [algorithm]), 200, headers(context.req.raw));
43
52
  }
44
53
  catch (cause) {
45
54
  if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
@@ -51,6 +60,9 @@ export function mountIssuer(app, input, cors, active, timeoutMs) {
51
60
  operation.settle();
52
61
  }
53
62
  });
63
+ if (tokenExchange !== undefined) {
64
+ mount(app, issuer, tokenExchange, cors, active, timeoutMs);
65
+ }
54
66
  const loadIssuerPublicKeys = input.loadIssuerPublicKeys;
55
67
  if (loadIssuerPublicKeys !== undefined) {
56
68
  const base = new URL(issuer).pathname.replace(/\/+$/, '');
@@ -59,7 +71,7 @@ export function mountIssuer(app, input, cors, active, timeoutMs) {
59
71
  app.options(`${base}/iss/:thumbprint/.well-known/jwks.json`, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
60
72
  }
61
73
  app.get(`${base}/iss/:thumbprint/.well-known/openid-configuration`, async (context) => {
62
- const hostedIssuer = issuerDiscovery.hosted(issuer, context.req.param('thumbprint'));
74
+ const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
63
75
  if (hostedIssuer === undefined)
64
76
  return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
65
77
  const operation = active.begin(timeoutMs, context.req.raw.signal);
@@ -70,7 +82,7 @@ export function mountIssuer(app, input, cors, active, timeoutMs) {
70
82
  const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
71
83
  if (keys.length === 0)
72
84
  return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
73
- return context.json(issuerDiscovery.configuration(hostedIssuer, [algorithm]), 200, headers(context.req.raw));
85
+ return context.json(configuration(hostedIssuer, [algorithm]), 200, headers(context.req.raw));
74
86
  }
75
87
  catch (cause) {
76
88
  if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
@@ -83,7 +95,7 @@ export function mountIssuer(app, input, cors, active, timeoutMs) {
83
95
  }
84
96
  });
85
97
  app.get(`${base}/iss/:thumbprint/.well-known/jwks.json`, async (context) => {
86
- const hostedIssuer = issuerDiscovery.hosted(issuer, context.req.param('thumbprint'));
98
+ const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
87
99
  if (hostedIssuer === undefined)
88
100
  return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
89
101
  const operation = active.begin(timeoutMs, context.req.raw.signal);
@@ -109,12 +121,12 @@ export function mountIssuer(app, input, cors, active, timeoutMs) {
109
121
  }
110
122
  }
111
123
  async function hostedKeys(load, issuer, signal, algorithm) {
112
- return issuerDiscovery.keySet(await load(issuer, signal), [algorithm]).keys;
124
+ return keySet(await load(issuer, signal), [algorithm]).keys;
113
125
  }
114
126
  /** Admit static discovery configuration before Runtime Boot performs any durable work. */
115
127
  export function admitIssuer(input) {
116
128
  const signingAlgorithm = input.signingAlgorithm ?? 'ES256';
117
- const issuer = issuerDiscovery.configuration(input.issuer, [signingAlgorithm]).issuer;
129
+ const issuer = configuration(input.issuer, [signingAlgorithm]).issuer;
118
130
  if (typeof input.loadPublicKeys !== 'function') {
119
131
  throw new TypeError('Issuer public-key loader is invalid.');
120
132
  }
@@ -135,4 +147,3 @@ export function admitIssuer(input) {
135
147
  }),
136
148
  });
137
149
  }
138
- //# sourceMappingURL=routes.js.map
@@ -1,4 +1,5 @@
1
- import type { IssuerId, JsonWebKey } from '@astrale-os/kernel-core/auth';
1
+ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
+ import type { JsonWebKey } from '@astrale-os/kernel-core/auth/jwk';
2
3
  /** Public issuer material; Server never receives private signing material. */
3
4
  export interface Issuer {
4
5
  readonly issuer: IssuerId;
@@ -8,4 +9,3 @@ export interface Issuer {
8
9
  /** Optional hosted-key discovery for issuers below `<issuer>/iss/:thumbprint`. */
9
10
  readonly loadIssuerPublicKeys?: (issuer: IssuerId, signal: AbortSignal) => Promise<readonly JsonWebKey[]>;
10
11
  }
11
- //# sourceMappingURL=types.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=types.js.map
@@ -37,4 +37,3 @@ export declare class ActiveOperations {
37
37
  private closeSockets;
38
38
  }
39
39
  export {};
40
- //# sourceMappingURL=active.d.ts.map
@@ -197,4 +197,3 @@ async function settleCancellation(cancel, reason) {
197
197
  function abortReason(signal) {
198
198
  return signal.reason instanceof Error ? signal.reason.message : 'operation cancelled';
199
199
  }
200
- //# sourceMappingURL=active.js.map
@@ -1,2 +1 @@
1
1
  export { ActiveOperations, type ActiveOperation, type ActiveSocket } from './active.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1 @@
1
1
  export { ActiveOperations } from './active.js';
2
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ import type { ActiveOperations } from '../../lifecycle/index.js';
2
+ import type { PreparedPublicationSnapshot } from '../snapshot.js';
3
+ import type { Provider } from './types.js';
4
+ export interface Limits {
5
+ readonly maximumDeliveryBytes: number;
6
+ readonly invocationTimeoutMs: number;
7
+ }
8
+ /** Serve one delivery request against the exact Publication snapshot that selected its URL. */
9
+ export declare function handleDelivery(request: Request, snapshot: PreparedPublicationSnapshot, provider: Provider | undefined, active: ActiveOperations, limits: Limits, cors: Readonly<Record<string, string>>): Promise<Response>;
@@ -1,18 +1,20 @@
1
- import { codecs, installation } from '@astrale-os/kernel-protocol';
2
- import { InstallationUnavailableError } from '../errors/index.js';
3
- import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../errors/index.js';
4
- import { readBoundedBody } from '../transport/index.js';
5
- /** Serve one installation request against the exact Publication snapshot that selected its URL. */
6
- export async function handleInstallation(request, snapshot, provider, active, limits, cors) {
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
+ import * as protocolPublication from '@astrale-os/kernel-protocol/publication';
3
+ import { DeliveryUnavailableError } from '../../errors/index.js';
4
+ import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../../errors/index.js';
5
+ import { readBoundedBody } from '../../transport/index.js';
6
+ const delivery = protocolPublication.delivery;
7
+ /** Serve one delivery request against the exact Publication snapshot that selected its URL. */
8
+ export async function handleDelivery(request, snapshot, provider, active, limits, cors) {
7
9
  if (request.method !== 'POST') {
8
- return response({ error: 'Installation requires POST.' }, 405, cors);
10
+ return response({ error: 'Delivery requires POST.' }, 405, cors);
9
11
  }
10
12
  if (provider === undefined) {
11
- return response({ error: 'Installation is unavailable.' }, 503, cors);
13
+ return response({ error: 'Delivery is unavailable.' }, 503, cors);
12
14
  }
13
- const advertised = snapshot.publication.installation;
15
+ const advertised = snapshot.publication.delivery;
14
16
  if (advertised === undefined) {
15
- return response({ error: 'Installation is unavailable.' }, 404, cors);
17
+ return response({ error: 'Delivery is unavailable.' }, 404, cors);
16
18
  }
17
19
  const operation = active.begin(limits.invocationTimeoutMs, request.signal);
18
20
  if (operation === undefined)
@@ -24,21 +26,21 @@ export async function handleInstallation(request, snapshot, provider, active, li
24
26
  return response({ error: 'Bearer authentication is required.' }, 401, cors);
25
27
  }
26
28
  if (advertised.authentication.kind === 'public' && authorization !== null) {
27
- return response({ error: 'This installation endpoint is public.' }, 400, cors);
29
+ return response({ error: 'This delivery endpoint is public.' }, 400, cors);
28
30
  }
29
31
  let body;
30
32
  try {
31
- body = await readBoundedBody(request, limits.maximumInstallationBytes, operation.signal);
33
+ body = await readBoundedBody(request, limits.maximumDeliveryBytes, operation.signal);
32
34
  }
33
35
  catch (cause) {
34
36
  if (cause instanceof RequestBodyTooLargeError) {
35
- return response({ error: 'Installation request is too large.' }, 413, cors);
37
+ return response({ error: 'Delivery request is too large.' }, 413, cors);
36
38
  }
37
39
  if (cause instanceof RequestBodyInvalidError) {
38
- return response({ error: 'Installation request is invalid.' }, 400, cors);
40
+ return response({ error: 'Delivery request is invalid.' }, 400, cors);
39
41
  }
40
42
  if (operation.signal.aborted) {
41
- return response({ error: 'Installation request was cancelled.' }, 503, cors);
43
+ return response({ error: 'Delivery request was cancelled.' }, 503, cors);
42
44
  }
43
45
  throw cause;
44
46
  }
@@ -48,17 +50,17 @@ export async function handleInstallation(request, snapshot, provider, active, li
48
50
  }
49
51
  catch (cause) {
50
52
  if (cause instanceof codecs.DecodeError) {
51
- return response({ error: 'Installation request is invalid.' }, 400, cors);
53
+ return response({ error: 'Delivery request is invalid.' }, 400, cors);
52
54
  }
53
55
  throw cause;
54
56
  }
55
57
  let input;
56
58
  try {
57
- input = installation.acceptRequest(decoded);
59
+ input = delivery.acceptRequest(decoded);
58
60
  }
59
61
  catch (cause) {
60
- if (cause instanceof installation.Error && cause.code === 'INSTALLATION_REQUEST_INVALID') {
61
- return response({ error: 'Installation request is invalid.' }, 400, cors);
62
+ if (cause instanceof delivery.Error && cause.code === 'DELIVERY_REQUEST_INVALID') {
63
+ return response({ error: 'Delivery request is invalid.' }, 400, cors);
62
64
  }
63
65
  throw cause;
64
66
  }
@@ -73,27 +75,27 @@ export async function handleInstallation(request, snapshot, provider, active, li
73
75
  });
74
76
  }
75
77
  catch (cause) {
76
- if (cause instanceof InstallationUnavailableError) {
77
- return response({ error: 'Installation provider is unavailable.' }, 503, cors);
78
+ if (cause instanceof DeliveryUnavailableError) {
79
+ return response({ error: 'Delivery provider is unavailable.' }, 503, cors);
78
80
  }
79
81
  throw cause;
80
82
  }
81
83
  let created;
82
84
  try {
83
- created = installation.accept(candidate);
85
+ created = delivery.accept(candidate);
84
86
  }
85
87
  catch (cause) {
86
- if (cause instanceof installation.Error) {
87
- return response({ error: 'Installation provider returned an invalid response.' }, 500, cors);
88
+ if (cause instanceof delivery.Error) {
89
+ return response({ error: 'Delivery provider returned an invalid response.' }, 500, cors);
88
90
  }
89
91
  throw cause;
90
92
  }
91
93
  if (created.publication.etag !== snapshot.publication.etag) {
92
- return response({ error: 'Installation provider returned a different Publication.' }, 500, cors);
94
+ return response({ error: 'Delivery provider returned a different Publication.' }, 500, cors);
93
95
  }
94
96
  const encoded = codecs.json.encode(created);
95
- if (encodedSize(encoded) > limits.maximumInstallationBytes) {
96
- return response({ error: 'Installation response exceeds its serving limit.' }, 500, cors);
97
+ if (encodedSize(encoded) > limits.maximumDeliveryBytes) {
98
+ return response({ error: 'Delivery response exceeds its serving limit.' }, 500, cors);
97
99
  }
98
100
  return new Response(encodedBody(encoded), {
99
101
  headers: { ...cors, 'content-type': 'application/json' },
@@ -123,4 +125,3 @@ function encodedSize(input) {
123
125
  function toBody(input) {
124
126
  return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
125
127
  }
126
- //# sourceMappingURL=handler.js.map
@@ -0,0 +1,2 @@
1
+ export { handleDelivery, type Limits } from './handler.js';
2
+ export type { Context, Provider } from './types.js';
@@ -0,0 +1 @@
1
+ export { handleDelivery } from './handler.js';
@@ -0,0 +1,11 @@
1
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
2
+ export interface Context {
3
+ /** Omitted for a public endpoint; otherwise the exact admitted retrieval credential. */
4
+ readonly token?: publication.delivery.Token;
5
+ readonly signal: AbortSignal;
6
+ }
7
+ /** Provider-owned retrieval, construction, and attestation signing. */
8
+ export interface Provider {
9
+ /** Throw DeliveryUnavailableError only for an expected dependency outage. */
10
+ create(request: publication.delivery.Request, context: Context): Promise<publication.delivery.Delivery>;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,3 @@
1
1
  export { preparePublicationSnapshot, readPreparedPublicationSnapshot, type PreparedPublicationSnapshot, } from './snapshot.js';
2
2
  export type { PublicationSnapshot, PublicationSnapshotInput, PublicationSource } from './types.js';
3
- //# sourceMappingURL=index.d.ts.map
3
+ export * as delivery from './delivery/index.js';
@@ -1,2 +1,2 @@
1
1
  export { preparePublicationSnapshot, readPreparedPublicationSnapshot, } from './snapshot.js';
2
- //# sourceMappingURL=index.js.map
2
+ export * as delivery from './delivery/index.js';
@@ -1,14 +1,16 @@
1
- import { publication, routes } from '@astrale-os/kernel-protocol';
1
+ import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
3
+ import type * as routes from '@astrale-os/kernel-protocol/routes';
2
4
  import type { PublicationSnapshot, PublicationSnapshotInput } from './types.js';
3
5
  export interface PreparedPublicationSnapshot {
4
6
  readonly snapshot: PublicationSnapshot;
5
7
  readonly publication: publication.Publication;
6
8
  readonly publicationBytes: Uint8Array;
7
9
  readonly bundle: Uint8Array;
8
- readonly routes: readonly routes.CompiledRoute[];
10
+ readonly table: routes.Table;
11
+ readonly domain: Domain;
9
12
  }
10
13
  /** Admit and detach one revision-coherent serving snapshot before publication. */
11
14
  export declare function preparePublicationSnapshot(input: PublicationSnapshotInput): PublicationSnapshot;
12
15
  /** Internal proof that a PublicationSource returned a Server-prepared snapshot without cloning it. */
13
16
  export declare function readPreparedPublicationSnapshot(input: unknown): PreparedPublicationSnapshot | undefined;
14
- //# sourceMappingURL=snapshot.d.ts.map
@@ -1,22 +1,30 @@
1
- import { publication, routes } from '@astrale-os/kernel-protocol';
1
+ import { verifyBundleBytes } from '@astrale-os/kernel-protocol/publication/bundle-evidence';
2
+ import { accept as acceptPublication, encode as encodePublication, } from '@astrale-os/kernel-protocol/publication/publication';
3
+ import { admitTable } from './table.js';
2
4
  const snapshots = new WeakMap();
3
5
  /** Admit and detach one revision-coherent serving snapshot before publication. */
4
6
  export function preparePublicationSnapshot(input) {
5
7
  if (input === null || typeof input !== 'object' || !(input.bundle instanceof Uint8Array)) {
6
8
  throw new TypeError('Publication snapshot input is invalid.');
7
9
  }
8
- const admittedPublication = publication.accept(input.publication);
10
+ const admittedPublication = acceptPublication(input.publication);
9
11
  const bundleBytes = new Uint8Array(input.bundle);
10
- const artifact = publication.decodeBundle(admittedPublication, bundleBytes);
11
- const compiledRoutes = routes.compile(admittedPublication, artifact);
12
- const publicationBytes = publication.encode(admittedPublication);
12
+ verifyBundleBytes(admittedPublication, bundleBytes);
13
+ const domain = input.domain;
14
+ if (domain.origin !== admittedPublication.origin ||
15
+ domain.revision !== admittedPublication.schema.revision) {
16
+ throw new TypeError('Publication snapshot Domain identity differs from its Publication.');
17
+ }
18
+ const table = admitTable(admittedPublication, input.table, domain);
19
+ const publicationBytes = encodePublication(admittedPublication);
13
20
  const snapshot = Object.freeze({ publication: admittedPublication });
14
21
  const prepared = Object.freeze({
15
22
  snapshot,
16
23
  publication: admittedPublication,
17
24
  publicationBytes,
18
25
  bundle: bundleBytes,
19
- routes: compiledRoutes,
26
+ table,
27
+ domain,
20
28
  });
21
29
  snapshots.set(snapshot, prepared);
22
30
  return snapshot;
@@ -27,4 +35,3 @@ export function readPreparedPublicationSnapshot(input) {
27
35
  return undefined;
28
36
  return snapshots.get(input);
29
37
  }
30
- //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1,4 @@
1
+ import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
3
+ import type * as routes from '@astrale-os/kernel-protocol/routes';
4
+ export declare function admitTable(deployed: publication.Publication, input: routes.Table | undefined, domain: Domain): routes.Table;
@@ -0,0 +1,16 @@
1
+ import { reference } from '@astrale-os/kernel-protocol/publication/reference';
2
+ import { acceptTable } from '@astrale-os/kernel-protocol/routes/admission';
3
+ import { verifyTable } from '@astrale-os/kernel-protocol/routes/evidence';
4
+ export function admitTable(deployed, input, domain) {
5
+ if (input === undefined) {
6
+ if (deployed.routes.length !== 0) {
7
+ throw new TypeError('A Publication with native routes requires a Route Table.');
8
+ }
9
+ return verifyTable(Object.freeze({
10
+ publication: reference(deployed),
11
+ entries: Object.freeze([]),
12
+ }), deployed, domain);
13
+ }
14
+ const table = acceptTable(input);
15
+ return verifyTable(table, deployed, domain);
16
+ }
@@ -1,9 +1,13 @@
1
- import type { publication } from '@astrale-os/kernel-protocol';
1
+ import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
3
+ import type * as routes from '@astrale-os/kernel-protocol/routes';
2
4
  declare const PUBLICATION_SNAPSHOT: unique symbol;
3
5
  export interface PublicationSnapshotInput {
4
6
  readonly publication: publication.Publication;
5
7
  /** Exact artifact served at Publication.schema.bundle.href. */
6
8
  readonly bundle: Uint8Array;
9
+ readonly table?: routes.Table;
10
+ readonly domain: Domain;
7
11
  }
8
12
  /** Immutable admitted serving identity; bundle and compiled routes remain Server-private. */
9
13
  export interface PublicationSnapshot {
@@ -16,4 +20,3 @@ export interface PublicationSource {
16
20
  current(): PublicationSnapshot | undefined;
17
21
  }
18
22
  export {};
19
- //# sourceMappingURL=types.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=types.js.map
@@ -1,2 +1 @@
1
1
  export { currentPublicationSnapshot, mountRoutes, type RouteLimits, type RouteReservation, } from './routes.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1 @@
1
1
  export { currentPublicationSnapshot, mountRoutes, } from './routes.js';
2
- //# sourceMappingURL=index.js.map
@@ -1,24 +1,25 @@
1
1
  import type { CorsPolicy } from '../cors/index.js';
2
- import type { InstallationProvider } from '../installation/index.js';
2
+ import type { NextInvocationId } from '../invocation/index.js';
3
3
  import type { ActiveOperations } from '../lifecycle/index.js';
4
+ import type { Provider as DeliveryProvider } from '../publication/delivery/index.js';
4
5
  import type { PublicationSource } from '../publication/index.js';
5
6
  import { type ApplicationHost, type Handler } from '../invocation/index.js';
6
7
  import { type PreparedPublicationSnapshot } from '../publication/index.js';
7
- import { type ServerRouter } from '../transport/index.js';
8
+ import { type ServerRouter } from '../transport/router.js';
8
9
  export interface RouteLimits {
9
10
  readonly maximumRequestBytes: number;
10
11
  readonly maximumPublicationBytes: number;
11
12
  readonly maximumBundleBytes: number;
12
- readonly maximumInstallationBytes: number;
13
+ readonly maximumDeliveryBytes: number;
13
14
  readonly invocationTimeoutMs: number;
14
15
  }
15
16
  export interface RouteReservation {
16
17
  readonly path: string;
17
18
  readonly methods: readonly string[];
18
19
  }
19
- export declare function mountRoutes(app: ServerRouter, source: PublicationSource, installationProvider: InstallationProvider | undefined, handler: Handler, active: ActiveOperations, limits: RouteLimits, cors: CorsPolicy, hostFor: (request: Request) => ApplicationHost | undefined, reservations: readonly RouteReservation[], externalOrigin?: string): void;
20
+ export declare function mountRoutes(app: ServerRouter, source: PublicationSource, deliveryProvider: DeliveryProvider | undefined, handler: Handler, active: ActiveOperations, nextInvocation: NextInvocationId, limits: RouteLimits, cors: CorsPolicy, hostFor: (request: Request) => ApplicationHost | undefined, reservations: readonly RouteReservation[], externalIssuer?: string): void;
20
21
  export declare function currentPublicationSnapshot(source: PublicationSource, limits: Pick<RouteLimits, 'maximumPublicationBytes' | 'maximumBundleBytes'>, serving?: {
21
22
  readonly origin: string;
23
+ readonly issuer?: string;
22
24
  readonly reservations: readonly RouteReservation[];
23
25
  }): PreparedPublicationSnapshot | undefined;
24
- //# sourceMappingURL=routes.d.ts.map