@amalgm/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/PURPOSE.md +94 -0
  2. package/README.md +30 -0
  3. package/dist/adapters/index.d.ts +13 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +11 -0
  6. package/dist/adapters/index.js.map +1 -0
  7. package/dist/identity/auth-records.d.ts +147 -0
  8. package/dist/identity/auth-records.d.ts.map +1 -0
  9. package/dist/identity/auth-records.js +354 -0
  10. package/dist/identity/auth-records.js.map +1 -0
  11. package/dist/identity/ids.d.ts +57 -0
  12. package/dist/identity/ids.d.ts.map +1 -0
  13. package/dist/identity/ids.js +80 -0
  14. package/dist/identity/ids.js.map +1 -0
  15. package/dist/identity/index.d.ts +14 -0
  16. package/dist/identity/index.d.ts.map +1 -0
  17. package/dist/identity/index.js +14 -0
  18. package/dist/identity/index.js.map +1 -0
  19. package/dist/identity/labels.d.ts +115 -0
  20. package/dist/identity/labels.d.ts.map +1 -0
  21. package/dist/identity/labels.js +154 -0
  22. package/dist/identity/labels.js.map +1 -0
  23. package/dist/identity/layout.d.ts +188 -0
  24. package/dist/identity/layout.d.ts.map +1 -0
  25. package/dist/identity/layout.js +233 -0
  26. package/dist/identity/layout.js.map +1 -0
  27. package/dist/identity/ports.d.ts +31 -0
  28. package/dist/identity/ports.d.ts.map +1 -0
  29. package/dist/identity/ports.js +9 -0
  30. package/dist/identity/ports.js.map +1 -0
  31. package/dist/identity/product-state.d.ts +49 -0
  32. package/dist/identity/product-state.d.ts.map +1 -0
  33. package/dist/identity/product-state.js +55 -0
  34. package/dist/identity/product-state.js.map +1 -0
  35. package/dist/index.d.ts +19 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +19 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/supervision/app-health.d.ts +148 -0
  40. package/dist/supervision/app-health.d.ts.map +1 -0
  41. package/dist/supervision/app-health.js +165 -0
  42. package/dist/supervision/app-health.js.map +1 -0
  43. package/dist/supervision/app-restart.d.ts +105 -0
  44. package/dist/supervision/app-restart.d.ts.map +1 -0
  45. package/dist/supervision/app-restart.js +97 -0
  46. package/dist/supervision/app-restart.js.map +1 -0
  47. package/dist/supervision/health.d.ts +218 -0
  48. package/dist/supervision/health.d.ts.map +1 -0
  49. package/dist/supervision/health.js +227 -0
  50. package/dist/supervision/health.js.map +1 -0
  51. package/dist/supervision/index.d.ts +17 -0
  52. package/dist/supervision/index.d.ts.map +1 -0
  53. package/dist/supervision/index.js +17 -0
  54. package/dist/supervision/index.js.map +1 -0
  55. package/dist/supervision/service-registration.d.ts +343 -0
  56. package/dist/supervision/service-registration.d.ts.map +1 -0
  57. package/dist/supervision/service-registration.js +543 -0
  58. package/dist/supervision/service-registration.js.map +1 -0
  59. package/dist/supervision/update.d.ts +231 -0
  60. package/dist/supervision/update.d.ts.map +1 -0
  61. package/dist/supervision/update.js +336 -0
  62. package/dist/supervision/update.js.map +1 -0
  63. package/dist/transport/admission.d.ts +235 -0
  64. package/dist/transport/admission.d.ts.map +1 -0
  65. package/dist/transport/admission.js +313 -0
  66. package/dist/transport/admission.js.map +1 -0
  67. package/dist/transport/auth.d.ts +232 -0
  68. package/dist/transport/auth.d.ts.map +1 -0
  69. package/dist/transport/auth.js +266 -0
  70. package/dist/transport/auth.js.map +1 -0
  71. package/dist/transport/index.d.ts +21 -0
  72. package/dist/transport/index.d.ts.map +1 -0
  73. package/dist/transport/index.js +21 -0
  74. package/dist/transport/index.js.map +1 -0
  75. package/dist/transport/ingress.d.ts +94 -0
  76. package/dist/transport/ingress.d.ts.map +1 -0
  77. package/dist/transport/ingress.js +171 -0
  78. package/dist/transport/ingress.js.map +1 -0
  79. package/dist/transport/routing.d.ts +235 -0
  80. package/dist/transport/routing.d.ts.map +1 -0
  81. package/dist/transport/routing.js +288 -0
  82. package/dist/transport/routing.js.map +1 -0
  83. package/dist/transport/shipped-routes.d.ts +21 -0
  84. package/dist/transport/shipped-routes.d.ts.map +1 -0
  85. package/dist/transport/shipped-routes.js +53 -0
  86. package/dist/transport/shipped-routes.js.map +1 -0
  87. package/package.json +51 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingress.d.ts","sourceRoot":"","sources":["../../src/transport/ingress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAM7C,kGAAkG;AAClG,eAAO,MAAM,oBAAoB,QAAkB,CAAC;AAEpD,8HAA8H;AAC9H,eAAO,MAAM,yBAAyB;;;CAG5B,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAkBpC,CAAC;AAEZ;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS7F;AAED,+GAA+G;AAC/G,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAElE;AAMD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,0HAA0H;AAC1H,eAAO,MAAM,sBAAsB,4EAIzB,CAAC;AAEX,qGAAqG;AACrG,eAAO,MAAM,aAAa,4EAIhB,CAAC;AAEX,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,kBAAkB,GAAG,IAAI,CAiBhF;AAED,uGAAuG;AACvG,MAAM,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;AAEzF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,WAAW,EACpB,SAAS,SAAW,GACnB,MAAM,CAER;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAM5G;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,mOAYtB,CAAC;AAEX,kFAAkF;AAClF,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOtF"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Event-rail ingress — the pipe half of the webhook rail. Success at
