@intx/db 0.2.2 → 0.4.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/README.md +13 -2
- package/dist/approval-store.d.ts +59 -0
- package/dist/approval-store.js +92 -0
- package/dist/asset-resolution.d.ts +2 -2
- package/dist/backfill-principal-keys.d.ts +9 -0
- package/dist/backfill-principal-keys.js +51 -0
- package/dist/client.d.ts +37 -2
- package/dist/client.js +6 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -0
- package/dist/connection.js +12 -8
- package/dist/credential-resolution.d.ts +181 -20
- package/dist/credential-resolution.js +251 -3
- package/dist/grant-store.js +35 -13
- package/dist/index.d.ts +20 -5
- package/dist/index.js +19 -5
- package/dist/model-source-resolution.d.ts +54 -7
- package/dist/model-source-resolution.js +177 -22
- package/dist/parse-row.d.ts +245 -34
- package/dist/parse-row.js +117 -18
- package/dist/principal-key-store.d.ts +51 -0
- package/dist/principal-key-store.js +82 -0
- package/dist/principal-store.d.ts +44 -0
- package/dist/principal-store.js +67 -0
- package/dist/rekey-credential-secrets.d.ts +13 -0
- package/dist/rekey-credential-secrets.js +61 -0
- package/dist/schema/approvals.d.ts +245 -0
- package/dist/schema/approvals.js +57 -0
- package/dist/schema/catalog.d.ts +17 -0
- package/dist/schema/catalog.js +7 -1
- package/dist/schema/credentials.d.ts +1 -1
- package/dist/schema/git-tokens.js +1 -1
- package/dist/schema/grants.js +8 -2
- package/dist/schema/index.d.ts +10 -4
- package/dist/schema/index.js +10 -4
- package/dist/schema/messages.d.ts +5 -5
- package/dist/schema/messages.js +20 -9
- package/dist/schema/offerings.js +8 -2
- package/dist/schema/{workflow-deployments.d.ts → principal-keys.d.ts} +29 -32
- package/dist/schema/principal-keys.js +28 -0
- package/dist/schema/principals.d.ts +2 -2
- package/dist/schema/principals.js +7 -5
- package/dist/schema/providers.d.ts +17 -0
- package/dist/schema/providers.js +5 -0
- package/dist/schema/roles.js +6 -2
- package/dist/schema/session-assets.d.ts +1 -38
- package/dist/schema/session-assets.js +36 -19
- package/dist/schema/sessions.js +7 -2
- package/dist/schema/sidecar-allocation.d.ts +385 -0
- package/dist/schema/sidecar-allocation.js +53 -0
- package/dist/schema/sidecar.d.ts +2 -2
- package/dist/schema/sidecar.js +3 -1
- package/dist/schema/signal-correlations.d.ts +177 -0
- package/dist/schema/signal-correlations.js +31 -0
- package/dist/schema/tenants.js +13 -3
- package/dist/schema/wallets.d.ts +3 -3
- package/dist/schema/wallets.js +4 -2
- package/dist/schema/{agents.d.ts → workflow-definitions.d.ts} +79 -113
- package/dist/schema/workflow-definitions.js +97 -0
- package/dist/schema/workflow-probe.d.ts +358 -0
- package/dist/schema/workflow-probe.js +51 -0
- package/dist/schema/workflow-run-dispatch.d.ts +345 -0
- package/dist/schema/workflow-run-dispatch.js +63 -0
- package/dist/schema/{agent-assets.d.ts → workflow-run-execution.d.ts} +44 -27
- package/dist/schema/workflow-run-execution.js +48 -0
- package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
- package/dist/schema/workflow-run-launch-spec.js +26 -0
- package/dist/schema/{instances.d.ts → workflow-run.d.ts} +61 -66
- package/dist/schema/workflow-run.js +110 -0
- package/dist/sender-key-resolver.d.ts +91 -0
- package/dist/sender-key-resolver.js +184 -0
- package/dist/sidecar-allocation-store.d.ts +206 -0
- package/dist/sidecar-allocation-store.js +714 -0
- package/dist/signal-correlation-store.d.ts +36 -0
- package/dist/signal-correlation-store.js +63 -0
- package/dist/signer-identity.d.ts +12 -0
- package/dist/signer-identity.js +15 -0
- package/dist/tenant-hierarchy.d.ts +4 -1
- package/dist/tenant-hierarchy.js +37 -5
- package/dist/workflow-definition-store.d.ts +55 -0
- package/dist/workflow-definition-store.js +93 -0
- package/dist/workflow-probe-store.d.ts +47 -0
- package/dist/workflow-probe-store.js +124 -0
- package/dist/workflow-run-dispatch-store.d.ts +83 -0
- package/dist/workflow-run-dispatch-store.js +308 -0
- package/dist/workflow-run-launch-spec-store.d.ts +13 -0
- package/dist/workflow-run-launch-spec-store.js +30 -0
- package/dist/workflow-run-store.d.ts +54 -0
- package/dist/workflow-run-store.js +85 -0
- package/migrations/0037_credential_use_backfill.sql +39 -0
- package/migrations/0038_chilly_blacklash.sql +37 -0
- package/migrations/0039_quick_carnage.sql +2 -0
- package/migrations/0040_reshape_approval_origin.sql +24 -0
- package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
- package/migrations/0042_youthful_mantis.sql +2 -0
- package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
- package/migrations/0044_model_offering_quirks.sql +1 -0
- package/migrations/0045_create_workflow_run.sql +13 -0
- package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
- package/migrations/0047_create_workflow_definition.sql +30 -0
- package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
- package/migrations/0049_curly_omega_flight.sql +2 -0
- package/migrations/0050_late_crystal.sql +10 -0
- package/migrations/0051_funny_madelyne_pryor.sql +1 -0
- package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
- package/migrations/0053_session_mail_session_id_index.sql +1 -0
- package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
- package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
- package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
- package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
- package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
- package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
- package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
- package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
- package/migrations/0062_drop_agent_asset_table.sql +11 -0
- package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
- package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
- package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
- package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
- package/migrations/0067_add_workflow_definition_kind.sql +10 -0
- package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
- package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
- package/migrations/0070_flashy_proteus.sql +13 -0
- package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
- package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
- package/migrations/0074_add_provider_api_base_url.sql +1 -0
- package/migrations/0075_workflow_run_launch_spec.sql +17 -0
- package/migrations/0076_sidecar_allocation.sql +40 -0
- package/migrations/0077_workflow_run_dispatch.sql +29 -0
- package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
- package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
- package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
- package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
- package/migrations/0082_blue_black_queen.sql +1 -0
- package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
- package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
- package/migrations/0085_add_approval_run_idx.sql +1 -0
- package/migrations/0086_cool_human_cannonball.sql +1 -0
- package/migrations/0087_drop_sidecar_placement.sql +3 -0
- package/migrations/0088_thick_sprite.sql +32 -0
- package/migrations/0089_tense_selene.sql +13 -0
- package/migrations/0090_tenant_domain_lower_unique.sql +2 -0
- package/migrations/0091_workflow_run_dispatch_sender_address.sql +21 -0
- package/migrations/0092_sidecar_destroy_failed.sql +4 -0
- package/migrations/0093_sidecar_initialization.sql +2 -0
- package/migrations/meta/0037_snapshot.json +3237 -0
- package/migrations/meta/0038_snapshot.json +3470 -0
- package/migrations/meta/0039_snapshot.json +3470 -0
- package/migrations/meta/0040_snapshot.json +3489 -0
- package/migrations/meta/0041_snapshot.json +3489 -0
- package/migrations/meta/0042_snapshot.json +3489 -0
- package/migrations/meta/0043_snapshot.json +3498 -0
- package/migrations/meta/0044_snapshot.json +3504 -0
- package/migrations/meta/0045_snapshot.json +3589 -0
- package/migrations/meta/0046_snapshot.json +3607 -0
- package/migrations/meta/0047_snapshot.json +3832 -0
- package/migrations/meta/0048_snapshot.json +3853 -0
- package/migrations/meta/0049_snapshot.json +3854 -0
- package/migrations/meta/0050_snapshot.json +3924 -0
- package/migrations/meta/0051_snapshot.json +3940 -0
- package/migrations/meta/0052_snapshot.json +3931 -0
- package/migrations/meta/0053_snapshot.json +3952 -0
- package/migrations/meta/0054_snapshot.json +3952 -0
- package/migrations/meta/0055_snapshot.json +3952 -0
- package/migrations/meta/0056_snapshot.json +3952 -0
- package/migrations/meta/0057_snapshot.json +3839 -0
- package/migrations/meta/0058_snapshot.json +3839 -0
- package/migrations/meta/0059_snapshot.json +3839 -0
- package/migrations/meta/0060_snapshot.json +3830 -0
- package/migrations/meta/0061_snapshot.json +3821 -0
- package/migrations/meta/0062_snapshot.json +3723 -0
- package/migrations/meta/0063_snapshot.json +3723 -0
- package/migrations/meta/0064_snapshot.json +3723 -0
- package/migrations/meta/0065_snapshot.json +3729 -0
- package/migrations/meta/0066_snapshot.json +3729 -0
- package/migrations/meta/0067_snapshot.json +3736 -0
- package/migrations/meta/0068_snapshot.json +3347 -0
- package/migrations/meta/0069_snapshot.json +3340 -0
- package/migrations/meta/0070_snapshot.json +3444 -0
- package/migrations/meta/0072_snapshot.json +3450 -0
- package/migrations/meta/0073_snapshot.json +3455 -0
- package/migrations/meta/0074_snapshot.json +3461 -0
- package/migrations/meta/0075_snapshot.json +3567 -0
- package/migrations/meta/0076_snapshot.json +3851 -0
- package/migrations/meta/0077_snapshot.json +4067 -0
- package/migrations/meta/0078_snapshot.json +4078 -0
- package/migrations/meta/0079_snapshot.json +4078 -0
- package/migrations/meta/0080_snapshot.json +4078 -0
- package/migrations/meta/0081_snapshot.json +4096 -0
- package/migrations/meta/0082_snapshot.json +4102 -0
- package/migrations/meta/0083_snapshot.json +4096 -0
- package/migrations/meta/0084_snapshot.json +4096 -0
- package/migrations/meta/0085_snapshot.json +4111 -0
- package/migrations/meta/0086_snapshot.json +4117 -0
- package/migrations/meta/0087_snapshot.json +4100 -0
- package/migrations/meta/0088_snapshot.json +4286 -0
- package/migrations/meta/0089_snapshot.json +4376 -0
- package/migrations/meta/0090_snapshot.json +4387 -0
- package/migrations/meta/0091_snapshot.json +4397 -0
- package/migrations/meta/0092_snapshot.json +4397 -0
- package/migrations/meta/0093_snapshot.json +4407 -0
- package/migrations/meta/_journal.json +392 -0
- package/package.json +7 -3
- package/dist/schema/agent-assets.js +0 -15
- package/dist/schema/agents.js +0 -57
- package/dist/schema/instances.js +0 -39
- package/dist/schema/workflow-deployments.js +0 -37
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { and, eq, isNotNull, or, sql } from "drizzle-orm";
|
|
2
|
+
import { parseAddress } from "@intx/types";
|
|
3
|
+
import { getLogger } from "@intx/log";
|
|
4
|
+
import { principal } from "./schema/principals.js";
|
|
5
|
+
import { tenant } from "./schema/tenants.js";
|
|
6
|
+
import { workflowRun } from "./schema/workflow-run.js";
|
|
7
|
+
const RUN_PREFIX = "run_";
|
|
8
|
+
const logger = getLogger(["db", "sender-key-resolver"]);
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the durable public key a signed mail sender must verify against,
|
|
11
|
+
* unioning the two existing durable-key sources: a run address
|
|
12
|
+
* (`run_<id>@<domain>`) resolves to the run's `workflow_run.public_key`; any
|
|
13
|
+
* other address (`<refId>@<domain>`) is treated as a user sender and resolves
|
|
14
|
+
* to that principal's hub-custodied key.
|
|
15
|
+
*
|
|
16
|
+
* Read-only. Returns `null` when the sender has no durable key to resolve -- a
|
|
17
|
+
* malformed address, an unknown run, a run whose deploy has not been acked yet,
|
|
18
|
+
* or an address that matches no user principal. A `null` is a legitimate
|
|
19
|
+
* "unresolvable sender" answer the caller acts on; it is NOT an error.
|
|
20
|
+
*
|
|
21
|
+
* Addresses are matched case-insensitively. Inbound `From` addresses are
|
|
22
|
+
* lowercased when parsed (see `@intx/mime` `extractAddrSpec`), so the address is
|
|
23
|
+
* normalized here and stored values are compared under `lower(...)`. Tenant
|
|
24
|
+
* domains are `lower(domain)`-unique (`tenant_domain_lower_idx`), so a
|
|
25
|
+
* normalized domain matches at most one tenant. A user `refId` is a
|
|
26
|
+
* case-sensitively-unique betterAuth id that can carry uppercase; lowercasing it
|
|
27
|
+
* to reconcile with the parser is lossy, so the resolver prefers the exact
|
|
28
|
+
* (already-lowercase) row and falls back to a case-insensitive match to find a
|
|
29
|
+
* mixed-case-stored refId. When two principals in one tenant hold case-variant
|
|
30
|
+
* refIds the case-insensitive match is not unique and no exact row disambiguates
|
|
31
|
+
* it: the resolver throws rather than silently return one, which would attribute
|
|
32
|
+
* the sender to the wrong principal.
|
|
33
|
+
*/
|
|
34
|
+
export async function resolveSenderKey(db, principalKeyStore, address) {
|
|
35
|
+
const normalized = address.toLowerCase();
|
|
36
|
+
const parsed = parseAddress(normalized);
|
|
37
|
+
if (parsed === null)
|
|
38
|
+
return null;
|
|
39
|
+
const { localPart, domain } = parsed;
|
|
40
|
+
if (localPart.startsWith(RUN_PREFIX)) {
|
|
41
|
+
// A run sender resolves to the sidecar-minted key the hub recorded on the
|
|
42
|
+
// deployment anchor at `agent.deploy.ack`. `workflow_run.address` is unique
|
|
43
|
+
// among the runs that set it, so there is at most one row. A row whose
|
|
44
|
+
// `public_key` is still null is a deployed-but-not-yet-acked run -- the
|
|
45
|
+
// expected pre-ack state -- so it resolves to nothing rather than erroring,
|
|
46
|
+
// unlike the keyless-principal invariant break below.
|
|
47
|
+
const [row] = await db
|
|
48
|
+
.select({ publicKey: workflowRun.publicKey })
|
|
49
|
+
.from(workflowRun)
|
|
50
|
+
.where(eq(sql `lower(${workflowRun.address})`, normalized))
|
|
51
|
+
.limit(1);
|
|
52
|
+
if (row === undefined || row.publicKey === null)
|
|
53
|
+
return null;
|
|
54
|
+
return { source: "run", publicKey: row.publicKey };
|
|
55
|
+
}
|
|
56
|
+
// A user sender resolves to its hub-custodied principal key, keyed by the
|
|
57
|
+
// `(tenant domain, user refId)` its From address carries.
|
|
58
|
+
const principalId = await resolveUserPrincipalId(db, domain, localPart);
|
|
59
|
+
if (principalId === null)
|
|
60
|
+
return null;
|
|
61
|
+
// The principal exists; a principal with no active key violates INTR-164's
|
|
62
|
+
// invariant that every principal is minted with one, so `getPublicKey` throws
|
|
63
|
+
// rather than defaulting. Do not soften that to null -- unlike the pre-ack run
|
|
64
|
+
// key above, a keyless principal is a real breakage that must surface.
|
|
65
|
+
const publicKey = await principalKeyStore.getPublicKey(principalId, db);
|
|
66
|
+
return { source: "user", publicKey };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the hex-encoded public key to stamp on an outbound mail frame, as a
|
|
70
|
+
* BEST-EFFORT value that never blocks delivery. Returns the key, or `null` when
|
|
71
|
+
* the sender has no resolvable key -- OR when resolution FAILS.
|
|
72
|
+
*
|
|
73
|
+
* {@link resolveSenderKey} throws on a genuine fault (an ambiguous user address,
|
|
74
|
+
* or a principal with no active key -- an INTR-164 invariant break). Those
|
|
75
|
+
* throws must fail loud for {@link auditSenderKeys}, but they must not break
|
|
76
|
+
* the send path: the frame key is nullable, so a recipient with no co-delivered
|
|
77
|
+
* key resolves the sender as `unknown`, which its admission policy rejects by
|
|
78
|
+
* default (a workflow may relax `unknown` to admit).
|
|
79
|
+
* Coupling delivery to key resolution would let a data-integrity fault strand a
|
|
80
|
+
* run or drop mail. So a throw here degrades to `null` and is logged at ERROR
|
|
81
|
+
* with its cause -- a degraded fault, kept distinct from the ordinary
|
|
82
|
+
* unresolvable-sender `null`, which stays silent.
|
|
83
|
+
*
|
|
84
|
+
* The principal key store is real in production (INTR-164 mints every principal
|
|
85
|
+
* a key), so the throw path is a defensive safety net, not the common case: the
|
|
86
|
+
* normal outcome is a resolved, populated key.
|
|
87
|
+
*/
|
|
88
|
+
export async function resolveFrameSenderKey(db, principalKeyStore, address) {
|
|
89
|
+
try {
|
|
90
|
+
return ((await resolveSenderKey(db, principalKeyStore, address))?.publicKey ??
|
|
91
|
+
null);
|
|
92
|
+
}
|
|
93
|
+
catch (cause) {
|
|
94
|
+
logger.error `Degraded to a null frame sender key for ${address}: resolving its public key failed (a fault, not an unresolvable sender): ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve the user principal a normalized `<localPart>@<domain>` address names,
|
|
100
|
+
* or `null` when none matches. `lower(domain)` is unique, so the domain selects
|
|
101
|
+
* at most one tenant; within it, prefer the principal whose `refId` is exactly
|
|
102
|
+
* the normalized localPart (the canonical lowercase refId). The domain is
|
|
103
|
+
* matched case-insensitively in both queries because an existing tenant's stored
|
|
104
|
+
* domain may be mixed-case (creation lowercases new ones, but legacy rows are
|
|
105
|
+
* left as stored). Only when no exact refId exists does it fall back to a
|
|
106
|
+
* case-insensitive refId match, which finds a mixed-case-stored refId. A
|
|
107
|
+
* non-unique case-insensitive match with no exact row means two principals in
|
|
108
|
+
* the tenant hold case-variant refIds; that is genuinely ambiguous, so it throws
|
|
109
|
+
* rather than return an arbitrary one and attribute the sender to the wrong
|
|
110
|
+
* principal.
|
|
111
|
+
*/
|
|
112
|
+
async function resolveUserPrincipalId(db, domain, localPart) {
|
|
113
|
+
const [exact] = await db
|
|
114
|
+
.select({ principalId: principal.id })
|
|
115
|
+
.from(principal)
|
|
116
|
+
.innerJoin(tenant, eq(principal.tenantId, tenant.id))
|
|
117
|
+
.where(and(eq(sql `lower(${tenant.domain})`, domain), eq(principal.kind, "user"), eq(principal.refId, localPart)))
|
|
118
|
+
.limit(1);
|
|
119
|
+
if (exact !== undefined)
|
|
120
|
+
return exact.principalId;
|
|
121
|
+
const caseInsensitive = await db
|
|
122
|
+
.select({ principalId: principal.id })
|
|
123
|
+
.from(principal)
|
|
124
|
+
.innerJoin(tenant, eq(principal.tenantId, tenant.id))
|
|
125
|
+
.where(and(eq(sql `lower(${tenant.domain})`, domain), eq(principal.kind, "user"), eq(sql `lower(${principal.refId})`, localPart)))
|
|
126
|
+
.limit(2);
|
|
127
|
+
if (caseInsensitive.length === 0)
|
|
128
|
+
return null;
|
|
129
|
+
if (caseInsensitive.length > 1) {
|
|
130
|
+
throw new Error(`resolveSenderKey: user sender ${localPart}@${domain} is ambiguous; it ` +
|
|
131
|
+
`matches multiple principals with case-variant refIds and no canonical ` +
|
|
132
|
+
`row -- the colliding principal refIds must be reconciled`);
|
|
133
|
+
}
|
|
134
|
+
const [only] = caseInsensitive;
|
|
135
|
+
return only === undefined ? null : only.principalId;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Sweep every mail sender that could sign and confirm each resolves to a durable
|
|
139
|
+
* public key via {@link resolveSenderKey}. Read-only.
|
|
140
|
+
*
|
|
141
|
+
* The senders that can sign are every workflow run that holds a signing key or
|
|
142
|
+
* is live ("running") and carries a sending address, plus every active user
|
|
143
|
+
* principal. A never-signed run -- deployed-but-not-yet-acked, or failed or
|
|
144
|
+
* cancelled before ack (address set, key null) -- is excluded. Any checked
|
|
145
|
+
* sender that does not resolve is collected in `unresolved`. A keyless user
|
|
146
|
+
* principal is an INTR-164 invariant break, so `resolveSenderKey` throws and the
|
|
147
|
+
* sweep fails loudly rather than folding it into `unresolved` -- it is a
|
|
148
|
+
* different, more serious fault than an unresolvable address.
|
|
149
|
+
*/
|
|
150
|
+
export async function auditSenderKeys(db, principalKeyStore) {
|
|
151
|
+
const unresolved = [];
|
|
152
|
+
const runs = await db
|
|
153
|
+
.select({ address: workflowRun.address })
|
|
154
|
+
.from(workflowRun)
|
|
155
|
+
.where(and(isNotNull(workflowRun.address),
|
|
156
|
+
// A run can only have signed if it holds a key, so every key-null row is
|
|
157
|
+
// a non-signer EXCEPT a live "running" run, which is the genuine
|
|
158
|
+
// can-sign-but-keyless hole to flag. This includes all key-bearing rows
|
|
159
|
+
// (any status -- a terminal run's in-flight mail is still verifiable)
|
|
160
|
+
// and excludes both the pre-ack "deployed" window and a run that failed
|
|
161
|
+
// or was cancelled before ack (address set, key null, never signed).
|
|
162
|
+
// Match "running" literally, NOT isLiveWorkflowRunStatus, which also
|
|
163
|
+
// admits "deployed" and would re-open the pre-ack false positive.
|
|
164
|
+
or(isNotNull(workflowRun.publicKey), eq(workflowRun.status, "running"))));
|
|
165
|
+
for (const run of runs) {
|
|
166
|
+
if (run.address === null)
|
|
167
|
+
continue;
|
|
168
|
+
if ((await resolveSenderKey(db, principalKeyStore, run.address)) === null) {
|
|
169
|
+
unresolved.push({ address: run.address, kind: "run" });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const users = await db
|
|
173
|
+
.select({ refId: principal.refId, domain: tenant.domain })
|
|
174
|
+
.from(principal)
|
|
175
|
+
.innerJoin(tenant, eq(principal.tenantId, tenant.id))
|
|
176
|
+
.where(and(eq(principal.kind, "user"), eq(principal.status, "active")));
|
|
177
|
+
for (const user of users) {
|
|
178
|
+
const address = `${user.refId}@${user.domain}`;
|
|
179
|
+
if ((await resolveSenderKey(db, principalKeyStore, address)) === null) {
|
|
180
|
+
unresolved.push({ address, kind: "user" });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { runsChecked: runs.length, usersChecked: users.length, unresolved };
|
|
184
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { type SidecarAllocationStatus } from "@intx/types";
|
|
2
|
+
import type { DB, DBExecutor } from "./client.js";
|
|
3
|
+
import { type WorkflowRunCredentialRefs } from "./schema/index.js";
|
|
4
|
+
type DBHandle = DB["db"];
|
|
5
|
+
declare const activeStatuses: readonly ["pending", "provisioning", "allocated", "replacing", "releasing"];
|
|
6
|
+
export type SidecarAllocation = {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly anchorRunId: string;
|
|
9
|
+
readonly tenantId: string;
|
|
10
|
+
readonly provisionerId: string;
|
|
11
|
+
readonly provisionerApiVersion: 1;
|
|
12
|
+
readonly provisionerBindingFingerprint: string;
|
|
13
|
+
readonly sidecarId?: string;
|
|
14
|
+
readonly status: SidecarAllocationStatus;
|
|
15
|
+
readonly generation: number;
|
|
16
|
+
readonly ensureAcceptedGeneration?: number;
|
|
17
|
+
readonly externalRef?: string;
|
|
18
|
+
readonly nextAttemptAt?: Date;
|
|
19
|
+
readonly reconciliationLeaseId?: string;
|
|
20
|
+
readonly reconciliationLeaseExpiresAt?: Date;
|
|
21
|
+
/** Outstanding deploy attempt, retained after its reconciliation lease ends. */
|
|
22
|
+
readonly initializationLeaseId?: string;
|
|
23
|
+
readonly ensureAttempts: number;
|
|
24
|
+
readonly destroyAttempts: number;
|
|
25
|
+
readonly connectDeadline?: Date;
|
|
26
|
+
readonly failureCode?: string;
|
|
27
|
+
readonly failureMessage?: string;
|
|
28
|
+
readonly createdAt: Date;
|
|
29
|
+
readonly updatedAt: Date;
|
|
30
|
+
};
|
|
31
|
+
export type CreatePendingSidecarAllocationArgs = {
|
|
32
|
+
readonly id: string;
|
|
33
|
+
readonly anchorRunId: string;
|
|
34
|
+
readonly tenantId: string;
|
|
35
|
+
readonly provisionerId: string;
|
|
36
|
+
readonly provisionerApiVersion: 1;
|
|
37
|
+
readonly provisionerBindingFingerprint: string;
|
|
38
|
+
readonly now?: Date;
|
|
39
|
+
};
|
|
40
|
+
export type CreateAdoptedSidecarAllocationArgs = CreatePendingSidecarAllocationArgs & {
|
|
41
|
+
readonly sidecarId: string;
|
|
42
|
+
readonly generation: number;
|
|
43
|
+
readonly externalRef?: string;
|
|
44
|
+
readonly connectDeadline: Date;
|
|
45
|
+
};
|
|
46
|
+
export type ClaimSidecarAllocationArgs = {
|
|
47
|
+
readonly excludedAllocationIds?: readonly string[];
|
|
48
|
+
readonly leaseId: string;
|
|
49
|
+
readonly leaseDurationMs: number;
|
|
50
|
+
};
|
|
51
|
+
export type ParkSidecarReconciliationPolicy = {
|
|
52
|
+
readonly kind: "await-connection";
|
|
53
|
+
readonly fallbackNextAttemptAt: Date;
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: "retry-after-error";
|
|
56
|
+
readonly notBefore: Date;
|
|
57
|
+
};
|
|
58
|
+
export type BindInitialSidecarArgs = {
|
|
59
|
+
readonly allocationId: string;
|
|
60
|
+
readonly expectedGeneration: number;
|
|
61
|
+
readonly sidecarId: string;
|
|
62
|
+
readonly tokenHashSha256: Uint8Array;
|
|
63
|
+
readonly connectDeadline: Date;
|
|
64
|
+
readonly expectedLeaseId?: string;
|
|
65
|
+
readonly now?: Date;
|
|
66
|
+
};
|
|
67
|
+
export type BindReplacementSidecarArgs = {
|
|
68
|
+
readonly allocationId: string;
|
|
69
|
+
readonly generation: number;
|
|
70
|
+
readonly sidecarId: string;
|
|
71
|
+
readonly tokenHashSha256: Uint8Array;
|
|
72
|
+
readonly connectDeadline: Date;
|
|
73
|
+
readonly expectedLeaseId?: string;
|
|
74
|
+
readonly now?: Date;
|
|
75
|
+
};
|
|
76
|
+
export type MarkSidecarAllocatedArgs = {
|
|
77
|
+
readonly allocationId: string;
|
|
78
|
+
readonly generation: number;
|
|
79
|
+
readonly externalRef?: string;
|
|
80
|
+
readonly expectedLeaseId?: string;
|
|
81
|
+
readonly now?: Date;
|
|
82
|
+
};
|
|
83
|
+
export type ScheduleSidecarAllocationRetryArgs = {
|
|
84
|
+
readonly allocationId: string;
|
|
85
|
+
readonly expectedStatus: (typeof activeStatuses)[number];
|
|
86
|
+
readonly expectedGeneration: number;
|
|
87
|
+
readonly nextAttemptAt: Date;
|
|
88
|
+
readonly expectedLeaseId?: string;
|
|
89
|
+
readonly attempt?: "ensure" | "destroy";
|
|
90
|
+
readonly failure?: {
|
|
91
|
+
readonly code: string;
|
|
92
|
+
readonly message: string;
|
|
93
|
+
};
|
|
94
|
+
readonly now?: Date;
|
|
95
|
+
};
|
|
96
|
+
export type BeginSidecarReplacementArgs = {
|
|
97
|
+
readonly allocationId: string;
|
|
98
|
+
readonly expectedStatus: "provisioning" | "allocated";
|
|
99
|
+
readonly expectedGeneration: number;
|
|
100
|
+
readonly expectedLeaseId: string;
|
|
101
|
+
readonly onlyIfInitializationIncomplete?: boolean;
|
|
102
|
+
readonly expectedInitializationLeaseId?: string;
|
|
103
|
+
readonly nextAttemptAt: Date;
|
|
104
|
+
readonly failureCode: string;
|
|
105
|
+
readonly failureMessage: string;
|
|
106
|
+
readonly now?: Date;
|
|
107
|
+
};
|
|
108
|
+
export type BeginSidecarReleaseArgs = {
|
|
109
|
+
readonly allocationId: string;
|
|
110
|
+
readonly expectedStatus: Exclude<(typeof activeStatuses)[number], "releasing">;
|
|
111
|
+
readonly expectedGeneration: number;
|
|
112
|
+
readonly failureCode?: string;
|
|
113
|
+
readonly failureMessage?: string;
|
|
114
|
+
readonly expectedLeaseId?: string;
|
|
115
|
+
readonly expectedInitializationLeaseId?: string;
|
|
116
|
+
readonly now?: Date;
|
|
117
|
+
};
|
|
118
|
+
export type BeginUnrecoverableSidecarReleaseArgs = {
|
|
119
|
+
readonly allocationId: string;
|
|
120
|
+
readonly expectedGeneration: number;
|
|
121
|
+
readonly expectedLeaseId: string;
|
|
122
|
+
readonly onlyIfInitializationIncomplete?: boolean;
|
|
123
|
+
readonly expectedInitializationLeaseId?: string;
|
|
124
|
+
readonly failureCode: string;
|
|
125
|
+
readonly failureMessage: string;
|
|
126
|
+
readonly now?: Date;
|
|
127
|
+
};
|
|
128
|
+
export type MarkSidecarReleasedArgs = {
|
|
129
|
+
readonly allocationId: string;
|
|
130
|
+
readonly generation: number;
|
|
131
|
+
readonly expectedLeaseId?: string;
|
|
132
|
+
readonly now?: Date;
|
|
133
|
+
};
|
|
134
|
+
export type MarkSidecarConnectionReadyArgs = {
|
|
135
|
+
readonly allocationId: string;
|
|
136
|
+
readonly generation: number;
|
|
137
|
+
readonly expectedLeaseId?: string;
|
|
138
|
+
readonly now?: Date;
|
|
139
|
+
};
|
|
140
|
+
export type MarkSidecarConnectionLostArgs = {
|
|
141
|
+
readonly allocationId: string;
|
|
142
|
+
readonly generation: number;
|
|
143
|
+
readonly connectDeadline: Date;
|
|
144
|
+
readonly now?: Date;
|
|
145
|
+
};
|
|
146
|
+
export type ScheduleSidecarReconnectIfUnscheduledArgs = {
|
|
147
|
+
readonly allocationId: string;
|
|
148
|
+
readonly generation: number;
|
|
149
|
+
readonly connectDeadline: Date;
|
|
150
|
+
readonly now?: Date;
|
|
151
|
+
};
|
|
152
|
+
export type FailSidecarAllocationArgs = {
|
|
153
|
+
readonly allocationId: string;
|
|
154
|
+
readonly expectedStatus: "pending" | "provisioning";
|
|
155
|
+
readonly expectedGeneration: number;
|
|
156
|
+
readonly code: string;
|
|
157
|
+
readonly message: string;
|
|
158
|
+
readonly expectedLeaseId?: string;
|
|
159
|
+
readonly now?: Date;
|
|
160
|
+
};
|
|
161
|
+
export type MarkSidecarDestroyFailedArgs = Omit<FailSidecarAllocationArgs, "expectedStatus">;
|
|
162
|
+
type InitializationArgs = {
|
|
163
|
+
readonly allocationId: string;
|
|
164
|
+
readonly generation: number;
|
|
165
|
+
readonly anchorRunId: string;
|
|
166
|
+
readonly tenantId: string;
|
|
167
|
+
readonly leaseId: string;
|
|
168
|
+
readonly signal: AbortSignal;
|
|
169
|
+
};
|
|
170
|
+
export declare function createSidecarAllocationStore(db: DBHandle): {
|
|
171
|
+
beginInitialization(args: InitializationArgs): Promise<{
|
|
172
|
+
previousPublicKey: string | null;
|
|
173
|
+
} | null>;
|
|
174
|
+
completeInitialization(args: InitializationArgs & {
|
|
175
|
+
readonly publicKey: string;
|
|
176
|
+
readonly credentialRefs?: WorkflowRunCredentialRefs;
|
|
177
|
+
}): Promise<boolean>;
|
|
178
|
+
clearUnsentInitialization(args: InitializationArgs & {
|
|
179
|
+
readonly previousPublicKey: string | null;
|
|
180
|
+
}): Promise<boolean>;
|
|
181
|
+
createPending(args: CreatePendingSidecarAllocationArgs, tx?: DBExecutor): Promise<SidecarAllocation>;
|
|
182
|
+
createAdopted(args: CreateAdoptedSidecarAllocationArgs, tx?: DBExecutor): Promise<SidecarAllocation>;
|
|
183
|
+
bindInitialSidecar(args: BindInitialSidecarArgs): Promise<SidecarAllocation | null>;
|
|
184
|
+
bindReplacementSidecar(args: BindReplacementSidecarArgs): Promise<SidecarAllocation | null>;
|
|
185
|
+
markAllocated(args: MarkSidecarAllocatedArgs): Promise<SidecarAllocation | null>;
|
|
186
|
+
scheduleRetry(args: ScheduleSidecarAllocationRetryArgs): Promise<SidecarAllocation | null>;
|
|
187
|
+
beginReplacement(args: BeginSidecarReplacementArgs): Promise<SidecarAllocation | null>;
|
|
188
|
+
beginRelease: (args: BeginSidecarReleaseArgs, tx?: DBExecutor) => Promise<SidecarAllocation | null>;
|
|
189
|
+
beginUnrecoverableRelease(args: BeginUnrecoverableSidecarReleaseArgs): Promise<SidecarAllocation | null>;
|
|
190
|
+
markReleased(args: MarkSidecarReleasedArgs): Promise<SidecarAllocation | null>;
|
|
191
|
+
markDestroyFailed(args: MarkSidecarDestroyFailedArgs): Promise<SidecarAllocation | null>;
|
|
192
|
+
failWithoutInfrastructure(args: FailSidecarAllocationArgs, tx?: DBExecutor): Promise<SidecarAllocation | null>;
|
|
193
|
+
findById(id: string): Promise<SidecarAllocation | null>;
|
|
194
|
+
findByAnchorRunId(anchorRunId: string): Promise<SidecarAllocation | null>;
|
|
195
|
+
claimNextReconcilable(args: ClaimSidecarAllocationArgs): Promise<SidecarAllocation | null>;
|
|
196
|
+
extendReconciliationLease(allocationId: string, leaseId: string, leaseDurationMs: number): Promise<boolean>;
|
|
197
|
+
isReconciliationLeaseCurrent(allocationId: string, generation: number, leaseId: string): Promise<boolean>;
|
|
198
|
+
markConnectionReady(args: MarkSidecarConnectionReadyArgs): Promise<SidecarAllocation | null>;
|
|
199
|
+
markConnectionLost(args: MarkSidecarConnectionLostArgs): Promise<SidecarAllocation | null>;
|
|
200
|
+
scheduleReconnectIfUnscheduled(args: ScheduleSidecarReconnectIfUnscheduledArgs): Promise<SidecarAllocation | null>;
|
|
201
|
+
parkReconciliation(allocationId: string, leaseId: string, policy: ParkSidecarReconciliationPolicy): Promise<boolean>;
|
|
202
|
+
wakeReconciliation(allocationId: string, generation: number): Promise<boolean>;
|
|
203
|
+
listActive(): Promise<SidecarAllocation[]>;
|
|
204
|
+
};
|
|
205
|
+
export type SidecarAllocationStore = ReturnType<typeof createSidecarAllocationStore>;
|
|
206
|
+
export {};
|