@adonis-agora/durable 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/assets/dashboard.html +116 -3
- package/dist/commands/durable_worker.d.ts +25 -0
- package/dist/commands/durable_worker.d.ts.map +1 -0
- package/dist/commands/durable_worker.js +113 -0
- package/dist/commands/durable_worker.js.map +1 -0
- package/dist/commands/main.d.ts +6 -5
- package/dist/commands/main.d.ts.map +1 -1
- package/dist/commands/main.js +11 -4
- package/dist/commands/main.js.map +1 -1
- package/dist/providers/dashboard_provider.d.ts +38 -0
- package/dist/providers/dashboard_provider.d.ts.map +1 -1
- package/dist/providers/dashboard_provider.js +103 -10
- package/dist/providers/dashboard_provider.js.map +1 -1
- package/dist/providers/durable_provider.d.ts +24 -16
- package/dist/providers/durable_provider.d.ts.map +1 -1
- package/dist/providers/durable_provider.js +195 -20
- package/dist/providers/durable_provider.js.map +1 -1
- package/dist/src/config_types.d.ts +92 -0
- package/dist/src/config_types.d.ts.map +1 -0
- package/dist/src/config_types.js +2 -0
- package/dist/src/config_types.js.map +1 -0
- package/dist/src/dashboard/compat-view.d.ts +13 -0
- package/dist/src/dashboard/compat-view.d.ts.map +1 -0
- package/dist/src/dashboard/compat-view.js +32 -0
- package/dist/src/dashboard/compat-view.js.map +1 -0
- package/dist/src/dashboard/compat.d.ts +76 -0
- package/dist/src/dashboard/compat.d.ts.map +1 -0
- package/dist/src/dashboard/compat.js +130 -0
- package/dist/src/dashboard/compat.js.map +1 -0
- package/dist/src/dashboard/diagnostics-recorder.d.ts +59 -0
- package/dist/src/dashboard/diagnostics-recorder.d.ts.map +1 -0
- package/dist/src/dashboard/diagnostics-recorder.js +75 -0
- package/dist/src/dashboard/diagnostics-recorder.js.map +1 -0
- package/dist/src/dashboard/gateway-adapter.d.ts +37 -0
- package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -0
- package/dist/src/dashboard/gateway-adapter.js +49 -0
- package/dist/src/dashboard/gateway-adapter.js.map +1 -0
- package/dist/src/dashboard/handlers.d.ts +38 -8
- package/dist/src/dashboard/handlers.d.ts.map +1 -1
- package/dist/src/dashboard/handlers.js +17 -1
- package/dist/src/dashboard/handlers.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +10 -3
- package/dist/src/dashboard/index.d.ts.map +1 -1
- package/dist/src/dashboard/index.js +8 -2
- package/dist/src/dashboard/index.js.map +1 -1
- package/dist/src/define_config.d.ts +31 -5
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +4 -3
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/dispatch-routing.d.ts +105 -0
- package/dist/src/dispatch-routing.d.ts.map +1 -0
- package/dist/src/dispatch-routing.js +114 -0
- package/dist/src/dispatch-routing.js.map +1 -0
- package/dist/src/engine.d.ts +129 -3
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +354 -12
- package/dist/src/engine.js.map +1 -1
- package/dist/src/handshake/descriptor.d.ts +128 -0
- package/dist/src/handshake/descriptor.d.ts.map +1 -0
- package/dist/src/handshake/descriptor.js +133 -0
- package/dist/src/handshake/descriptor.js.map +1 -0
- package/dist/src/handshake/negotiate.d.ts +83 -0
- package/dist/src/handshake/negotiate.d.ts.map +1 -0
- package/dist/src/handshake/negotiate.js +108 -0
- package/dist/src/handshake/negotiate.js.map +1 -0
- package/dist/src/handshake/routing.d.ts +51 -0
- package/dist/src/handshake/routing.d.ts.map +1 -0
- package/dist/src/handshake/routing.js +57 -0
- package/dist/src/handshake/routing.js.map +1 -0
- package/dist/src/index.d.ts +14 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces.d.ts +184 -7
- package/dist/src/interfaces.d.ts.map +1 -1
- package/dist/src/remote-workflow-executor.d.ts +25 -1
- package/dist/src/remote-workflow-executor.d.ts.map +1 -1
- package/dist/src/remote-workflow-executor.js +41 -0
- package/dist/src/remote-workflow-executor.js.map +1 -1
- package/dist/src/role_bindings.d.ts +85 -0
- package/dist/src/role_bindings.d.ts.map +1 -0
- package/dist/src/role_bindings.js +73 -0
- package/dist/src/role_bindings.js.map +1 -0
- package/dist/src/run-gateway/interface.d.ts +89 -0
- package/dist/src/run-gateway/interface.d.ts.map +1 -0
- package/dist/src/run-gateway/interface.js +2 -0
- package/dist/src/run-gateway/interface.js.map +1 -0
- package/dist/src/run-gateway/proxy-run-gateway.d.ts +84 -0
- package/dist/src/run-gateway/proxy-run-gateway.d.ts.map +1 -0
- package/dist/src/run-gateway/proxy-run-gateway.js +142 -0
- package/dist/src/run-gateway/proxy-run-gateway.js.map +1 -0
- package/dist/src/run-gateway/run-request-responder.d.ts +67 -0
- package/dist/src/run-gateway/run-request-responder.d.ts.map +1 -0
- package/dist/src/run-gateway/run-request-responder.js +228 -0
- package/dist/src/run-gateway/run-request-responder.js.map +1 -0
- package/dist/src/run-gateway/store-run-gateway.d.ts +90 -0
- package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -0
- package/dist/src/run-gateway/store-run-gateway.js +80 -0
- package/dist/src/run-gateway/store-run-gateway.js.map +1 -0
- package/dist/src/run-gateway/tenant-auth.d.ts +19 -0
- package/dist/src/run-gateway/tenant-auth.d.ts.map +1 -0
- package/dist/src/run-gateway/tenant-auth.js +71 -0
- package/dist/src/run-gateway/tenant-auth.js.map +1 -0
- package/dist/src/services/main.d.ts +16 -3
- package/dist/src/services/main.d.ts.map +1 -1
- package/dist/src/services/main.js +23 -4
- package/dist/src/services/main.js.map +1 -1
- package/dist/src/step-name-symbol.d.ts +3 -0
- package/dist/src/step-name-symbol.d.ts.map +1 -1
- package/dist/src/step-name-symbol.js.map +1 -1
- package/dist/src/step-ref.d.ts +4 -0
- package/dist/src/step-ref.d.ts.map +1 -1
- package/dist/src/step-ref.js +2 -0
- package/dist/src/step-ref.js.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.d.ts +130 -0
- package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -0
- package/dist/src/transports/bullmq/bullmq-transport.js +562 -0
- package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -0
- package/dist/src/transports/bullmq/deps.d.ts +59 -0
- package/dist/src/transports/bullmq/deps.d.ts.map +1 -0
- package/dist/src/transports/bullmq/deps.js +41 -0
- package/dist/src/transports/bullmq/deps.js.map +1 -0
- package/dist/src/transports/bullmq/index.d.ts +5 -0
- package/dist/src/transports/bullmq/index.d.ts.map +1 -0
- package/dist/src/transports/bullmq/index.js +5 -0
- package/dist/src/transports/bullmq/index.js.map +1 -0
- package/dist/src/transports/bullmq/naming.d.ts +71 -0
- package/dist/src/transports/bullmq/naming.d.ts.map +1 -0
- package/dist/src/transports/bullmq/naming.js +101 -0
- package/dist/src/transports/bullmq/naming.js.map +1 -0
- package/dist/src/transports/bullmq/serialization.d.ts +56 -0
- package/dist/src/transports/bullmq/serialization.d.ts.map +1 -0
- package/dist/src/transports/bullmq/serialization.js +101 -0
- package/dist/src/transports/bullmq/serialization.js.map +1 -0
- package/dist/src/transports/factory.d.ts +31 -0
- package/dist/src/transports/factory.d.ts.map +1 -1
- package/dist/src/transports/factory.js +21 -0
- package/dist/src/transports/factory.js.map +1 -1
- package/dist/src/worker-runtime/index.d.ts +22 -0
- package/dist/src/worker-runtime/index.d.ts.map +1 -0
- package/dist/src/worker-runtime/index.js +27 -0
- package/dist/src/worker-runtime/index.js.map +1 -0
- package/dist/src/worker-runtime/naming.d.ts +24 -0
- package/dist/src/worker-runtime/naming.d.ts.map +1 -0
- package/dist/src/worker-runtime/naming.js +28 -0
- package/dist/src/worker-runtime/naming.js.map +1 -0
- package/dist/src/worker-runtime/registry.d.ts +81 -0
- package/dist/src/worker-runtime/registry.d.ts.map +1 -0
- package/dist/src/worker-runtime/registry.js +44 -0
- package/dist/src/worker-runtime/registry.js.map +1 -0
- package/dist/src/worker-runtime/worker-runtime.d.ts +155 -0
- package/dist/src/worker-runtime/worker-runtime.d.ts.map +1 -0
- package/dist/src/worker-runtime/worker-runtime.js +265 -0
- package/dist/src/worker-runtime/worker-runtime.js.map +1 -0
- package/dist/src/workflow-ctx.d.ts +9 -0
- package/dist/src/workflow-ctx.d.ts.map +1 -1
- package/dist/src/workflow-ctx.js +78 -27
- package/dist/src/workflow-ctx.js.map +1 -1
- package/dist/src/workflow-turn.d.ts +167 -0
- package/dist/src/workflow-turn.d.ts.map +1 -0
- package/dist/src/workflow-turn.js +372 -0
- package/dist/src/workflow-turn.js.map +1 -0
- package/package.json +7 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/** `EngineEvent` types that END a run's lifecycle — no further event for that `runId` follows, so it is
|
|
2
|
+
* the safe point to drop the run from the namespace cache. Cancellation + dead-lettering are both
|
|
3
|
+
* emitted as `run.failed`; `run.suspended` is NOT terminal (a suspended run resumes). */
|
|
4
|
+
function isTerminalRunEvent(event) {
|
|
5
|
+
return event.type === 'run.completed' || event.type === 'run.failed';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Operator-side consumer of a store-less tenant's P4 traffic — the TRUST BOUNDARY of the tenant run
|
|
9
|
+
* gateway (spec §8/§9). It answers each {@link RunRequest}/{@link StartRunMessage} against a
|
|
10
|
+
* store-backed {@link RunGateway} (typically `StoreRunGateway`), and re-publishes the engine's
|
|
11
|
+
* lifecycle events to each tenant's channel.
|
|
12
|
+
*
|
|
13
|
+
* Enforcement, per request, BEFORE any run is touched:
|
|
14
|
+
* 1. **Authenticate + derive tenant.** With a {@link RunRequestResponderOptions.verifyTenant}, the
|
|
15
|
+
* signed token (carried in the wire `tenant` field) is verified and the real tenant is DERIVED from
|
|
16
|
+
* it; the body's claim is ignored. An invalid token short-circuits to an `unauthorized` reply.
|
|
17
|
+
* 2. **`listRuns` is namespace-forced** to the derived tenant — the client's `namespace` is discarded,
|
|
18
|
+
* never merely validated, so a tenant cannot enumerate another's runs.
|
|
19
|
+
* 3. **`workerHealth` is group-scoped** to the tenant's own `@<tenant>` queues.
|
|
20
|
+
* 4. **Every runId-bearing verb** (`getRun`/`getCheckpoints`/`getSearchAttributes`/`signal`/`cancel`/
|
|
21
|
+
* `redispatch`) loads the run FIRST and rejects with `cross-tenant` when `run.namespace` isn't the
|
|
22
|
+
* derived tenant — anti-IDOR, so a tenant can never read OR act on another tenant's run.
|
|
23
|
+
* 5. **`start` forces the run's namespace** to the derived tenant, so a started run is owned by, and
|
|
24
|
+
* later only reachable by, that tenant.
|
|
25
|
+
* 6. **Unknown verbs are rejected** rather than silently ignored.
|
|
26
|
+
*
|
|
27
|
+
* Do not weaken any of these — they are the isolation guarantee of the store-less cluster.
|
|
28
|
+
*/
|
|
29
|
+
export class RunRequestResponder {
|
|
30
|
+
#transport;
|
|
31
|
+
#gateway;
|
|
32
|
+
#verifyTenant;
|
|
33
|
+
#subscribeEngineEvents;
|
|
34
|
+
// Per-run namespace memo for tenant-events republishing: read the store at most once per run id (a
|
|
35
|
+
// `step.*` event carries no namespace), dropped on the run's terminal event so it stays bounded to
|
|
36
|
+
// in-flight runs. `null` = "resolved, not a tenant" (distinct from absent = "not yet resolved").
|
|
37
|
+
#runNamespaces = new Map();
|
|
38
|
+
#unsubscribeEvents;
|
|
39
|
+
constructor(transport, gateway, options = {}) {
|
|
40
|
+
this.#transport = transport;
|
|
41
|
+
this.#gateway = gateway;
|
|
42
|
+
this.#verifyTenant = options.verifyTenant;
|
|
43
|
+
this.#subscribeEngineEvents = options.subscribeEngineEvents;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Register the consumers on the transport (idempotent per transport). Each request is answered
|
|
47
|
+
* independently; a handler failure never throws back into the transport — every error is captured
|
|
48
|
+
* into an error reply. If an engine-event source was given, starts re-publishing tenant events.
|
|
49
|
+
*/
|
|
50
|
+
start() {
|
|
51
|
+
this.#transport.onRunRequest(async (msg) => {
|
|
52
|
+
const reply = await this.#handleRequest(msg);
|
|
53
|
+
await this.#transport.publishRunReply(reply);
|
|
54
|
+
});
|
|
55
|
+
this.#transport.onStartRun(async (msg) => {
|
|
56
|
+
const reply = await this.#handleStart(msg);
|
|
57
|
+
if (reply)
|
|
58
|
+
await this.#transport.publishRunReply(reply);
|
|
59
|
+
});
|
|
60
|
+
if (this.#subscribeEngineEvents) {
|
|
61
|
+
this.#unsubscribeEvents = this.#subscribeEngineEvents((event) => {
|
|
62
|
+
void this.#republish(event);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Stop re-publishing tenant events (the transport consumers are torn down with the transport). */
|
|
67
|
+
stop() {
|
|
68
|
+
this.#unsubscribeEvents?.();
|
|
69
|
+
this.#unsubscribeEvents = undefined;
|
|
70
|
+
this.#runNamespaces.clear();
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// read / control
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
async #handleRequest(msg) {
|
|
76
|
+
const tenant = await this.#resolveTenant(msg.tenant);
|
|
77
|
+
if (tenant === null) {
|
|
78
|
+
return reply(msg.requestId, {
|
|
79
|
+
ok: false,
|
|
80
|
+
error: { message: 'invalid or unauthorized tenant token', code: 'unauthorized' },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const { body } = msg;
|
|
84
|
+
if (body.kind === 'listRuns') {
|
|
85
|
+
// Force the namespace to the verified tenant — the client value is DISCARDED, never validated, so
|
|
86
|
+
// a tenant can't widen its own query into another's namespace.
|
|
87
|
+
const data = await this.#gateway.listRuns({ ...body.query, namespace: tenant });
|
|
88
|
+
return reply(msg.requestId, { ok: true, data });
|
|
89
|
+
}
|
|
90
|
+
if (body.kind === 'workerHealth') {
|
|
91
|
+
// Not runId-bearing. Scope by the group-name convention instead: a tenant's queues are suffixed
|
|
92
|
+
// `<name>@<tenant>`, so keep only groups ending in `@<tenant>` — the operator's own bare groups
|
|
93
|
+
// and every other tenant's are dropped.
|
|
94
|
+
const all = await this.#gateway.workerHealth();
|
|
95
|
+
const data = all.filter((h) => h.group.endsWith(`@${tenant}`));
|
|
96
|
+
return reply(msg.requestId, { ok: true, data });
|
|
97
|
+
}
|
|
98
|
+
// Every remaining verb is runId-bearing. Load the run FIRST — before calling the verb — so a
|
|
99
|
+
// cross-tenant request never reaches the gateway's mutating methods (signal/cancel/redispatch) or
|
|
100
|
+
// leaks another tenant's run via a read (getRun/getCheckpoints/getSearchAttributes).
|
|
101
|
+
const run = await this.#gateway.getRun(body.runId);
|
|
102
|
+
if (run && run.namespace !== tenant) {
|
|
103
|
+
return reply(msg.requestId, {
|
|
104
|
+
ok: false,
|
|
105
|
+
error: { message: 'run belongs to another tenant', code: 'cross-tenant' },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const data = await this.#callVerb(body, run);
|
|
110
|
+
return reply(msg.requestId, { ok: true, data });
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return reply(msg.requestId, {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: { message: err instanceof Error ? err.message : String(err) },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Dispatch a runId-bearing verb. `run` is the already-loaded run (ownership-checked by the caller) —
|
|
120
|
+
* reused for `getRun` so it isn't fetched twice. An unknown verb is rejected, never silently dropped. */
|
|
121
|
+
#callVerb(body, run) {
|
|
122
|
+
switch (body.kind) {
|
|
123
|
+
case 'getRun':
|
|
124
|
+
return Promise.resolve(run);
|
|
125
|
+
case 'getCheckpoints':
|
|
126
|
+
return this.#gateway.getCheckpoints(body.runId);
|
|
127
|
+
case 'getRunChildren':
|
|
128
|
+
return this.#gateway.getRunChildren(body.runId);
|
|
129
|
+
case 'getSearchAttributes':
|
|
130
|
+
return this.#gateway.getSearchAttributes(body.runId);
|
|
131
|
+
case 'signal':
|
|
132
|
+
return this.#gateway.signal(body.runId, body.signal, body.payload);
|
|
133
|
+
case 'cancel':
|
|
134
|
+
return this.#gateway.cancel(body.runId, body.opts);
|
|
135
|
+
case 'redispatch':
|
|
136
|
+
return this.#gateway.redispatchPending(body.runId);
|
|
137
|
+
default:
|
|
138
|
+
// Exhaustiveness guard: an unrecognised verb (a newer/incompatible client) is rejected loudly.
|
|
139
|
+
return Promise.reject(new Error(`unknown run-request verb: ${body.kind}`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// start-run
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
/**
|
|
146
|
+
* Answer a start-run. The reply is correlated by the message's `runId` (the proxy mints/forwards one
|
|
147
|
+
* so the fire-and-forget aviary `StartRunMessage` can carry a correlated answer). The run's namespace
|
|
148
|
+
* is FORCED to the verified tenant, so the started run is owned by that tenant and later reachable
|
|
149
|
+
* only by it. Returns `undefined` (no reply) only when there is no id to correlate on.
|
|
150
|
+
*/
|
|
151
|
+
async #handleStart(msg) {
|
|
152
|
+
const correlationId = msg.runId;
|
|
153
|
+
const tenant = await this.#resolveTenant(msg.tenant);
|
|
154
|
+
if (tenant === null) {
|
|
155
|
+
return correlationId === undefined
|
|
156
|
+
? undefined
|
|
157
|
+
: reply(correlationId, {
|
|
158
|
+
ok: false,
|
|
159
|
+
error: { message: 'invalid or unauthorized tenant token', code: 'unauthorized' },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const result = await this.#gateway.start(msg.workflow, msg.input, {
|
|
164
|
+
...(msg.runId !== undefined ? { runId: msg.runId } : {}),
|
|
165
|
+
...(msg.tags !== undefined ? { tags: msg.tags } : {}),
|
|
166
|
+
...(msg.searchAttributes !== undefined ? { searchAttributes: msg.searchAttributes } : {}),
|
|
167
|
+
// Force the run's namespace to the verified tenant — never a body claim (anti-IDOR at the root).
|
|
168
|
+
namespace: tenant,
|
|
169
|
+
});
|
|
170
|
+
return reply(correlationId ?? result.runId, { ok: true, data: result });
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
return correlationId === undefined
|
|
174
|
+
? undefined
|
|
175
|
+
: reply(correlationId, {
|
|
176
|
+
ok: false,
|
|
177
|
+
error: { message: err instanceof Error ? err.message : String(err) },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// tenant-events republishing
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
async #republish(event) {
|
|
185
|
+
const namespace = await this.#namespaceFor(event);
|
|
186
|
+
// A terminal run emits no further events — drop its cache entry now regardless of whether this event
|
|
187
|
+
// was itself re-published, so the slot is reclaimed even for a bare/default run.
|
|
188
|
+
if (isTerminalRunEvent(event))
|
|
189
|
+
this.#runNamespaces.delete(event.runId);
|
|
190
|
+
// Skip bare/default runs — no real tenant to re-publish to.
|
|
191
|
+
if (!namespace || namespace === 'default')
|
|
192
|
+
return;
|
|
193
|
+
await this.#transport.publishTenantEvent({ tenant: namespace, event }).catch(() => undefined);
|
|
194
|
+
}
|
|
195
|
+
async #namespaceFor(event) {
|
|
196
|
+
if (this.#runNamespaces.has(event.runId)) {
|
|
197
|
+
const cached = this.#runNamespaces.get(event.runId);
|
|
198
|
+
return cached === null ? undefined : cached;
|
|
199
|
+
}
|
|
200
|
+
if (event.namespace !== undefined) {
|
|
201
|
+
this.#runNamespaces.set(event.runId, event.namespace === 'default' ? null : event.namespace);
|
|
202
|
+
return event.namespace === 'default' ? undefined : event.namespace;
|
|
203
|
+
}
|
|
204
|
+
const run = await this.#gateway.getRun(event.runId);
|
|
205
|
+
const ns = run?.namespace !== undefined && run.namespace !== 'default' ? run.namespace : null;
|
|
206
|
+
this.#runNamespaces.set(event.runId, ns);
|
|
207
|
+
return ns === null ? undefined : ns;
|
|
208
|
+
}
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// trust boundary
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
/**
|
|
213
|
+
* Resolve the tenant a request is ALLOWED to act as. With a verifier, the wire `tenant` is treated as
|
|
214
|
+
* the signed token: it is verified and the real tenant DERIVED from it (`null` on reject). Without a
|
|
215
|
+
* verifier, the wire value is trusted verbatim (prefix/network isolation only, aviary-compatible).
|
|
216
|
+
*/
|
|
217
|
+
async #resolveTenant(wireTenant) {
|
|
218
|
+
if (!this.#verifyTenant)
|
|
219
|
+
return wireTenant;
|
|
220
|
+
const verified = await this.#verifyTenant({ token: wireTenant, tenant: wireTenant });
|
|
221
|
+
return verified ? verified.tenant : null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/** Build a correlated {@link RunReply}. */
|
|
225
|
+
function reply(requestId, result) {
|
|
226
|
+
return { requestId, result };
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=run-request-responder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-request-responder.js","sourceRoot":"","sources":["../../../src/run-gateway/run-request-responder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { EngineEvent, GroupHealth, RunQuery, RunResult, SearchAttributes, StepCheckpoint, WorkflowRun } from '../interfaces.js';
|
|
2
|
+
import type { WorkflowRef } from '../workflow-ref.js';
|
|
3
|
+
import type { DurableTopology, RunGateway, StartRunOptions } from './interface.js';
|
|
4
|
+
/**
|
|
5
|
+
* The slice of {@link WorkflowEngine} the {@link StoreRunGateway} delegates to. Declared structurally
|
|
6
|
+
* (not by importing the concrete class) so the gateway depends only on the verbs it forwards, and so
|
|
7
|
+
* `redispatchPending` — which the AdonisJS engine does NOT yet implement (the lost-remote-step
|
|
8
|
+
* re-dispatch machinery from aviary's core engine isn't ported) — can be declared OPTIONAL: the
|
|
9
|
+
* gateway calls it when present and degrades to `null` when absent. The real `WorkflowEngine` is
|
|
10
|
+
* structurally assignable to this.
|
|
11
|
+
*
|
|
12
|
+
* TODO(integrator): once the engine grows a `redispatchPending(runId)` method (port of aviary
|
|
13
|
+
* `WorkflowEngine.redispatchPending`), this optional member becomes a hard delegation. Consider
|
|
14
|
+
* exporting a shared engine-facing port type from the package so this local structural mirror can be
|
|
15
|
+
* dropped.
|
|
16
|
+
*/
|
|
17
|
+
export interface RunGatewayEngine {
|
|
18
|
+
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
19
|
+
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
20
|
+
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
21
|
+
getRunChildren(parentRunId: string): Promise<string[]>;
|
|
22
|
+
workerHealth(extra?: string[]): Promise<GroupHealth[]>;
|
|
23
|
+
start(workflow: string, input: unknown, runId: string, opts?: StartRunOptions): Promise<RunResult>;
|
|
24
|
+
signal(token: string, payload: unknown): Promise<RunResult | null>;
|
|
25
|
+
cancel(runId: string, opts?: {
|
|
26
|
+
compensate?: boolean;
|
|
27
|
+
}): Promise<RunResult | null>;
|
|
28
|
+
subscribe(listener: (event: EngineEvent) => void): () => void;
|
|
29
|
+
/** Optional — absent on the current AdonisJS engine; see {@link RunGatewayEngine} note. */
|
|
30
|
+
redispatchPending?(runId: string): Promise<(RunResult & {
|
|
31
|
+
redispatched: number;
|
|
32
|
+
}) | null>;
|
|
33
|
+
}
|
|
34
|
+
/** Options for {@link StoreRunGateway}. */
|
|
35
|
+
export interface StoreRunGatewayOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The role reported by {@link StoreRunGateway.topology}. `standalone` for a single-process
|
|
38
|
+
* deployment (the default — this package's first target); `control-plane` when this engine is the
|
|
39
|
+
* store-owning operator that tenant proxies round-trip to. A store-backed gateway is never a
|
|
40
|
+
* `tenant` (that's the proxy's role), so only these two are accepted.
|
|
41
|
+
*/
|
|
42
|
+
role?: 'standalone' | 'control-plane' | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Store-backed {@link RunGateway} — the operator-side implementation. Wraps the local
|
|
46
|
+
* {@link WorkflowEngine} and forwards each read/control/stream verb to the engine's existing method,
|
|
47
|
+
* exactly the surface a `standalone` or `control-plane` deployment exposes. It holds NO state of its
|
|
48
|
+
* own beyond the injected engine and its declared role; all durability lives in the engine/store.
|
|
49
|
+
*
|
|
50
|
+
* Idiomatic AdonisJS: a plain class constructed with its dependency, wired by the package's provider /
|
|
51
|
+
* `defineConfig` (no framework DI decorators). A later `ProxyRunGateway` (Wave 2) will implement the
|
|
52
|
+
* SAME {@link RunGateway} interface over the transport for `tenant` deployments — nothing here needs
|
|
53
|
+
* to change for that.
|
|
54
|
+
*/
|
|
55
|
+
export declare class StoreRunGateway implements RunGateway {
|
|
56
|
+
#private;
|
|
57
|
+
constructor(engine: RunGatewayEngine, options?: StoreRunGatewayOptions);
|
|
58
|
+
topology(): DurableTopology;
|
|
59
|
+
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
60
|
+
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
61
|
+
getCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
62
|
+
getRunChildren(runId: string): Promise<string[]>;
|
|
63
|
+
getSearchAttributes(runId: string): Promise<SearchAttributes | undefined>;
|
|
64
|
+
workerHealth(): Promise<GroupHealth[]>;
|
|
65
|
+
start(workflow: WorkflowRef, input: unknown, opts?: StartRunOptions): Promise<RunResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Deliver `signal` (by name) with `payload` to the addressed run. Confirmed against the engine's
|
|
68
|
+
* waiter convention (design §8): `engine.signal` is GENUINELY GLOBAL-BY-NAME — `ctx.waitForSignal(name)`
|
|
69
|
+
* registers a waiter under the caller-chosen `name` VERBATIM, and `engine.signal(name, payload)` matches
|
|
70
|
+
* by that exact string. There is NO `signal:<runId>:<name>` waiter convention: the run-scoped families
|
|
71
|
+
* (`task:<runId>:<name>`, `update:<runId>:<name>`, `wh:<runId>:<seq>`, `child:<id>`) are the FRAMEWORK
|
|
72
|
+
* composing the token before it hands it to `waitForSignal` — an external, user-named signal never is.
|
|
73
|
+
* Deriving a run-scoped token here would therefore reach NO waiter (a run parked on `waitForSignal('go')`
|
|
74
|
+
* would never see `signal('go')`), so the name is forwarded unchanged.
|
|
75
|
+
*
|
|
76
|
+
* `runId` is NOT dropped from the port on purpose: the `RunRequestResponder` uses it for the tenant
|
|
77
|
+
* ownership check (loads the run, rejects cross-tenant) BEFORE this delegates, and a `ProxyRunGateway`
|
|
78
|
+
* uses it to route to the run's owning control plane. So the signal stays name-addressed while the run
|
|
79
|
+
* boundary is still enforced one layer up.
|
|
80
|
+
*/
|
|
81
|
+
signal(_runId: string, signal: string, payload?: unknown): Promise<RunResult | null>;
|
|
82
|
+
cancel(runId: string, opts?: {
|
|
83
|
+
compensate?: boolean;
|
|
84
|
+
}): Promise<RunResult | null>;
|
|
85
|
+
redispatchPending(runId: string): Promise<(RunResult & {
|
|
86
|
+
redispatched: number;
|
|
87
|
+
}) | null>;
|
|
88
|
+
subscribe(runId: string, onEvent: (event: EngineEvent) => void): () => void;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=store-run-gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-run-gateway.d.ts","sourceRoot":"","sources":["../../../src/run-gateway/store-run-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEnF;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAClD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC1D,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvD,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACvD,KAAK,CACH,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,eAAe,GACrB,OAAO,CAAC,SAAS,CAAC,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACnE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAClF,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,2FAA2F;IAC3F,iBAAiB,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,SAAS,GAAG;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC,CAAC;CAC3F;AAED,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,GAAG,eAAe,GAAG,SAAS,CAAC;CACnD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAgB,YAAW,UAAU;;gBAIpC,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAE,sBAA2B;IAK1E,QAAQ,IAAI,eAAe;IAI3B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAIlD,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAIjD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAIxD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI1C,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAK/E,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAIhC,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAO9F;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAIpF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAIjF,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,SAAS,GAAG;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAMxF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI;CAK5E"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store-backed {@link RunGateway} — the operator-side implementation. Wraps the local
|
|
3
|
+
* {@link WorkflowEngine} and forwards each read/control/stream verb to the engine's existing method,
|
|
4
|
+
* exactly the surface a `standalone` or `control-plane` deployment exposes. It holds NO state of its
|
|
5
|
+
* own beyond the injected engine and its declared role; all durability lives in the engine/store.
|
|
6
|
+
*
|
|
7
|
+
* Idiomatic AdonisJS: a plain class constructed with its dependency, wired by the package's provider /
|
|
8
|
+
* `defineConfig` (no framework DI decorators). A later `ProxyRunGateway` (Wave 2) will implement the
|
|
9
|
+
* SAME {@link RunGateway} interface over the transport for `tenant` deployments — nothing here needs
|
|
10
|
+
* to change for that.
|
|
11
|
+
*/
|
|
12
|
+
export class StoreRunGateway {
|
|
13
|
+
#engine;
|
|
14
|
+
#role;
|
|
15
|
+
constructor(engine, options = {}) {
|
|
16
|
+
this.#engine = engine;
|
|
17
|
+
this.#role = options.role ?? 'standalone';
|
|
18
|
+
}
|
|
19
|
+
topology() {
|
|
20
|
+
return { role: this.#role };
|
|
21
|
+
}
|
|
22
|
+
getRun(runId) {
|
|
23
|
+
return this.#engine.getRun(runId);
|
|
24
|
+
}
|
|
25
|
+
listRuns(query) {
|
|
26
|
+
return this.#engine.listRuns(query);
|
|
27
|
+
}
|
|
28
|
+
getCheckpoints(runId) {
|
|
29
|
+
return this.#engine.listCheckpoints(runId);
|
|
30
|
+
}
|
|
31
|
+
getRunChildren(runId) {
|
|
32
|
+
return this.#engine.getRunChildren(runId);
|
|
33
|
+
}
|
|
34
|
+
async getSearchAttributes(runId) {
|
|
35
|
+
const run = await this.#engine.getRun(runId);
|
|
36
|
+
return run?.searchAttributes;
|
|
37
|
+
}
|
|
38
|
+
workerHealth() {
|
|
39
|
+
return this.#engine.workerHealth();
|
|
40
|
+
}
|
|
41
|
+
async start(workflow, input, opts) {
|
|
42
|
+
const runId = opts?.runId ?? globalThis.crypto.randomUUID();
|
|
43
|
+
// `start` is overloaded per ref kind (class | string) on the engine; a `WorkflowRef` union fits
|
|
44
|
+
// neither overload, so resolve to the string overload (the engine handles both at runtime).
|
|
45
|
+
return this.#engine.start(workflow, input, runId, opts);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Deliver `signal` (by name) with `payload` to the addressed run. Confirmed against the engine's
|
|
49
|
+
* waiter convention (design §8): `engine.signal` is GENUINELY GLOBAL-BY-NAME — `ctx.waitForSignal(name)`
|
|
50
|
+
* registers a waiter under the caller-chosen `name` VERBATIM, and `engine.signal(name, payload)` matches
|
|
51
|
+
* by that exact string. There is NO `signal:<runId>:<name>` waiter convention: the run-scoped families
|
|
52
|
+
* (`task:<runId>:<name>`, `update:<runId>:<name>`, `wh:<runId>:<seq>`, `child:<id>`) are the FRAMEWORK
|
|
53
|
+
* composing the token before it hands it to `waitForSignal` — an external, user-named signal never is.
|
|
54
|
+
* Deriving a run-scoped token here would therefore reach NO waiter (a run parked on `waitForSignal('go')`
|
|
55
|
+
* would never see `signal('go')`), so the name is forwarded unchanged.
|
|
56
|
+
*
|
|
57
|
+
* `runId` is NOT dropped from the port on purpose: the `RunRequestResponder` uses it for the tenant
|
|
58
|
+
* ownership check (loads the run, rejects cross-tenant) BEFORE this delegates, and a `ProxyRunGateway`
|
|
59
|
+
* uses it to route to the run's owning control plane. So the signal stays name-addressed while the run
|
|
60
|
+
* boundary is still enforced one layer up.
|
|
61
|
+
*/
|
|
62
|
+
signal(_runId, signal, payload) {
|
|
63
|
+
return this.#engine.signal(signal, payload);
|
|
64
|
+
}
|
|
65
|
+
cancel(runId, opts) {
|
|
66
|
+
return this.#engine.cancel(runId, opts);
|
|
67
|
+
}
|
|
68
|
+
redispatchPending(runId) {
|
|
69
|
+
// The AdonisJS engine doesn't yet expose per-step re-dispatch; degrade to null when absent so the
|
|
70
|
+
// verb stays byte-compatible for a proxy without inventing engine behaviour. See RunGatewayEngine.
|
|
71
|
+
return this.#engine.redispatchPending?.(runId) ?? Promise.resolve(null);
|
|
72
|
+
}
|
|
73
|
+
subscribe(runId, onEvent) {
|
|
74
|
+
return this.#engine.subscribe((event) => {
|
|
75
|
+
if (event.runId === runId)
|
|
76
|
+
onEvent(event);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=store-run-gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-run-gateway.js","sourceRoot":"","sources":["../../../src/run-gateway/store-run-gateway.ts"],"names":[],"mappings":"AAuDA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,eAAe;IACjB,OAAO,CAAmB;IAC1B,KAAK,CAAiC;IAE/C,YAAY,MAAwB,EAAE,UAAkC,EAAE;QACxE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,IAAI,YAAY,CAAC;IAC5C,CAAC;IAED,QAAQ;QACN,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,QAAQ,CAAC,KAAe;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAa;QACrC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,OAAO,GAAG,EAAE,gBAAgB,CAAC;IAC/B,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,QAAqB,EAAE,KAAc,EAAE,IAAsB;QACvE,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC5D,gGAAgG;QAChG,4FAA4F;QAC5F,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAkB,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,OAAiB;QACtD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,KAAa,EAAE,IAA+B;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,iBAAiB,CAAC,KAAa;QAC7B,kGAAkG;QAClG,mGAAmG;QACnG,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,CAAC;IAED,SAAS,CAAC,KAAa,EAAE,OAAqC;QAC5D,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACtC,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;gBAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TenantVerifier } from '../config_types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mint the signed token a tenant pod presents: `` `${tenant}.${base64url(HMAC-SHA256(secret, tenant))}` ``.
|
|
4
|
+
* Put it on the pod's `config/durable.ts` as `tenant.token`; the pod carries only the token (never the
|
|
5
|
+
* secret), and the control plane verifies it with the SAME `secret` via {@link hmacTenantVerifier}.
|
|
6
|
+
*/
|
|
7
|
+
export declare function signTenantToken(tenant: string, secret: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Build the control-plane-side {@link TenantVerifier} for {@link signTenantToken}-minted tokens under
|
|
10
|
+
* `secret`. It parses the token, recomputes the HMAC over the embedded tenant, and compares it in
|
|
11
|
+
* CONSTANT TIME (`timingSafeEqual`) — so a tampered tenant, a forged/absent signature, or a token
|
|
12
|
+
* signed with a different secret all return `null` (rejected). The verified tenant is DERIVED from the
|
|
13
|
+
* token's own claim, and the advisory `tenant` on the request body is ignored (spec §9).
|
|
14
|
+
*
|
|
15
|
+
* `capabilities`, if given, is a static grant stamped onto every {@link VerifiedTenant} this verifier
|
|
16
|
+
* authenticates (the HMAC token carries no scoped claims of its own); omit for none.
|
|
17
|
+
*/
|
|
18
|
+
export declare function hmacTenantVerifier(secret: string, capabilities?: string[]): TenantVerifier;
|
|
19
|
+
//# sourceMappingURL=tenant-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-auth.d.ts","sourceRoot":"","sources":["../../../src/run-gateway/tenant-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,oBAAoB,CAAC;AA8BzE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAY1F"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Layered tenant auth (spec §9). On a store-less pod the `tenant` on a `RunRequest`/`StartRunMessage`
|
|
4
|
+
* is only a CLAIM; without a signed proof the isolation boundary is meaningless. This module provides
|
|
5
|
+
* the default defense-in-depth on top of prefix/network isolation: a symmetric HMAC token a tenant pod
|
|
6
|
+
* carries and presents in the `tenant` wire field, which the control-plane {@link TenantVerifier}
|
|
7
|
+
* checks and DERIVES the real tenant from — never trusting the body.
|
|
8
|
+
*
|
|
9
|
+
* The token is deliberately tiny and self-describing (`<tenant>.<base64url-hmac>`), so it fits the
|
|
10
|
+
* existing byte-compatible `tenant: string` wire field with NO envelope change: an aviary/Python
|
|
11
|
+
* control plane that doesn't verify simply treats the whole string as the tenant name (and finds no
|
|
12
|
+
* runs — a safe failure), while an Adonis control plane configured with {@link hmacTenantVerifier}
|
|
13
|
+
* verifies the signature and scopes to the embedded tenant.
|
|
14
|
+
*
|
|
15
|
+
* HMAC (shared secret) is the zero-infra default; a deployment that prefers asymmetric / issued tokens
|
|
16
|
+
* supplies its own {@link TenantVerifier} in `config/durable.ts` instead. Prefix/network segmentation
|
|
17
|
+
* is a deployment concern (Redis ACLs, per-tenant prefixes) and lives outside this code.
|
|
18
|
+
*/
|
|
19
|
+
/** Separator between the tenant claim and its signature in a token. `.` is URL/JSON-safe and never
|
|
20
|
+
* appears in the base64url signature, so the LAST `.` unambiguously splits the two halves — a tenant
|
|
21
|
+
* name may itself contain dots. */
|
|
22
|
+
const TOKEN_SEP = '.';
|
|
23
|
+
/** The HMAC digest, byte-for-byte, that authenticates `tenant` under `secret`. */
|
|
24
|
+
function sign(tenant, secret) {
|
|
25
|
+
return createHmac('sha256', secret).update(tenant).digest('base64url');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Mint the signed token a tenant pod presents: `` `${tenant}.${base64url(HMAC-SHA256(secret, tenant))}` ``.
|
|
29
|
+
* Put it on the pod's `config/durable.ts` as `tenant.token`; the pod carries only the token (never the
|
|
30
|
+
* secret), and the control plane verifies it with the SAME `secret` via {@link hmacTenantVerifier}.
|
|
31
|
+
*/
|
|
32
|
+
export function signTenantToken(tenant, secret) {
|
|
33
|
+
return `${tenant}${TOKEN_SEP}${sign(tenant, secret)}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build the control-plane-side {@link TenantVerifier} for {@link signTenantToken}-minted tokens under
|
|
37
|
+
* `secret`. It parses the token, recomputes the HMAC over the embedded tenant, and compares it in
|
|
38
|
+
* CONSTANT TIME (`timingSafeEqual`) — so a tampered tenant, a forged/absent signature, or a token
|
|
39
|
+
* signed with a different secret all return `null` (rejected). The verified tenant is DERIVED from the
|
|
40
|
+
* token's own claim, and the advisory `tenant` on the request body is ignored (spec §9).
|
|
41
|
+
*
|
|
42
|
+
* `capabilities`, if given, is a static grant stamped onto every {@link VerifiedTenant} this verifier
|
|
43
|
+
* authenticates (the HMAC token carries no scoped claims of its own); omit for none.
|
|
44
|
+
*/
|
|
45
|
+
export function hmacTenantVerifier(secret, capabilities) {
|
|
46
|
+
return ({ token }) => {
|
|
47
|
+
// The signed token travels in the request's `tenant` field, so the responder passes it as `token`.
|
|
48
|
+
if (typeof token !== 'string' || token.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
const sep = token.lastIndexOf(TOKEN_SEP);
|
|
51
|
+
if (sep <= 0 || sep === token.length - 1)
|
|
52
|
+
return null; // no claim or no signature half
|
|
53
|
+
const tenant = token.slice(0, sep);
|
|
54
|
+
const presented = token.slice(sep + 1);
|
|
55
|
+
const expected = sign(tenant, secret);
|
|
56
|
+
if (!constantTimeEquals(presented, expected))
|
|
57
|
+
return null;
|
|
58
|
+
return capabilities !== undefined ? { tenant, capabilities } : { tenant };
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Constant-time string compare that never throws on a length mismatch (`timingSafeEqual` requires
|
|
62
|
+
* equal-length buffers) and never short-circuits on the first differing byte — so a rejected token
|
|
63
|
+
* leaks nothing about how much of the signature matched. */
|
|
64
|
+
function constantTimeEquals(a, b) {
|
|
65
|
+
const ab = Buffer.from(a);
|
|
66
|
+
const bb = Buffer.from(b);
|
|
67
|
+
if (ab.length !== bb.length)
|
|
68
|
+
return false;
|
|
69
|
+
return timingSafeEqual(ab, bb);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=tenant-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-auth.js","sourceRoot":"","sources":["../../../src/run-gateway/tenant-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG1D;;;;;;;;;;;;;;;;GAgBG;AAEH;;oCAEoC;AACpC,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,kFAAkF;AAClF,SAAS,IAAI,CAAC,MAAc,EAAE,MAAc;IAC1C,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,MAAc;IAC5D,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,YAAuB;IACxE,OAAO,CAAC,EAAE,KAAK,EAAE,EAAyB,EAAE;QAC1C,mGAAmG;QACnG,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACjE,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,gCAAgC;QACvF,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1D,OAAO,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5E,CAAC,CAAC;AACJ,CAAC;AAED;;6DAE6D;AAC7D,SAAS,kBAAkB,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { WorkflowEngine } from '../engine.js';
|
|
2
|
+
import type { RunGateway } from '../run-gateway/interface.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* The ACTIVE role's {@link RunGateway} — the store-less-cluster read/control/start surface (design §8).
|
|
5
|
+
* On `standalone`/`control-plane` it is the store-backed `StoreRunGateway`; on `tenant` it is the
|
|
6
|
+
* `ProxyRunGateway` that round-trips over the wire. App/dashboard/CLI code reads the SAME object either
|
|
7
|
+
* way, so store presence is invisible above this line:
|
|
4
8
|
*
|
|
5
|
-
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { runGateway } from '@adonis-agora/durable/services/main'
|
|
11
|
+
* const run = await runGateway.getRun(runId)
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
declare let runGateway: RunGateway;
|
|
15
|
+
/**
|
|
16
|
+
* The singleton {@link WorkflowEngine} — bound ONLY on the store roles (`standalone`/`control-plane`).
|
|
17
|
+
* A `tenant` pod owns no engine (structural isolation), so this stays `undefined` there; use
|
|
18
|
+
* {@link runGateway} for role-agnostic access.
|
|
6
19
|
*
|
|
7
20
|
* ```ts
|
|
8
21
|
* import engine from '@adonis-agora/durable/services/main'
|
|
9
22
|
* ```
|
|
10
23
|
*/
|
|
11
24
|
declare let engine: WorkflowEngine;
|
|
12
|
-
export { engine as default };
|
|
25
|
+
export { engine as default, runGateway };
|
|
13
26
|
//# sourceMappingURL=main.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;;;;;;;GAUG;AACH,QAAA,IAAI,UAAU,EAAE,UAAU,CAAC;AAE3B;;;;;;;;GAQG;AACH,QAAA,IAAI,MAAM,EAAE,cAAc,CAAC;AAY3B,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import app from '@adonisjs/core/services/app';
|
|
2
2
|
import { WorkflowEngine } from '../engine.js';
|
|
3
|
+
import { DURABLE_RUN_GATEWAY } from '../role_bindings.js';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* The ACTIVE role's {@link RunGateway} — the store-less-cluster read/control/start surface (design §8).
|
|
6
|
+
* On `standalone`/`control-plane` it is the store-backed `StoreRunGateway`; on `tenant` it is the
|
|
7
|
+
* `ProxyRunGateway` that round-trips over the wire. App/dashboard/CLI code reads the SAME object either
|
|
8
|
+
* way, so store presence is invisible above this line:
|
|
5
9
|
*
|
|
6
|
-
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { runGateway } from '@adonis-agora/durable/services/main'
|
|
12
|
+
* const run = await runGateway.getRun(runId)
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
let runGateway;
|
|
16
|
+
/**
|
|
17
|
+
* The singleton {@link WorkflowEngine} — bound ONLY on the store roles (`standalone`/`control-plane`).
|
|
18
|
+
* A `tenant` pod owns no engine (structural isolation), so this stays `undefined` there; use
|
|
19
|
+
* {@link runGateway} for role-agnostic access.
|
|
7
20
|
*
|
|
8
21
|
* ```ts
|
|
9
22
|
* import engine from '@adonis-agora/durable/services/main'
|
|
@@ -11,7 +24,13 @@ import { WorkflowEngine } from '../engine.js';
|
|
|
11
24
|
*/
|
|
12
25
|
let engine;
|
|
13
26
|
await app.booted(async () => {
|
|
14
|
-
|
|
27
|
+
const config = app.config.get('durable', {});
|
|
28
|
+
const role = config.role ?? 'standalone';
|
|
29
|
+
runGateway = await app.container.make(DURABLE_RUN_GATEWAY);
|
|
30
|
+
// Expose the engine only for store roles — a tenant pod has no engine binding to resolve.
|
|
31
|
+
if (role !== 'tenant') {
|
|
32
|
+
engine = await app.container.make(WorkflowEngine);
|
|
33
|
+
}
|
|
15
34
|
});
|
|
16
|
-
export { engine as default };
|
|
35
|
+
export { engine as default, runGateway };
|
|
17
36
|
//# sourceMappingURL=main.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,6BAA6B,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1D;;;;;;;;;;GAUG;AACH,IAAI,UAAsB,CAAC;AAE3B;;;;;;;;GAQG;AACH,IAAI,MAAsB,CAAC;AAE3B,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;IAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAgB,SAAS,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,YAAY,CAAC;IACzC,UAAU,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC3D,0FAA0F;IAC1F,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -40,6 +40,9 @@ export interface StepConfig {
|
|
|
40
40
|
* worker dead and fails the dispatch with a `RemoteStepTimeout` (retryable — re-dispatches per
|
|
41
41
|
* `retries`). Omit to wait indefinitely. */
|
|
42
42
|
timeoutMs?: number | undefined;
|
|
43
|
+
/** Capabilities a live worker must advertise to run this step (design §7.5). Dispatch parks the run
|
|
44
|
+
* `blocked` when no live capable+protocol-compatible worker exists. Absent/empty = runs anywhere. */
|
|
45
|
+
requires?: string[] | undefined;
|
|
43
46
|
}
|
|
44
47
|
/**
|
|
45
48
|
* A method carrying its `@Step`-stamped routing name (and, optionally, its dispatch policy).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-name-symbol.d.ts","sourceRoot":"","sources":["../../src/step-name-symbol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAiD,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACtC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B;;iDAE6C;IAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"step-name-symbol.d.ts","sourceRoot":"","sources":["../../src/step-name-symbol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAiD,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACtC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B;;iDAE6C;IAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B;0GACsG;IACtG,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACjC;AAMD;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,CAAC,CAC1D,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG;IACjC,CAAC,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,CAAC,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAChD,CAAC;AAEF;;uEAEuE;AACvE,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI3D;AAED;;0EAE0E;AAC1E,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAIjE"}
|