3
+ * this layer means exactly one thing: the immutable envelope (raw
4
+ * body + allowlisted headers) is durably queued and acknowledged.
5
+ * What a delivery TRIGGERS — the automation fan-out below
6
+ * events/processor.js:119 — is product logic and deliberately not
7
+ * here: the pipe stores and acknowledges the mail, it never reads it
8
+ * (axiom 3). The signature laws live at this layer because verifying
9
+ * WHO sent a payload is admission, not interpretation.
10
+ *
11
+ * Ported from amalgm-mcp/events/ingress.js (receipt contract) and
12
+ * events/matcher.js (signature laws — already pure in the engine).
13
+ */
14
+ // ---------------------------------------------------------------------------
15
+ // Receipt contract — events/ingress.js
16
+ // ---------------------------------------------------------------------------
17
+ /** ingress.js:12 — the body cap. Counted over raw bytes as they stream in, not after assembly. */
18
+ export const MAX_EVENT_BODY_BYTES = 2 * 1024 * 1024;
19
+ /** The over-limit rejection — ingress.js:50-53 surfaced through the generic error path (:80): status + byte-exact message. */
20
+ export const EVENT_BODY_LIMIT_EXCEEDED = {
21
+ status: 413,
22
+ message: `Event body exceeds ${MAX_EVENT_BODY_BYTES} bytes`,
23
+ };
24
+ /**
25
+ * The 17-header storage allowlist — ingress.js:13-31. Everything else
26
+ * is dropped BEFORE the envelope is persisted: the envelope is
27
+ * immutable and replayed, so an unlisted header stored today is a
28
+ * secret leaked forever. The list is exactly: routing identity
29
+ * (x-amalgm-*), each supported sender's event/delivery markers, and
30
+ * every signature/secret header the matcher may need to re-verify.
31
+ */
32
+ export const STORED_HEADERS = new Set([
33
+ 'authorization',
34
+ 'content-type',
35
+ 'stripe-signature',
36
+ 'x-amalgm-delivery-id',
37
+ 'x-amalgm-event',
38
+ 'x-amalgm-source',
39
+ 'x-amalgm-webhook-secret',
40
+ 'x-github-delivery',
41
+ 'x-github-event',
42
+ 'x-gitlab-event',
43
+ 'x-gitlab-token',
44
+ 'x-hub-signature',
45
+ 'x-hub-signature-256',
46
+ 'x-linear-event',
47
+ 'x-linear-signature',
48
+ 'x-webhook-secret',
49
+ 'x-webhook-signature',
50
+ ]);
51
+ /**
52
+ * ingress.js:33-42 — filter to the allowlist, lowercase the names,
53
+ * join repeated values with ', ', stringify the rest. The result is
54
+ * the envelope's header record — and the flattened shape every
55
+ * signature function below reads.
56
+ */
57
+ export function storedHeaders(headers) {
58
+ const result = {};
59
+ for (const [key, value] of Object.entries(headers || {})) {
60
+ const normalized = key.toLowerCase();
61
+ if (!STORED_HEADERS.has(normalized))
62
+ continue;
63
+ if (Array.isArray(value))
64
+ result[normalized] = value.join(', ');
65
+ else if (value != null)
66
+ result[normalized] = String(value);
67
+ }
68
+ return result;
69
+ }
70
+ /** The receipt acknowledgment — ingress.js:73-78: 202, because acceptance is durable-queued, not processed. */
71
+ export const EVENT_ACCEPTED_STATUS = 202;
72
+ export function eventAcceptedBody(deliveryId, status) {
73
+ return { ok: true, accepted: true, deliveryId, status };
74
+ }
75
+ /** matcher.js:13-17 — HMAC proof headers, in precedence order (generic first, then GitHub's sha256, then legacy sha1). */
76
+ export const HMAC_SIGNATURE_HEADERS = [
77
+ 'x-webhook-signature',
78
+ 'x-hub-signature-256',
79
+ 'x-hub-signature',
80
+ ];
81
+ /** matcher.js:19-23 — plain shared-secret headers, in precedence order (ours, generic, GitLab's). */
82
+ export const TOKEN_HEADERS = [
83
+ 'x-amalgm-webhook-secret',
84
+ 'x-webhook-secret',
85
+ 'x-gitlab-token',
86
+ ];
87
+ /**
88
+ * The extraction order — matcher.js:28-43: every HMAC header first
89
+ * (an HMAC proof is stronger than a bearer secret, so it must win
90
+ * when a sender supplies both), then `Authorization: Bearer <secret>`
91
+ * (reported under header 'authorization'), then the plain token
92
+ * headers. First PRESENT header wins — an empty value reads as
93
+ * absent. Null when nothing proves anything.
94
+ */
95
+ export function extractSignature(headers) {
96
+ for (const header of HMAC_SIGNATURE_HEADERS) {
97
+ const value = headers[header];
98
+ if (value)
99
+ return { header, value };
100
+ }
101
+ const authorization = headers['authorization'];
102
+ if (typeof authorization === 'string') {
103
+ const match = authorization.match(/^Bearer\s+(.+)$/i);
104
+ if (match)
105
+ return { header: 'authorization', value: match[1] ?? '' };
106
+ }
107
+ for (const header of TOKEN_HEADERS) {
108
+ const value = headers[header];
109
+ if (value)
110
+ return { header, value };
111
+ }
112
+ return null;
113
+ }
114
+ /**
115
+ * matcher.js:48-50 — the expected proof is `<algorithm>=<hex>`,
116
+ * sha256 unless the caller says otherwise (verification picks sha1
117
+ * only when the presented value announces it — GitHub's legacy
118
+ * x-hub-signature).
119
+ */
120
+ export function computeSignature(secret, rawBody, hmacHex, algorithm = 'sha256') {
121
+ return `${algorithm}=` + hmacHex(algorithm, secret, rawBody);
122
+ }
123
+ /**
124
+ * matcher.js:122-128 — the human-readable source label, first
125
+ * recognized marker wins in this order: github → stripe → linear →
126
+ * gitlab → 'external'.
127
+ */
128
+ export function pickSourceLabel(headers) {
129
+ if (headers['x-github-event'])
130
+ return 'github';
131
+ if (headers['stripe-signature'])
132
+ return 'stripe';
133
+ if (headers['x-linear-event'])
134
+ return 'linear';
135
+ if (headers['x-gitlab-event'])
136
+ return 'gitlab';
137
+ return 'external';
138
+ }
139
+ /**
140
+ * The 11 headers worth handing to the agent run context —
141
+ * matcher.js:133-145. Note the deliberate asymmetry with
142
+ * STORED_HEADERS: secrets used only for admission (authorization,
143
+ * x-amalgm-webhook-secret, x-webhook-secret) are stored for
144
+ * re-verification but NOT passed through to agent context — except
145
+ * x-gitlab-token, which GitLab overloads as both auth and event
146
+ * metadata.
147
+ */
148
+ export const PASSTHROUGH_HEADERS = [
149
+ 'x-github-event',
150
+ 'x-github-delivery',
151
+ 'x-hub-signature-256',
152
+ 'x-hub-signature',
153
+ 'stripe-signature',
154
+ 'x-webhook-signature',
155
+ 'content-type',
156
+ 'x-linear-event',
157
+ 'x-linear-signature',
158
+ 'x-gitlab-event',
159
+ 'x-gitlab-token',
160
+ ];
161
+ /** matcher.js:147-151 — project the passthrough subset, preserving list order. */
162
+ export function collectPassthroughHeaders(headers) {
163
+ const out = {};
164
+ for (const header of PASSTHROUGH_HEADERS) {
165
+ const value = headers[header];
166
+ if (value)
167
+ out[header] = value;
168
+ }
169
+ return out;
170
+ }
171
+ //# sourceMappingURL=ingress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingress.js","sourceRoot":"","sources":["../../src/transport/ingress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * ONE routing table (axiom 4). The engine carries three drifted copies
3
+ * of these laws — local-gateway.js:133-176,627-654 (the gateway's own
4
+ * dispatch), tunnel-events.js:58-89,279-353 and tunnel-chat.js:33-64,
5
+ * 138-194,286-300 (each tunnel's local delivery) — and every incident
6
+ * where a new REST prefix worked over the tunnel but 404'd locally (or
7
+ * vice versa) is that drift cashing out. This module is the one
8
+ * definition; the table CONTENTS are injected product configuration
9
+ * (the kernel routes, it does not know what chat is — axiom 3), with
10
+ * the engine's shipped lists exported from shipped-routes.ts.
11
+ *
12
+ * The pipe never reads the mail: everything here classifies pathnames
13
+ * and port numbers. Nothing inspects a payload.
14
+ */
15
+ /** The four services a runtime pathname can resolve to. */
16
+ export type RuntimeService = 'gateway' | 'portMonitor' | 'chat' | 'mcp';
17
+ /**
18
+ * The declared-port probe order — tunnel-events.js:315, byte-order
19
+ * preserved (mcp before chat; the sets never overlap in practice, but
20
+ * order is contract until proven dead).
21
+ */
22
+ export declare const RUNTIME_SERVICES: readonly ["gateway", "portMonitor", "mcp", "chat"];
23
+ /**
24
+ * The one routing table. Buckets are prefix lists; gateway
25
+ * additionally owns a set of exact paths. Contents are product
26
+ * configuration — see shippedRouteTable for the engine's lists.
27
+ */
28
+ export interface RouteTable {
29
+ readonly gatewayPrefixes: readonly string[];
30
+ readonly gatewayExactPaths: readonly string[];
31
+ readonly portMonitorPrefixes: readonly string[];
32
+ readonly chatPrefixes: readonly string[];
33
+ readonly mcpPrefixes: readonly string[];
34
+ }
35
+ /**
36
+ * The prefix law — tunnel-events.js:279-281, identical in chat and
37
+ * gateway: a prefix claims itself and everything below a '/'
38
+ * boundary. '/chat' claims '/chat' and '/chat/x', never
39
+ * '/chat-payloads' — segment-honest matching is what lets '/chat' and
40
+ * '/chat-payloads' live in different buckets.
41
+ */
42
+ export declare function prefixMatches(pathname: string, prefix: string): boolean;
43
+ /**
44
+ * tunnel-events.js:283-289 (framePathname) / runtime-auth.js:102-108
45
+ * (pathnameFor) — one copy. Tunnel frame paths and req.url both carry
46
+ * query strings; routing decisions are over the pathname alone.
47
+ * Unparseable input routes as '/'.
48
+ */
49
+ export declare function pathnameOf(path: string | null | undefined): string;
50
+ /**
51
+ * The classifier — canonical order is the TUNNEL order
52
+ * (tunnel-events.js:291-300): gateway (exact paths first, then
53
+ * prefixes) → portMonitor → chat → mcp → null.
54
+ *
55
+ * The engine's gateway checks chat → mcp → portMonitor instead
56
+ * (local-gateway.js:627-647, after handling its own routes). The two
57
+ * orders are currently indistinguishable because no prefix in one
58
+ * bucket sits under a prefix in another — no pathname can match two
59
+ * buckets, so match order never decides a winner. That is an
60
+ * assumption about table CONTENTS, so assertNoBucketCollisions below
61
+ * makes it executable: run it over any injected table and the
62
+ * "order doesn't matter" claim is proven, not remembered.
63
+ */
64
+ export declare function serviceForRuntimePath(pathname: string, table: RouteTable): RuntimeService | null;
65
+ /**
66
+ * The executable form of the order-indistinguishability assumption:
67
+ * two prefixes from different buckets may never claim the same
68
+ * pathname. Prefix a and prefix b share a claimable pathname exactly
69
+ * when one prefix-matches the other (then the deeper prefix itself is
70
+ * such a pathname), so that is the whole check. Gateway exact paths
71
+ * are also checked against the other buckets' prefixes: both shipped
72
+ * orders test gateway first, and this keeps even that ordering
73
+ * non-load-bearing.
74
+ */
75
+ export declare function assertNoBucketCollisions(table: RouteTable): void;
76
+ /**
77
+ * The gateway's prefix-strip law — local-gateway.js:637-645: before
78
+ * proxying to the port monitor it strips '/port-monitor', else
79
+ * '/ports' (first match wins, checked in this order), but deliberately
80
+ * NOT '/api/ports' — that one is the port monitor's own native prefix
81
+ * and forwards verbatim. '/chat' and '/mcp'-bucket paths always
82
+ * forward verbatim (stripPrefix '').
83
+ */
84
+ export declare const PORT_MONITOR_STRIP_PREFIXES: readonly ["/port-monitor", "/ports"];
85
+ export interface ForwardTarget {
86
+ readonly service: RuntimeService;
87
+ readonly forwardPath: string;
88
+ }
89
+ /**
90
+ * Classify a pathname and compute the path to forward upstream —
91
+ * local-gateway.js:627-647 (serviceForPath) + :687-692 (proxiedPath,
92
+ * slice then `|| '/'`), generalized over the injected table. Pathname
93
+ * in, pathname out: the host re-attaches the query string, as
94
+ * proxiedPath does.
95
+ *
96
+ * DRIFT, documented as a wound the cutover closes: the tunnels
97
+ * forward frame paths VERBATIM to the resolved service
98
+ * (tunnel-events.js:678-684 passes frame.path straight to
99
+ * http.request), so '/ports/x' reaches the port monitor as '/ports/x'
100
+ * over a tunnel but as '/x' through the gateway. The gateway behavior
101
+ * ported here is canonical; when the tunnels adopt this function the
102
+ * two paths converge.
103
+ *
104
+ * Two deliberate departures from the engine's serviceForPath, both
105
+ * because this is the whole table rather than the gateway's slice of
106
+ * it: gateway-bucket paths return {service:'gateway'} instead of null
107
+ * (the engine returns null there because the gateway has already
108
+ * handled its own routes by that line), and the strip test uses the
109
+ * classified pathname, where the engine's raw startsWith is
110
+ * indistinguishable ('/portsfoo' never classifies as portMonitor).
111
+ */
112
+ export declare function forwardPathForService(pathname: string, table: RouteTable): ForwardTarget | null;
113
+ /** tunnel-events.js:274-277 — a port is an integer in (0, 65535], or it is nothing. */
114
+ export declare function validPort(value: unknown): number | null;
115
+ /** A fully resolved port per service (defaults come from the host's runtime manifest). */
116
+ export interface RuntimePorts {
117
+ readonly gateway: number;
118
+ readonly portMonitor: number;
119
+ readonly mcp: number;
120
+ readonly chat: number;
121
+ }
122
+ /** Possibly-partial ports as read from a runtime-state file of unknown age. */
123
+ export type RuntimePortsLike = {
124
+ readonly [service in RuntimeService]?: number | null | undefined;
125
+ };
126
+ /** The subset of runtime-state.json the port mapping reads. */
127
+ export interface RuntimeStateLike {
128
+ readonly gateway_port?: unknown;
129
+ readonly ports?: {
130
+ readonly gateway?: unknown;
131
+ readonly port_monitor?: unknown;
132
+ readonly amalgm_mcp?: unknown;
133
+ readonly chat_server?: unknown;
134
+ } | null;
135
+ }
136
+ /**
137
+ * The runtime-state port mapping — the tunnels' `readRuntimePorts`
138
+ * (tunnel-events.js:255-272, byte-identical in tunnel-chat.js:114-131;
139
+ * the engine name is kept — the manifest's declaration-driven
140
+ * `runtimePortsFromState` is a different law and lives in
141
+ * `supervision/service-registration.ts`). Top-level `gateway_port` wins
142
+ * over `ports.gateway` (older writers only wrote the former); the
143
+ * other services live only under `ports.*` in snake_case. `Number(x)
144
+ * || fallback` verbatim: absent, unparseable, and zero all fall back.
145
+ * The engine's catch-branch (unreadable file) is the host's: pass no
146
+ * state and the fallbacks come through whole.
147
+ */
148
+ export declare function readRuntimePorts(state: RuntimeStateLike | null | undefined, fallbacks: RuntimePorts): RuntimePorts;
149
+ /**
150
+ * The mirror-write shape — local-gateway.js:395-402: what the gateway
151
+ * writes is exactly what readRuntimePorts reads, top-level
152
+ * `gateway_port` doubled into `ports.gateway` included.
153
+ */
154
+ export declare function runtimeStatePortsPatch(ports: RuntimePorts): {
155
+ gateway_port: number;
156
+ ports: {
157
+ gateway: number;
158
+ port_monitor: number;
159
+ amalgm_mcp: number;
160
+ chat_server: number;
161
+ };
162
+ };
163
+ /** tunnel-events.js:302-308 — which service does a well-known DEFAULT port imply? Checked against defaults, never live ports. */
164
+ export declare function defaultServiceForPort(port: number, defaults: RuntimePorts): RuntimeService | null;
165
+ /** tunnel-events.js:310-312 — live port if valid, else the default; null when both are broken. */
166
+ export declare function runtimePortForService(runtimePorts: RuntimePortsLike, defaults: RuntimePorts, service: RuntimeService): number | null;
167
+ /** tunnel-events.js:314-316 — is this port one the live runtime actually declared? Order-sensitive over RUNTIME_SERVICES. */
168
+ export declare function isDeclaredRuntimePort(port: number, runtimePorts: RuntimePortsLike): boolean;
169
+ /**
170
+ * The full set of ports a tunnel frame may target —
171
+ * tunnel-events.js:318-326: every resolvable service port plus, on
172
+ * the events tunnel, every advertised app-route port. DRIFT:
173
+ * tunnel-chat.js:177-184 omits the app ports (chat frames never
174
+ * target apps); that divergence is this parameter, not a second copy
175
+ * — pass no appRoutePorts and you have the chat version.
176
+ */
177
+ export declare function allowedTargetPorts(runtimePorts: RuntimePortsLike, defaults: RuntimePorts, appRoutePorts?: Iterable<number>): Set<number>;
178
+ /** The wire fields target resolution reads, named as the tunnel frame names them. */
179
+ export interface TunnelFrameTarget {
180
+ readonly target_port?: unknown;
181
+ readonly path?: string | null;
182
+ }
183
+ export interface ResolveTargetPortOptions {
184
+ readonly runtimePorts: RuntimePortsLike;
185
+ readonly defaults: RuntimePorts;
186
+ readonly table: RouteTable;
187
+ /**
188
+ * 'mcp' for the events tunnel, 'chat' for the chat tunnel. The
189
+ * events engine derives this as `fallbackPort === defaultTargetPort()
190
+ * ? 'mcp' : defaultServiceForPort(fallbackPort)` (tunnel-events.js:343)
191
+ * — but no caller ever passes a fallbackPort, so the derivation is
192
+ * always 'mcp'; the chat tunnel hardcodes 'chat'
193
+ * (tunnel-chat.js:291). Injected here instead of re-porting a dead
194
+ * parameter.
195
+ */
196
+ readonly fallbackService: RuntimeService;
197
+ /** App-route ports (events tunnel only) — see allowedTargetPorts. */
198
+ readonly appRoutePorts?: Iterable<number>;
199
+ }
200
+ /**
201
+ * The target-port law — canonical form is tunnel-events.js:338-353
202
+ * (tunnel-chat.js:286-300 is the same law with fallbackService
203
+ * 'chat' and no app ports):
204
+ * 1. a requested port the live runtime declared is honored verbatim;
205
+ * 2. otherwise the frame's PATH picks the service — but only when no
206
+ * port was requested, or the requested port is a well-known
207
+ * default (a stale peer aiming at where a service used to live:
208
+ * the path outranks the stale number and both remap to the live
209
+ * port);
210
+ * 3. a requested port that names neither a declared port, a default,
211
+ * nor a routable path must be in the allowed set (app routes) or
212
+ * it resolves to null — and null is the denial contract below.
213
+ */
214
+ export declare function resolveTargetPort(frame: TunnelFrameTarget, options: ResolveTargetPortOptions): number | null;
215
+ /** Tunnel res-frame denial — tunnel-events.js:668-674 (and stream/ws variants at :710-715,:778-779). */
216
+ export declare const TARGET_PORT_DENIED_STATUS = 403;
217
+ export declare const TARGET_PORT_DENIED_MESSAGE = "Target port is not allowed";
218
+ /** The frozen body bytes of the non-stream denial (JSON.stringify({ error: ... }) at :673). */
219
+ export declare const TARGET_PORT_DENIED_BODY = "{\"error\":\"Target port is not allowed\"}";
220
+ /** WebSocket denial — tunnel-events.js:779: policy-violation close with the same reason. */
221
+ export declare const WS_CLOSE_TARGET_PORT_DENIED: {
222
+ readonly code: 1008;
223
+ readonly reason: "Target port is not allowed";
224
+ };
225
+ /** Gateway preview denial — local-gateway.js:675. */
226
+ export declare function previewPortDeniedError(port: number): {
227
+ status: 403;
228
+ message: string;
229
+ };
230
+ /** Gateway app denial — local-gateway.js:662. */
231
+ export declare function appNotRoutableError(appRef: string): {
232
+ status: 404;
233
+ message: string;
234
+ };
235
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/transport/routing.ts"],"names":[],"mappings":"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"}