@runuai/host 0.9.148 → 0.9.150
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/db/migrations/0024_task_env_mode.sql +9 -0
- package/db/migrations/0025_project_env_authority.sql +147 -0
- package/db/migrations/0026_project_env_replica.sql +150 -0
- package/db/migrations/meta/_journal.json +21 -0
- package/db/schema.ts +312 -2
- package/images/standard/Dockerfile +44 -6
- package/images/standard/container/uai-init +85 -2
- package/images/standard/container/uai-task-forwarder.mjs +303 -0
- package/lib/agent-cli.ts +18 -1
- package/lib/agent.ts +99 -34
- package/lib/agents/codex.ts +9 -1
- package/lib/agents/cursor.ts +15 -1
- package/lib/agents/grok.ts +8 -0
- package/lib/agents/kimi.ts +1 -0
- package/lib/agents/machine-durable.ts +2 -0
- package/lib/agents/opencode.ts +1 -0
- package/lib/agents/proc.ts +6 -1
- package/lib/agents/transport.ts +9 -5
- package/lib/browser-testing.ts +6 -0
- package/lib/codex-auth.ts +2 -0
- package/lib/credential-store.ts +141 -6
- package/lib/engine-accounts.ts +1 -0
- package/lib/env-file.ts +7 -0
- package/lib/env-schema.ts +841 -51
- package/lib/git-diff.ts +1 -0
- package/lib/git-identity.ts +2 -0
- package/lib/github-tokens.ts +1 -0
- package/lib/host-env.ts +98 -18
- package/lib/lease.ts +11 -3
- package/lib/machine-gateway-tunnel.ts +195 -17
- package/lib/machine-project-env-loader.ts +152 -0
- package/lib/managed-runtime.ts +270 -51
- package/lib/mcp-gateway.ts +777 -26
- package/lib/orchestrator.ts +408 -81
- package/lib/project-env-authority.ts +1319 -0
- package/lib/project-env-feature.ts +74 -0
- package/lib/project-env-loader.ts +237 -0
- package/lib/project-env-replica.ts +2094 -0
- package/lib/project-env-schema-capture.ts +342 -0
- package/lib/project-env-staging.ts +148 -0
- package/lib/project-env-task-policy.ts +176 -0
- package/lib/runtime-archive.ts +79 -16
- package/lib/runtime-authority.ts +44 -0
- package/lib/runtime-state.ts +175 -10
- package/lib/secrets-proxy/address.ts +72 -0
- package/lib/secrets-proxy/audit.ts +30 -0
- package/lib/secrets-proxy/ca-store.ts +200 -0
- package/lib/secrets-proxy/ca.ts +470 -0
- package/lib/secrets-proxy/container-steps.ts +138 -0
- package/lib/secrets-proxy/der.ts +273 -0
- package/lib/secrets-proxy/engine.ts +1633 -0
- package/lib/secrets-proxy/host.ts +93 -0
- package/lib/secrets-proxy/hostname.ts +60 -0
- package/lib/secrets-proxy/placeholders.ts +196 -0
- package/lib/secrets-proxy/policy.ts +141 -0
- package/lib/secrets-proxy/readiness.ts +148 -0
- package/lib/secrets-proxy/runtime.ts +582 -0
- package/lib/secrets-proxy/scrub.ts +559 -0
- package/lib/secrets-proxy/sidecar-view.ts +139 -0
- package/lib/secrets-proxy/task-scripts.ts +262 -0
- package/lib/secrets-proxy/task-trust.ts +574 -0
- package/lib/secrets-proxy/trust-watch.ts +80 -0
- package/lib/shared-files-s3.ts +3 -0
- package/lib/sidecar-guard.ts +58 -0
- package/lib/sidecar-resume.ts +51 -3
- package/lib/standard-image.ts +50 -12
- package/lib/task-env-forwarder.ts +228 -0
- package/lib/task-env-source.ts +193 -0
- package/lib/task-environment/apple-container.ts +45 -11
- package/lib/task-environment/docker.ts +51 -12
- package/lib/task-environment/index.ts +6 -2
- package/lib/task-environment/machine-task-up.ts +258 -25
- package/lib/task-environment/machine.ts +80 -22
- package/lib/task-environment/registry.ts +3 -2
- package/lib/task-environment/self-identity.ts +20 -0
- package/lib/task-environment/self.ts +92 -16
- package/lib/task-environment/types.ts +63 -0
- package/lib/task-gateway-port.ts +22 -0
- package/lib/task-gateway-protocol.ts +190 -0
- package/lib/task-gateway-recovery.ts +44 -0
- package/lib/task-gateway-state.ts +497 -0
- package/lib/transcript.ts +1 -0
- package/package.json +13 -1
- package/scripts/agent/env-hook.sh +626 -0
- package/scripts/agent/task-up.sh +476 -27
- package/scripts/agent/uai-env-exec.sh +26 -0
- package/src/cli.ts +26 -3
- package/src/index.ts +273 -19
- package/src/main.ts +939 -48
- package/src/project-env-latest.ts +130 -0
- package/src/project-env-policy.ts +281 -0
- package/src/project-env-wording.ts +47 -0
- package/src/protocol.ts +2219 -15
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- ADR-153: durable rollout fence for per-command project env delivery.
|
|
2
|
+
-- Existing/null rows are interpreted as `baked`; new task-up writes `loader`
|
|
3
|
+
-- before creating runtime resources, so recovery never launches a binary that
|
|
4
|
+
-- is absent from an older container image.
|
|
5
|
+
ALTER TABLE `uai_host_tasks` ADD `env_mode` text;
|
|
6
|
+
--> statement-breakpoint
|
|
7
|
+
-- Value-free declaration/literal metadata needed to rebuild the resolver
|
|
8
|
+
-- after runner restart, before the cloud re-sends task-up.
|
|
9
|
+
ALTER TABLE `uai_host_tasks` ADD `project_env_spec` text;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
-- ADR-153: the owned runner's durable, value-free project-env authority.
|
|
2
|
+
--
|
|
3
|
+
-- This migration deliberately does not backfill host_project_env. An older
|
|
4
|
+
-- runtime may keep writing that table after the migration lands but before
|
|
5
|
+
-- the first bindings-v1 reconciliation. After its caller takes the capability
|
|
6
|
+
-- floor, the first begin transaction snapshots the then-current rows and
|
|
7
|
+
-- activates the permanent legacy-write journal below.
|
|
8
|
+
CREATE TABLE `host_project_env_records` (
|
|
9
|
+
`project_id` text NOT NULL,
|
|
10
|
+
`key` text NOT NULL,
|
|
11
|
+
`revision` integer NOT NULL,
|
|
12
|
+
`generation` integer NOT NULL,
|
|
13
|
+
`tombstone` integer NOT NULL,
|
|
14
|
+
`binding_json` text,
|
|
15
|
+
`policy_epoch` integer,
|
|
16
|
+
`listing_generation` integer NOT NULL,
|
|
17
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
18
|
+
PRIMARY KEY(`project_id`, `key`),
|
|
19
|
+
CONSTRAINT `host_project_env_records_head` CHECK (
|
|
20
|
+
(`tombstone` = 0 AND `revision` >= 1 AND `generation` >= 1) OR
|
|
21
|
+
(`tombstone` = 1 AND `revision` = 1 AND `generation` = 0) OR
|
|
22
|
+
(`tombstone` = 1 AND `revision` >= 2 AND `generation` >= 2)
|
|
23
|
+
),
|
|
24
|
+
CONSTRAINT `host_project_env_records_binding` CHECK (
|
|
25
|
+
(`generation` = 0 AND `binding_json` IS NULL AND `policy_epoch` IS NULL
|
|
26
|
+
AND `listing_generation` >= 2 AND (`listing_generation` % 2) = 0) OR
|
|
27
|
+
(`generation` > 0 AND `policy_epoch` >= 1)
|
|
28
|
+
),
|
|
29
|
+
CONSTRAINT `host_project_env_records_listing` CHECK (`listing_generation` >= 0),
|
|
30
|
+
CONSTRAINT `host_project_env_records_boolean` CHECK (`tombstone` IN (0, 1))
|
|
31
|
+
);
|
|
32
|
+
--> statement-breakpoint
|
|
33
|
+
CREATE TABLE `host_project_env_projects` (
|
|
34
|
+
`project_id` text PRIMARY KEY NOT NULL,
|
|
35
|
+
`record_version` integer NOT NULL DEFAULT 0,
|
|
36
|
+
CONSTRAINT `host_project_env_projects_version` CHECK (`record_version` >= 0)
|
|
37
|
+
);
|
|
38
|
+
--> statement-breakpoint
|
|
39
|
+
CREATE TABLE `host_project_env_authority` (
|
|
40
|
+
`id` text PRIMARY KEY NOT NULL DEFAULT 'host',
|
|
41
|
+
`activated` integer NOT NULL DEFAULT 0,
|
|
42
|
+
`gate_open` integer NOT NULL DEFAULT 1,
|
|
43
|
+
`stream_id` text,
|
|
44
|
+
`applied_sequence` integer NOT NULL DEFAULT 0,
|
|
45
|
+
`attempt_id` text,
|
|
46
|
+
`attempt_legacy_version` integer,
|
|
47
|
+
`legacy_version` integer NOT NULL DEFAULT 0,
|
|
48
|
+
`policy_epochs_json` text NOT NULL DEFAULT '[1]',
|
|
49
|
+
`modern_write` integer NOT NULL DEFAULT 0,
|
|
50
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
51
|
+
CONSTRAINT `host_project_env_authority_singleton` CHECK (`id` = 'host'),
|
|
52
|
+
CONSTRAINT `host_project_env_authority_booleans` CHECK (
|
|
53
|
+
`activated` IN (0, 1) AND `gate_open` IN (0, 1) AND `modern_write` IN (0, 1)
|
|
54
|
+
),
|
|
55
|
+
CONSTRAINT `host_project_env_authority_cursor` CHECK (
|
|
56
|
+
`applied_sequence` >= 0 AND `legacy_version` >= 0 AND
|
|
57
|
+
(`attempt_legacy_version` IS NULL OR `attempt_legacy_version` >= 0)
|
|
58
|
+
),
|
|
59
|
+
CONSTRAINT `host_project_env_authority_stream` CHECK (
|
|
60
|
+
(`activated` = 0 AND `stream_id` IS NULL AND `applied_sequence` = 0 AND `attempt_id` IS NULL) OR
|
|
61
|
+
(`activated` = 1 AND `stream_id` IS NOT NULL)
|
|
62
|
+
),
|
|
63
|
+
CONSTRAINT `host_project_env_authority_attempt` CHECK (
|
|
64
|
+
(`attempt_id` IS NULL AND `attempt_legacy_version` IS NULL) OR
|
|
65
|
+
(`attempt_id` IS NOT NULL AND `attempt_legacy_version` IS NOT NULL AND `gate_open` = 0)
|
|
66
|
+
),
|
|
67
|
+
CONSTRAINT `host_project_env_authority_open` CHECK (`gate_open` = 0 OR `attempt_id` IS NULL)
|
|
68
|
+
);
|
|
69
|
+
--> statement-breakpoint
|
|
70
|
+
INSERT INTO `host_project_env_authority` (`id`) VALUES ('host');
|
|
71
|
+
--> statement-breakpoint
|
|
72
|
+
CREATE TABLE `host_project_env_delivery_receipts` (
|
|
73
|
+
`stream_id` text NOT NULL,
|
|
74
|
+
`sequence` integer NOT NULL,
|
|
75
|
+
`request_kind` text NOT NULL,
|
|
76
|
+
`request_mac` text NOT NULL,
|
|
77
|
+
`ok` integer NOT NULL,
|
|
78
|
+
`state_json` text,
|
|
79
|
+
`record_version` integer,
|
|
80
|
+
`code` text,
|
|
81
|
+
`created_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
82
|
+
PRIMARY KEY(`stream_id`, `sequence`),
|
|
83
|
+
CONSTRAINT `host_project_env_delivery_receipts_sequence` CHECK (`sequence` >= 1),
|
|
84
|
+
CONSTRAINT `host_project_env_delivery_receipts_boolean` CHECK (`ok` IN (0, 1)),
|
|
85
|
+
CONSTRAINT `host_project_env_delivery_receipts_outcome` CHECK (
|
|
86
|
+
(`ok` = 1 AND `code` IS NULL) OR (`ok` = 0 AND `code` IS NOT NULL)
|
|
87
|
+
),
|
|
88
|
+
CONSTRAINT `host_project_env_delivery_receipts_version` CHECK (
|
|
89
|
+
`record_version` IS NULL OR `record_version` >= 0
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
--> statement-breakpoint
|
|
93
|
+
CREATE TABLE `host_project_env_legacy_journal` (
|
|
94
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
95
|
+
`project_id` text NOT NULL,
|
|
96
|
+
`key` text NOT NULL,
|
|
97
|
+
`mutation` text NOT NULL,
|
|
98
|
+
`created_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
99
|
+
CONSTRAINT `host_project_env_legacy_journal_mutation` CHECK (`mutation` IN ('set', 'delete'))
|
|
100
|
+
);
|
|
101
|
+
--> statement-breakpoint
|
|
102
|
+
CREATE INDEX `host_project_env_legacy_journal_key_idx`
|
|
103
|
+
ON `host_project_env_legacy_journal` (`project_id`, `key`);
|
|
104
|
+
--> statement-breakpoint
|
|
105
|
+
-- These triggers are inert until the first modern reconciliation activates
|
|
106
|
+
-- the authority. Afterwards, any old process that changes plaintext storage
|
|
107
|
+
-- leaves durable evidence and closes the serving gate. Metadata-only updates
|
|
108
|
+
-- do neither. Modern writes raise modern_write in their enclosing transaction.
|
|
109
|
+
CREATE TRIGGER `host_project_env_legacy_insert`
|
|
110
|
+
AFTER INSERT ON `host_project_env`
|
|
111
|
+
WHEN (SELECT `activated` = 1 AND `modern_write` = 0
|
|
112
|
+
FROM `host_project_env_authority` WHERE `id` = 'host')
|
|
113
|
+
BEGIN
|
|
114
|
+
INSERT INTO `host_project_env_legacy_journal` (`project_id`, `key`, `mutation`)
|
|
115
|
+
VALUES (NEW.`project_id`, NEW.`key`, 'set');
|
|
116
|
+
UPDATE `host_project_env_authority`
|
|
117
|
+
SET `gate_open` = 0, `legacy_version` = `legacy_version` + 1,
|
|
118
|
+
`updated_at` = unixepoch() * 1000
|
|
119
|
+
WHERE `id` = 'host';
|
|
120
|
+
END;
|
|
121
|
+
--> statement-breakpoint
|
|
122
|
+
CREATE TRIGGER `host_project_env_legacy_update`
|
|
123
|
+
AFTER UPDATE OF `value_enc` ON `host_project_env`
|
|
124
|
+
WHEN OLD.`value_enc` IS NOT NEW.`value_enc`
|
|
125
|
+
AND (SELECT `activated` = 1 AND `modern_write` = 0
|
|
126
|
+
FROM `host_project_env_authority` WHERE `id` = 'host')
|
|
127
|
+
BEGIN
|
|
128
|
+
INSERT INTO `host_project_env_legacy_journal` (`project_id`, `key`, `mutation`)
|
|
129
|
+
VALUES (NEW.`project_id`, NEW.`key`, 'set');
|
|
130
|
+
UPDATE `host_project_env_authority`
|
|
131
|
+
SET `gate_open` = 0, `legacy_version` = `legacy_version` + 1,
|
|
132
|
+
`updated_at` = unixepoch() * 1000
|
|
133
|
+
WHERE `id` = 'host';
|
|
134
|
+
END;
|
|
135
|
+
--> statement-breakpoint
|
|
136
|
+
CREATE TRIGGER `host_project_env_legacy_delete`
|
|
137
|
+
AFTER DELETE ON `host_project_env`
|
|
138
|
+
WHEN (SELECT `activated` = 1 AND `modern_write` = 0
|
|
139
|
+
FROM `host_project_env_authority` WHERE `id` = 'host')
|
|
140
|
+
BEGIN
|
|
141
|
+
INSERT INTO `host_project_env_legacy_journal` (`project_id`, `key`, `mutation`)
|
|
142
|
+
VALUES (OLD.`project_id`, OLD.`key`, 'delete');
|
|
143
|
+
UPDATE `host_project_env_authority`
|
|
144
|
+
SET `gate_open` = 0, `legacy_version` = `legacy_version` + 1,
|
|
145
|
+
`updated_at` = unixepoch() * 1000
|
|
146
|
+
WHERE `id` = 'host';
|
|
147
|
+
END;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
-- ADR-153: durable Uai Cloud task-machine project-env replica.
|
|
2
|
+
--
|
|
3
|
+
-- The live and staged views are separate from both one another and the owned
|
|
4
|
+
-- runner authority. A page can therefore materialise values without making
|
|
5
|
+
-- them visible, and commit can replace the complete task view atomically.
|
|
6
|
+
CREATE TABLE `host_project_env_replica_authority` (
|
|
7
|
+
`id` text PRIMARY KEY NOT NULL DEFAULT 'replica',
|
|
8
|
+
`activated` integer NOT NULL DEFAULT 0,
|
|
9
|
+
`gate_open` integer NOT NULL DEFAULT 0,
|
|
10
|
+
`stream_id` text,
|
|
11
|
+
`applied_sequence` integer NOT NULL DEFAULT 0,
|
|
12
|
+
`attempt_id` text,
|
|
13
|
+
`org_id` text,
|
|
14
|
+
`policy_epochs_json` text NOT NULL DEFAULT '[1]',
|
|
15
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
16
|
+
CONSTRAINT `host_project_env_replica_authority_singleton` CHECK (`id` = 'replica'),
|
|
17
|
+
CONSTRAINT `host_project_env_replica_authority_booleans` CHECK (`activated` IN (0, 1) AND `gate_open` IN (0, 1)),
|
|
18
|
+
CONSTRAINT `host_project_env_replica_authority_cursor` CHECK (`applied_sequence` >= 0),
|
|
19
|
+
CONSTRAINT `host_project_env_replica_authority_stream` CHECK (
|
|
20
|
+
(`activated` = 0 AND `stream_id` IS NULL AND `applied_sequence` = 0 AND `attempt_id` IS NULL AND `org_id` IS NULL AND `gate_open` = 0) OR
|
|
21
|
+
(`activated` = 1 AND `stream_id` IS NOT NULL AND `org_id` IS NOT NULL)
|
|
22
|
+
),
|
|
23
|
+
CONSTRAINT `host_project_env_replica_authority_gate` CHECK (`gate_open` = 0 OR `attempt_id` IS NULL)
|
|
24
|
+
);
|
|
25
|
+
--> statement-breakpoint
|
|
26
|
+
INSERT INTO `host_project_env_replica_authority` (`id`) VALUES ('replica');
|
|
27
|
+
--> statement-breakpoint
|
|
28
|
+
CREATE TABLE `host_project_env_replica_projects` (
|
|
29
|
+
`project_id` text PRIMARY KEY NOT NULL,
|
|
30
|
+
`record_version` integer NOT NULL,
|
|
31
|
+
CONSTRAINT `host_project_env_replica_projects_version` CHECK (`record_version` >= 0)
|
|
32
|
+
);
|
|
33
|
+
--> statement-breakpoint
|
|
34
|
+
CREATE TABLE `host_project_env_replica_records` (
|
|
35
|
+
`project_id` text NOT NULL,
|
|
36
|
+
`key` text NOT NULL,
|
|
37
|
+
`revision` integer NOT NULL,
|
|
38
|
+
`generation` integer NOT NULL,
|
|
39
|
+
`tombstone` integer NOT NULL,
|
|
40
|
+
`value_ref_json` text,
|
|
41
|
+
`binding_json` text,
|
|
42
|
+
`policy_epoch` integer,
|
|
43
|
+
`listing_generation` integer NOT NULL,
|
|
44
|
+
`materialization` text NOT NULL,
|
|
45
|
+
`materialization_reason` text,
|
|
46
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
47
|
+
PRIMARY KEY(`project_id`, `key`),
|
|
48
|
+
CONSTRAINT `host_project_env_replica_records_head` CHECK (
|
|
49
|
+
(`tombstone` = 0 AND `revision` >= 1 AND `generation` >= 1 AND `value_ref_json` IS NOT NULL) OR
|
|
50
|
+
(`tombstone` = 1 AND `revision` = 1 AND `generation` = 0 AND `value_ref_json` IS NULL) OR
|
|
51
|
+
(`tombstone` = 1 AND `revision` >= 2 AND `generation` >= 2 AND `value_ref_json` IS NULL)
|
|
52
|
+
),
|
|
53
|
+
CONSTRAINT `host_project_env_replica_records_binding` CHECK (
|
|
54
|
+
(`generation` = 0 AND `binding_json` IS NULL AND `policy_epoch` IS NULL AND `listing_generation` >= 2 AND (`listing_generation` % 2) = 0) OR
|
|
55
|
+
(`generation` > 0 AND `policy_epoch` >= 1)
|
|
56
|
+
),
|
|
57
|
+
CONSTRAINT `host_project_env_replica_records_listing` CHECK (`listing_generation` >= 0),
|
|
58
|
+
CONSTRAINT `host_project_env_replica_records_boolean` CHECK (`tombstone` IN (0, 1)),
|
|
59
|
+
CONSTRAINT `host_project_env_replica_records_materialization` CHECK (
|
|
60
|
+
(`materialization` = 'installed' AND `tombstone` = 0 AND `materialization_reason` IS NULL) OR
|
|
61
|
+
(`materialization` IN ('withheld', 'unavailable') AND `materialization_reason` IS NOT NULL)
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
--> statement-breakpoint
|
|
65
|
+
CREATE TABLE `host_project_env_replica_values` (
|
|
66
|
+
`project_id` text NOT NULL,
|
|
67
|
+
`key` text NOT NULL,
|
|
68
|
+
`revision` integer NOT NULL,
|
|
69
|
+
`generation` integer NOT NULL,
|
|
70
|
+
`value_ref_json` text NOT NULL,
|
|
71
|
+
`value_enc` text NOT NULL,
|
|
72
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
73
|
+
PRIMARY KEY(`project_id`, `key`),
|
|
74
|
+
CONSTRAINT `host_project_env_replica_values_head` CHECK (`revision` >= 1 AND `generation` >= 1)
|
|
75
|
+
);
|
|
76
|
+
--> statement-breakpoint
|
|
77
|
+
CREATE TABLE `host_project_env_replica_stage_projects` (
|
|
78
|
+
`attempt_id` text NOT NULL,
|
|
79
|
+
`project_id` text NOT NULL,
|
|
80
|
+
`record_version` integer,
|
|
81
|
+
`last_key` text,
|
|
82
|
+
`complete` integer NOT NULL DEFAULT 0,
|
|
83
|
+
PRIMARY KEY(`attempt_id`, `project_id`),
|
|
84
|
+
CONSTRAINT `host_project_env_replica_stage_projects_version` CHECK (`record_version` IS NULL OR `record_version` >= 0),
|
|
85
|
+
CONSTRAINT `host_project_env_replica_stage_projects_complete` CHECK (`complete` IN (0, 1))
|
|
86
|
+
);
|
|
87
|
+
--> statement-breakpoint
|
|
88
|
+
CREATE TABLE `host_project_env_replica_stage_records` (
|
|
89
|
+
`attempt_id` text NOT NULL,
|
|
90
|
+
`project_id` text NOT NULL,
|
|
91
|
+
`key` text NOT NULL,
|
|
92
|
+
`revision` integer NOT NULL,
|
|
93
|
+
`generation` integer NOT NULL,
|
|
94
|
+
`tombstone` integer NOT NULL,
|
|
95
|
+
`value_ref_json` text,
|
|
96
|
+
`binding_json` text,
|
|
97
|
+
`policy_epoch` integer,
|
|
98
|
+
`listing_generation` integer NOT NULL,
|
|
99
|
+
`materialization` text NOT NULL,
|
|
100
|
+
`materialization_reason` text,
|
|
101
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
102
|
+
`present` integer NOT NULL DEFAULT 0,
|
|
103
|
+
PRIMARY KEY(`attempt_id`, `project_id`, `key`),
|
|
104
|
+
CONSTRAINT `host_project_env_replica_stage_records_head` CHECK (
|
|
105
|
+
(`tombstone` = 0 AND `revision` >= 1 AND `generation` >= 1 AND `value_ref_json` IS NOT NULL) OR
|
|
106
|
+
(`tombstone` = 1 AND `revision` = 1 AND `generation` = 0 AND `value_ref_json` IS NULL) OR
|
|
107
|
+
(`tombstone` = 1 AND `revision` >= 2 AND `generation` >= 2 AND `value_ref_json` IS NULL)
|
|
108
|
+
),
|
|
109
|
+
CONSTRAINT `host_project_env_replica_stage_records_binding` CHECK (
|
|
110
|
+
(`generation` = 0 AND `binding_json` IS NULL AND `policy_epoch` IS NULL AND `listing_generation` >= 2 AND (`listing_generation` % 2) = 0) OR
|
|
111
|
+
(`generation` > 0 AND `policy_epoch` >= 1)
|
|
112
|
+
),
|
|
113
|
+
CONSTRAINT `host_project_env_replica_stage_records_listing` CHECK (`listing_generation` >= 0),
|
|
114
|
+
CONSTRAINT `host_project_env_replica_stage_records_boolean` CHECK (`tombstone` IN (0, 1) AND `present` IN (0, 1)),
|
|
115
|
+
CONSTRAINT `host_project_env_replica_stage_records_materialization` CHECK (
|
|
116
|
+
(`materialization` = 'installed' AND `tombstone` = 0 AND `materialization_reason` IS NULL) OR
|
|
117
|
+
(`materialization` IN ('withheld', 'unavailable') AND `materialization_reason` IS NOT NULL)
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
--> statement-breakpoint
|
|
121
|
+
CREATE TABLE `host_project_env_replica_stage_values` (
|
|
122
|
+
`attempt_id` text NOT NULL,
|
|
123
|
+
`project_id` text NOT NULL,
|
|
124
|
+
`key` text NOT NULL,
|
|
125
|
+
`revision` integer NOT NULL,
|
|
126
|
+
`generation` integer NOT NULL,
|
|
127
|
+
`value_ref_json` text NOT NULL,
|
|
128
|
+
`value_enc` text NOT NULL,
|
|
129
|
+
`updated_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
130
|
+
PRIMARY KEY(`attempt_id`, `project_id`, `key`),
|
|
131
|
+
CONSTRAINT `host_project_env_replica_stage_values_head` CHECK (`revision` >= 1 AND `generation` >= 1)
|
|
132
|
+
);
|
|
133
|
+
--> statement-breakpoint
|
|
134
|
+
CREATE TABLE `host_project_env_replica_receipts` (
|
|
135
|
+
`stream_id` text NOT NULL,
|
|
136
|
+
`sequence` integer NOT NULL,
|
|
137
|
+
`request_mac` text NOT NULL,
|
|
138
|
+
`result_json` text,
|
|
139
|
+
`record_version` integer,
|
|
140
|
+
`ok` integer NOT NULL,
|
|
141
|
+
`code` text,
|
|
142
|
+
`created_at` integer NOT NULL DEFAULT (unixepoch() * 1000),
|
|
143
|
+
PRIMARY KEY(`stream_id`, `sequence`),
|
|
144
|
+
CONSTRAINT `host_project_env_replica_receipts_sequence` CHECK (`sequence` >= 1),
|
|
145
|
+
CONSTRAINT `host_project_env_replica_receipts_boolean` CHECK (`ok` IN (0, 1)),
|
|
146
|
+
CONSTRAINT `host_project_env_replica_receipts_outcome` CHECK (
|
|
147
|
+
(`ok` = 1 AND `result_json` IS NOT NULL AND `record_version` IS NOT NULL AND `code` IS NULL) OR
|
|
148
|
+
(`ok` = 0 AND `result_json` IS NULL AND `record_version` IS NULL AND `code` IS NOT NULL)
|
|
149
|
+
)
|
|
150
|
+
);
|
|
@@ -169,6 +169,27 @@
|
|
|
169
169
|
"when": 1788900000005,
|
|
170
170
|
"tag": "0023_project_env_org",
|
|
171
171
|
"breakpoints": true
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"idx": 24,
|
|
175
|
+
"version": "6",
|
|
176
|
+
"when": 1788900000006,
|
|
177
|
+
"tag": "0024_task_env_mode",
|
|
178
|
+
"breakpoints": true
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"idx": 25,
|
|
182
|
+
"version": "6",
|
|
183
|
+
"when": 1788900000007,
|
|
184
|
+
"tag": "0025_project_env_authority",
|
|
185
|
+
"breakpoints": true
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"idx": 26,
|
|
189
|
+
"version": "6",
|
|
190
|
+
"when": 1788900000008,
|
|
191
|
+
"tag": "0026_project_env_replica",
|
|
192
|
+
"breakpoints": true
|
|
172
193
|
}
|
|
173
194
|
]
|
|
174
195
|
}
|
package/db/schema.ts
CHANGED
|
@@ -29,6 +29,11 @@ export const hostTasks = sqliteTable(
|
|
|
29
29
|
// Docker Compose only when their legacy runtime identity is complete.
|
|
30
30
|
environmentProvider: text("environment_provider"),
|
|
31
31
|
environmentLocator: text("environment_locator"),
|
|
32
|
+
// ADR-153 rollout fence. Null rows predate the loader and mean `baked`.
|
|
33
|
+
envMode: text("env_mode"),
|
|
34
|
+
// Value-free resolver metadata for rebuilding the project-env gateway
|
|
35
|
+
// after a runner restart, before the cloud re-sends task-up.
|
|
36
|
+
projectEnvSpec: text("project_env_spec"),
|
|
32
37
|
statusMirror: text("status_mirror"),
|
|
33
38
|
// Owner + project slugs — received in TaskLaunchInput at task-up and
|
|
34
39
|
// persisted here so the local UI (ADR-028) can show "who/what" without a
|
|
@@ -37,8 +42,8 @@ export const hostTasks = sqliteTable(
|
|
|
37
42
|
ownerEmail: text("owner_email"),
|
|
38
43
|
ownerName: text("owner_name"),
|
|
39
44
|
projectSlugs: text("project_slugs").notNull().default("[]"),
|
|
40
|
-
// Lead project first.
|
|
41
|
-
//
|
|
45
|
+
// Lead project first. Retained for legacy/baked machine-task agent spawns;
|
|
46
|
+
// loader tasks resolve only declared names through projectEnvSpec.
|
|
42
47
|
projectIds: text("project_ids").notNull().default("[]"),
|
|
43
48
|
// ADR-131 D3: which engine-account scope this task resolves credentials
|
|
44
49
|
// from ("org:<orgId>" on a shared host; null = "host"). Persisted so a
|
|
@@ -202,6 +207,311 @@ export const hostProjectEnv = sqliteTable(
|
|
|
202
207
|
export type HostProjectEnv = typeof hostProjectEnv.$inferSelect;
|
|
203
208
|
export type NewHostProjectEnv = typeof hostProjectEnv.$inferInsert;
|
|
204
209
|
|
|
210
|
+
// ADR-153: value-free authority records live beside (not inside) the legacy
|
|
211
|
+
// encrypted value row. A delete therefore keeps its binding/head forever,
|
|
212
|
+
// while a live owned-runner head always refers to that row.
|
|
213
|
+
export const hostProjectEnvRecords = sqliteTable(
|
|
214
|
+
"host_project_env_records",
|
|
215
|
+
{
|
|
216
|
+
projectId: text("project_id").notNull(),
|
|
217
|
+
key: text("key").notNull(),
|
|
218
|
+
revision: integer("revision").notNull(),
|
|
219
|
+
generation: integer("generation").notNull(),
|
|
220
|
+
tombstone: integer("tombstone", { mode: "boolean" }).notNull(),
|
|
221
|
+
bindingJson: text("binding_json"),
|
|
222
|
+
policyEpoch: integer("policy_epoch"),
|
|
223
|
+
listingGeneration: integer("listing_generation").notNull(),
|
|
224
|
+
updatedAt: integer("updated_at", { mode: "number" })
|
|
225
|
+
.notNull()
|
|
226
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
227
|
+
},
|
|
228
|
+
(t) => ({
|
|
229
|
+
pk: primaryKey({ columns: [t.projectId, t.key] }),
|
|
230
|
+
}),
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
export const hostProjectEnvProjects = sqliteTable("host_project_env_projects", {
|
|
234
|
+
projectId: text("project_id").primaryKey(),
|
|
235
|
+
recordVersion: integer("record_version").notNull().default(0),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Singleton activation, serving gate and ordered-delivery cursor. `modernWrite`
|
|
239
|
+
// is raised only inside a mutation transaction so the permanent legacy table
|
|
240
|
+
// triggers can distinguish this build from an old process still writing it.
|
|
241
|
+
export const hostProjectEnvAuthority = sqliteTable("host_project_env_authority", {
|
|
242
|
+
id: text("id").primaryKey().default("host"),
|
|
243
|
+
activated: integer("activated", { mode: "boolean" }).notNull().default(false),
|
|
244
|
+
gateOpen: integer("gate_open", { mode: "boolean" }).notNull().default(true),
|
|
245
|
+
streamId: text("stream_id"),
|
|
246
|
+
appliedSequence: integer("applied_sequence").notNull().default(0),
|
|
247
|
+
attemptId: text("attempt_id"),
|
|
248
|
+
attemptLegacyVersion: integer("attempt_legacy_version"),
|
|
249
|
+
legacyVersion: integer("legacy_version").notNull().default(0),
|
|
250
|
+
policyEpochsJson: text("policy_epochs_json").notNull().default("[1]"),
|
|
251
|
+
modernWrite: integer("modern_write", { mode: "boolean" }).notNull().default(false),
|
|
252
|
+
updatedAt: integer("updated_at", { mode: "number" })
|
|
253
|
+
.notNull()
|
|
254
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// A receipt contains a keyed request MAC and a value-free result. In
|
|
258
|
+
// particular, a set's plaintext is never replayable from SQLite.
|
|
259
|
+
export const hostProjectEnvDeliveryReceipts = sqliteTable(
|
|
260
|
+
"host_project_env_delivery_receipts",
|
|
261
|
+
{
|
|
262
|
+
streamId: text("stream_id").notNull(),
|
|
263
|
+
sequence: integer("sequence").notNull(),
|
|
264
|
+
requestKind: text("request_kind").notNull(),
|
|
265
|
+
requestMac: text("request_mac").notNull(),
|
|
266
|
+
ok: integer("ok", { mode: "boolean" }).notNull(),
|
|
267
|
+
stateJson: text("state_json"),
|
|
268
|
+
recordVersion: integer("record_version"),
|
|
269
|
+
code: text("code"),
|
|
270
|
+
createdAt: integer("created_at", { mode: "number" })
|
|
271
|
+
.notNull()
|
|
272
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
273
|
+
},
|
|
274
|
+
(t) => ({ pk: primaryKey({ columns: [t.streamId, t.sequence] }) }),
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
export const hostProjectEnvLegacyJournal = sqliteTable(
|
|
278
|
+
"host_project_env_legacy_journal",
|
|
279
|
+
{
|
|
280
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
281
|
+
projectId: text("project_id").notNull(),
|
|
282
|
+
key: text("key").notNull(),
|
|
283
|
+
mutation: text("mutation").notNull(),
|
|
284
|
+
createdAt: integer("created_at", { mode: "number" })
|
|
285
|
+
.notNull()
|
|
286
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
287
|
+
},
|
|
288
|
+
(t) => ({ keyIdx: index("host_project_env_legacy_journal_key_idx").on(t.projectId, t.key) }),
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// ADR-153 Uai Cloud task-machine replica. These tables are deliberately
|
|
292
|
+
// separate from the owned-runner authority above: a sidecar stages a complete
|
|
293
|
+
// task snapshot while serving nothing, then promotes it atomically. In
|
|
294
|
+
// particular, materialising a cloud value must not fire the owned authority's
|
|
295
|
+
// permanent legacy-write triggers.
|
|
296
|
+
export const hostProjectEnvReplicaAuthority = sqliteTable(
|
|
297
|
+
"host_project_env_replica_authority",
|
|
298
|
+
{
|
|
299
|
+
id: text("id").primaryKey().default("replica"),
|
|
300
|
+
activated: integer("activated", { mode: "boolean" }).notNull().default(false),
|
|
301
|
+
gateOpen: integer("gate_open", { mode: "boolean" }).notNull().default(false),
|
|
302
|
+
streamId: text("stream_id"),
|
|
303
|
+
appliedSequence: integer("applied_sequence").notNull().default(0),
|
|
304
|
+
attemptId: text("attempt_id"),
|
|
305
|
+
orgId: text("org_id"),
|
|
306
|
+
policyEpochsJson: text("policy_epochs_json").notNull().default("[1]"),
|
|
307
|
+
updatedAt: integer("updated_at", { mode: "number" })
|
|
308
|
+
.notNull()
|
|
309
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
310
|
+
},
|
|
311
|
+
(t) => ({
|
|
312
|
+
singleton: check("host_project_env_replica_authority_singleton", sql`${t.id} = 'replica'`),
|
|
313
|
+
booleans: check(
|
|
314
|
+
"host_project_env_replica_authority_booleans",
|
|
315
|
+
sql`${t.activated} IN (0, 1) AND ${t.gateOpen} IN (0, 1)`,
|
|
316
|
+
),
|
|
317
|
+
cursor: check("host_project_env_replica_authority_cursor", sql`${t.appliedSequence} >= 0`),
|
|
318
|
+
stream: check(
|
|
319
|
+
"host_project_env_replica_authority_stream",
|
|
320
|
+
sql`(${t.activated} = 0 AND ${t.streamId} IS NULL AND ${t.appliedSequence} = 0 AND ${t.attemptId} IS NULL AND ${t.orgId} IS NULL AND ${t.gateOpen} = 0) OR (${t.activated} = 1 AND ${t.streamId} IS NOT NULL AND ${t.orgId} IS NOT NULL)`,
|
|
321
|
+
),
|
|
322
|
+
gate: check(
|
|
323
|
+
"host_project_env_replica_authority_gate",
|
|
324
|
+
sql`${t.gateOpen} = 0 OR ${t.attemptId} IS NULL`,
|
|
325
|
+
),
|
|
326
|
+
}),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
export const hostProjectEnvReplicaProjects = sqliteTable(
|
|
330
|
+
"host_project_env_replica_projects",
|
|
331
|
+
{
|
|
332
|
+
projectId: text("project_id").primaryKey(),
|
|
333
|
+
recordVersion: integer("record_version").notNull(),
|
|
334
|
+
},
|
|
335
|
+
(t) => ({
|
|
336
|
+
version: check("host_project_env_replica_projects_version", sql`${t.recordVersion} >= 0`),
|
|
337
|
+
}),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const replicaRecordColumns = () => ({
|
|
341
|
+
projectId: text("project_id").notNull(),
|
|
342
|
+
key: text("key").notNull(),
|
|
343
|
+
revision: integer("revision").notNull(),
|
|
344
|
+
generation: integer("generation").notNull(),
|
|
345
|
+
tombstone: integer("tombstone", { mode: "boolean" }).notNull(),
|
|
346
|
+
valueRefJson: text("value_ref_json"),
|
|
347
|
+
bindingJson: text("binding_json"),
|
|
348
|
+
policyEpoch: integer("policy_epoch"),
|
|
349
|
+
listingGeneration: integer("listing_generation").notNull(),
|
|
350
|
+
materialization: text("materialization").notNull(),
|
|
351
|
+
materializationReason: text("materialization_reason"),
|
|
352
|
+
updatedAt: integer("updated_at", { mode: "number" })
|
|
353
|
+
.notNull()
|
|
354
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
export const hostProjectEnvReplicaRecords = sqliteTable(
|
|
358
|
+
"host_project_env_replica_records",
|
|
359
|
+
replicaRecordColumns(),
|
|
360
|
+
(t) => ({
|
|
361
|
+
pk: primaryKey({ columns: [t.projectId, t.key] }),
|
|
362
|
+
head: check(
|
|
363
|
+
"host_project_env_replica_records_head",
|
|
364
|
+
sql`(${t.tombstone} = 0 AND ${t.revision} >= 1 AND ${t.generation} >= 1 AND ${t.valueRefJson} IS NOT NULL) OR (${t.tombstone} = 1 AND ${t.revision} = 1 AND ${t.generation} = 0 AND ${t.valueRefJson} IS NULL) OR (${t.tombstone} = 1 AND ${t.revision} >= 2 AND ${t.generation} >= 2 AND ${t.valueRefJson} IS NULL)`,
|
|
365
|
+
),
|
|
366
|
+
binding: check(
|
|
367
|
+
"host_project_env_replica_records_binding",
|
|
368
|
+
sql`(${t.generation} = 0 AND ${t.bindingJson} IS NULL AND ${t.policyEpoch} IS NULL AND ${t.listingGeneration} >= 2 AND (${t.listingGeneration} % 2) = 0) OR (${t.generation} > 0 AND ${t.policyEpoch} >= 1)`,
|
|
369
|
+
),
|
|
370
|
+
listing: check(
|
|
371
|
+
"host_project_env_replica_records_listing",
|
|
372
|
+
sql`${t.listingGeneration} >= 0`,
|
|
373
|
+
),
|
|
374
|
+
boolean: check(
|
|
375
|
+
"host_project_env_replica_records_boolean",
|
|
376
|
+
sql`${t.tombstone} IN (0, 1)`,
|
|
377
|
+
),
|
|
378
|
+
materialization: check(
|
|
379
|
+
"host_project_env_replica_records_materialization",
|
|
380
|
+
sql`(${t.materialization} = 'installed' AND ${t.tombstone} = 0 AND ${t.materializationReason} IS NULL) OR (${t.materialization} IN ('withheld', 'unavailable') AND ${t.materializationReason} IS NOT NULL)`,
|
|
381
|
+
),
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
export const hostProjectEnvReplicaValues = sqliteTable(
|
|
386
|
+
"host_project_env_replica_values",
|
|
387
|
+
{
|
|
388
|
+
projectId: text("project_id").notNull(),
|
|
389
|
+
key: text("key").notNull(),
|
|
390
|
+
revision: integer("revision").notNull(),
|
|
391
|
+
generation: integer("generation").notNull(),
|
|
392
|
+
valueRefJson: text("value_ref_json").notNull(),
|
|
393
|
+
valueEnc: text("value_enc").notNull(),
|
|
394
|
+
updatedAt: integer("updated_at", { mode: "number" })
|
|
395
|
+
.notNull()
|
|
396
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
397
|
+
},
|
|
398
|
+
(t) => ({
|
|
399
|
+
pk: primaryKey({ columns: [t.projectId, t.key] }),
|
|
400
|
+
head: check(
|
|
401
|
+
"host_project_env_replica_values_head",
|
|
402
|
+
sql`${t.revision} >= 1 AND ${t.generation} >= 1`,
|
|
403
|
+
),
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
export const hostProjectEnvReplicaStageProjects = sqliteTable(
|
|
408
|
+
"host_project_env_replica_stage_projects",
|
|
409
|
+
{
|
|
410
|
+
attemptId: text("attempt_id").notNull(),
|
|
411
|
+
projectId: text("project_id").notNull(),
|
|
412
|
+
recordVersion: integer("record_version"),
|
|
413
|
+
lastKey: text("last_key"),
|
|
414
|
+
complete: integer("complete", { mode: "boolean" }).notNull().default(false),
|
|
415
|
+
},
|
|
416
|
+
(t) => ({
|
|
417
|
+
pk: primaryKey({ columns: [t.attemptId, t.projectId] }),
|
|
418
|
+
version: check(
|
|
419
|
+
"host_project_env_replica_stage_projects_version",
|
|
420
|
+
sql`${t.recordVersion} IS NULL OR ${t.recordVersion} >= 0`,
|
|
421
|
+
),
|
|
422
|
+
completeBoolean: check(
|
|
423
|
+
"host_project_env_replica_stage_projects_complete",
|
|
424
|
+
sql`${t.complete} IN (0, 1)`,
|
|
425
|
+
),
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
export const hostProjectEnvReplicaStageRecords = sqliteTable(
|
|
430
|
+
"host_project_env_replica_stage_records",
|
|
431
|
+
{
|
|
432
|
+
attemptId: text("attempt_id").notNull(),
|
|
433
|
+
...replicaRecordColumns(),
|
|
434
|
+
present: integer("present", { mode: "boolean" }).notNull().default(false),
|
|
435
|
+
},
|
|
436
|
+
(t) => ({
|
|
437
|
+
pk: primaryKey({ columns: [t.attemptId, t.projectId, t.key] }),
|
|
438
|
+
head: check(
|
|
439
|
+
"host_project_env_replica_stage_records_head",
|
|
440
|
+
sql`(${t.tombstone} = 0 AND ${t.revision} >= 1 AND ${t.generation} >= 1 AND ${t.valueRefJson} IS NOT NULL) OR (${t.tombstone} = 1 AND ${t.revision} = 1 AND ${t.generation} = 0 AND ${t.valueRefJson} IS NULL) OR (${t.tombstone} = 1 AND ${t.revision} >= 2 AND ${t.generation} >= 2 AND ${t.valueRefJson} IS NULL)`,
|
|
441
|
+
),
|
|
442
|
+
binding: check(
|
|
443
|
+
"host_project_env_replica_stage_records_binding",
|
|
444
|
+
sql`(${t.generation} = 0 AND ${t.bindingJson} IS NULL AND ${t.policyEpoch} IS NULL AND ${t.listingGeneration} >= 2 AND (${t.listingGeneration} % 2) = 0) OR (${t.generation} > 0 AND ${t.policyEpoch} >= 1)`,
|
|
445
|
+
),
|
|
446
|
+
listing: check(
|
|
447
|
+
"host_project_env_replica_stage_records_listing",
|
|
448
|
+
sql`${t.listingGeneration} >= 0`,
|
|
449
|
+
),
|
|
450
|
+
boolean: check(
|
|
451
|
+
"host_project_env_replica_stage_records_boolean",
|
|
452
|
+
sql`${t.tombstone} IN (0, 1) AND ${t.present} IN (0, 1)`,
|
|
453
|
+
),
|
|
454
|
+
materialization: check(
|
|
455
|
+
"host_project_env_replica_stage_records_materialization",
|
|
456
|
+
sql`(${t.materialization} = 'installed' AND ${t.tombstone} = 0 AND ${t.materializationReason} IS NULL) OR (${t.materialization} IN ('withheld', 'unavailable') AND ${t.materializationReason} IS NOT NULL)`,
|
|
457
|
+
),
|
|
458
|
+
}),
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
export const hostProjectEnvReplicaStageValues = sqliteTable(
|
|
462
|
+
"host_project_env_replica_stage_values",
|
|
463
|
+
{
|
|
464
|
+
attemptId: text("attempt_id").notNull(),
|
|
465
|
+
projectId: text("project_id").notNull(),
|
|
466
|
+
key: text("key").notNull(),
|
|
467
|
+
revision: integer("revision").notNull(),
|
|
468
|
+
generation: integer("generation").notNull(),
|
|
469
|
+
valueRefJson: text("value_ref_json").notNull(),
|
|
470
|
+
valueEnc: text("value_enc").notNull(),
|
|
471
|
+
updatedAt: integer("updated_at", { mode: "number" })
|
|
472
|
+
.notNull()
|
|
473
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
474
|
+
},
|
|
475
|
+
(t) => ({
|
|
476
|
+
pk: primaryKey({ columns: [t.attemptId, t.projectId, t.key] }),
|
|
477
|
+
head: check(
|
|
478
|
+
"host_project_env_replica_stage_values_head",
|
|
479
|
+
sql`${t.revision} >= 1 AND ${t.generation} >= 1`,
|
|
480
|
+
),
|
|
481
|
+
}),
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
export const hostProjectEnvReplicaReceipts = sqliteTable(
|
|
485
|
+
"host_project_env_replica_receipts",
|
|
486
|
+
{
|
|
487
|
+
streamId: text("stream_id").notNull(),
|
|
488
|
+
sequence: integer("sequence").notNull(),
|
|
489
|
+
requestMac: text("request_mac").notNull(),
|
|
490
|
+
resultJson: text("result_json"),
|
|
491
|
+
recordVersion: integer("record_version"),
|
|
492
|
+
ok: integer("ok", { mode: "boolean" }).notNull(),
|
|
493
|
+
code: text("code"),
|
|
494
|
+
createdAt: integer("created_at", { mode: "number" })
|
|
495
|
+
.notNull()
|
|
496
|
+
.default(sql`(unixepoch() * 1000)`),
|
|
497
|
+
},
|
|
498
|
+
(t) => ({
|
|
499
|
+
pk: primaryKey({ columns: [t.streamId, t.sequence] }),
|
|
500
|
+
sequenceCheck: check(
|
|
501
|
+
"host_project_env_replica_receipts_sequence",
|
|
502
|
+
sql`${t.sequence} >= 1`,
|
|
503
|
+
),
|
|
504
|
+
booleanCheck: check(
|
|
505
|
+
"host_project_env_replica_receipts_boolean",
|
|
506
|
+
sql`${t.ok} IN (0, 1)`,
|
|
507
|
+
),
|
|
508
|
+
outcome: check(
|
|
509
|
+
"host_project_env_replica_receipts_outcome",
|
|
510
|
+
sql`(${t.ok} = 1 AND ${t.resultJson} IS NOT NULL AND ${t.recordVersion} IS NOT NULL AND ${t.code} IS NULL) OR (${t.ok} = 0 AND ${t.resultJson} IS NULL AND ${t.recordVersion} IS NULL AND ${t.code} IS NOT NULL)`,
|
|
511
|
+
),
|
|
512
|
+
}),
|
|
513
|
+
);
|
|
514
|
+
|
|
205
515
|
// User-authed MCP connections (ADR-057). One row per cloud connection id; all
|
|
206
516
|
// secret material (static header, OAuth client secret, PKCE verifier, tokens)
|
|
207
517
|
// is sealed with the host master key in single-column `ct.nonce` base64 form
|