@agentsdock/server 1.0.7-beta.5
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/LICENSE +202 -0
- package/NOTICE +19 -0
- package/README.md +60 -0
- package/npm/cli.cjs +289 -0
- package/package.json +32 -0
- package/server/LICENSE +202 -0
- package/server/NOTICE +19 -0
- package/server/VERSION +1 -0
- package/server/activation_transaction.py +2458 -0
- package/server/agent_server.py +93226 -0
- package/server/agentsdock_chats.py +1237 -0
- package/server/agentsdock_emergency.py +338 -0
- package/server/agentsdock_jobs.py +640 -0
- package/server/agentsdock_mail.py +325 -0
- package/server/agentsdock_publish.py +404 -0
- package/server/agentsdock_team.py +793 -0
- package/server/agentsdock_team_hub/__init__.py +33 -0
- package/server/agentsdock_team_hub/auth.py +710 -0
- package/server/agentsdock_team_hub/cli.py +647 -0
- package/server/agentsdock_team_hub/database.py +310 -0
- package/server/agentsdock_team_hub/mail_hint_streams.py +176 -0
- package/server/agentsdock_team_hub/mail_hints.py +201 -0
- package/server/agentsdock_team_hub/migrations/0001_identity_auth.sql +690 -0
- package/server/agentsdock_team_hub/migrations/0002_teamspace_ledger.sql +1336 -0
- package/server/agentsdock_team_hub/migrations/0003_service_runtime.sql +361 -0
- package/server/agentsdock_team_hub/migrations/0004_managed_host_binding.sql +22 -0
- package/server/agentsdock_team_hub/migrations/0005_tailnet_bootstrap_delegations.sql +63 -0
- package/server/agentsdock_team_hub/migrations/0006_team_network_mailbox.sql +423 -0
- package/server/agentsdock_team_hub/migrations/0007_local_agent_mail.sql +63 -0
- package/server/agentsdock_team_hub/migrations/0008_managed_server_session.sql +73 -0
- package/server/agentsdock_team_hub/migrations/0009_team_messages.sql +324 -0
- package/server/agentsdock_team_hub/migrations/0010_team_attachment_orphan_reclamation.sql +52 -0
- package/server/agentsdock_team_hub/migrations/0011_human_admin_paging.sql +65 -0
- package/server/agentsdock_team_hub/migrations/0012_network_content_deletions.sql +69 -0
- package/server/agentsdock_team_hub/migrations/0013_team_message_revisions.sql +48 -0
- package/server/agentsdock_team_hub/migrations/0014_managed_network_owner.sql +50 -0
- package/server/agentsdock_team_hub/migrations/0015_team_message_inbox_dismissals.sql +3 -0
- package/server/agentsdock_team_hub/migrations/0016_team_message_all_servers.sql +4 -0
- package/server/agentsdock_team_hub/migrations/0017_skill_announcement_deletions.sql +16 -0
- package/server/agentsdock_team_hub/migrations/0018_team_mail_subjects.sql +8 -0
- package/server/agentsdock_team_hub/migrations/0019_team_mailbox_state.sql +5 -0
- package/server/agentsdock_team_hub/migrations/0020_team_mail_arrivals.sql +44 -0
- package/server/agentsdock_team_hub/migrations/0021_team_mail_threads.sql +4 -0
- package/server/agentsdock_team_hub/migrations/0022_team_bulletin_changes.sql +49 -0
- package/server/agentsdock_team_hub/migrations/0023_team_message_search.sql +93 -0
- package/server/agentsdock_team_hub/migrations/__init__.py +1 -0
- package/server/agentsdock_team_hub/notification_hints.py +306 -0
- package/server/agentsdock_team_hub/secure_peer.py +11215 -0
- package/server/agentsdock_team_hub/secure_peer_hub.py +1372 -0
- package/server/agentsdock_team_hub/security.py +318 -0
- package/server/agentsdock_team_hub/service.py +1737 -0
- package/server/agentsdock_team_hub/store.py +16276 -0
- package/server/chat_mailbox.py +448 -0
- package/server/claude_background_reconciliation.py +92 -0
- package/server/claude_goals.py +245 -0
- package/server/claude_history_provenance.py +137 -0
- package/server/claude_history_repair.py +1601 -0
- package/server/claude_sdk_client.py +3731 -0
- package/server/claude_side_question.py +212 -0
- package/server/codex_app_server.py +3030 -0
- package/server/codex_auth.py +93 -0
- package/server/codex_history_repair.py +1247 -0
- package/server/codex_provider.py +1120 -0
- package/server/codex_side_question.py +301 -0
- package/server/cursor_agent_client.py +654 -0
- package/server/cursor_process_guard.py +347 -0
- package/server/execution_activation.py +610 -0
- package/server/execution_control.py +233 -0
- package/server/execution_durability.py +184 -0
- package/server/execution_http.py +256 -0
- package/server/execution_install.py +792 -0
- package/server/execution_legacy_runner.py +406 -0
- package/server/execution_maintenance.py +137 -0
- package/server/execution_manage.py +615 -0
- package/server/execution_ownership.py +83 -0
- package/server/execution_preparation.py +362 -0
- package/server/execution_recovery.py +845 -0
- package/server/execution_recovery_status.py +195 -0
- package/server/execution_service.py +387 -0
- package/server/execution_transport.py +622 -0
- package/server/execution_uninstall.py +406 -0
- package/server/execution_update_status.py +49 -0
- package/server/install.sh +7074 -0
- package/server/interactive_chat_controls.py +153 -0
- package/server/interactive_chat_native.py +66 -0
- package/server/interactive_chat_projection.py +152 -0
- package/server/interactive_chat_runtime.py +132 -0
- package/server/interactive_chat_share_routes.py +534 -0
- package/server/interactive_chat_share_web.py +12 -0
- package/server/interactive_chat_shares.py +238 -0
- package/server/provider_commands.py +425 -0
- package/server/public_chat_share_routes.py +426 -0
- package/server/public_chat_shares.py +759 -0
- package/server/public_chat_transcript.py +357 -0
- package/server/pyproject.toml +29 -0
- package/server/release-public-key.pem +3 -0
- package/server/secure_peer_delivery.py +1236 -0
- package/server/secure_peer_runtime.py +7694 -0
- package/server/shared_chat_video_stream.py +262 -0
- package/server/shared_chat_videos.py +155 -0
- package/server/side_questions.py +594 -0
- package/server/team_hub_host.py +1354 -0
- package/server/team_mail_grants.py +136 -0
- package/server/team_mail_runtime.py +302 -0
- package/server/team_mail_websocket.py +211 -0
- package/server/title_generation.py +333 -0
- package/server/uninstall.sh +359 -0
- package/server/update_handoff.py +237 -0
- package/server/update_preparation.py +264 -0
- package/server/update_recovery.py +140 -0
- package/server/update_runner.py +1819 -0
- package/server/uv.lock +1101 -0
- package/server/workspace_git.py +545 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
-- Runnable Team Hub V1 security and service ledgers. This migration tightens
|
|
2
|
+
-- the pre-service foundation without changing its public identity keys.
|
|
3
|
+
|
|
4
|
+
UPDATE invitations
|
|
5
|
+
SET revoked_at = created_at
|
|
6
|
+
WHERE invitee_email_normalized IS NULL
|
|
7
|
+
AND redeemed_at IS NULL
|
|
8
|
+
AND revoked_at IS NULL;
|
|
9
|
+
|
|
10
|
+
CREATE TRIGGER invitations_require_bound_recipient
|
|
11
|
+
BEFORE INSERT ON invitations
|
|
12
|
+
FOR EACH ROW WHEN NEW.invitee_email_normalized IS NULL
|
|
13
|
+
BEGIN
|
|
14
|
+
SELECT RAISE(ABORT, 'invitations require a bound recipient email');
|
|
15
|
+
END;
|
|
16
|
+
|
|
17
|
+
CREATE TRIGGER active_owner_cannot_be_removed
|
|
18
|
+
BEFORE UPDATE OF role, status ON memberships
|
|
19
|
+
FOR EACH ROW WHEN OLD.role = 'owner' AND OLD.status = 'active'
|
|
20
|
+
AND (NEW.role <> 'owner' OR NEW.status <> 'active')
|
|
21
|
+
BEGIN
|
|
22
|
+
SELECT RAISE(ABORT, 'an active team owner cannot be removed');
|
|
23
|
+
END;
|
|
24
|
+
|
|
25
|
+
CREATE TRIGGER active_owner_principal_cannot_be_disabled
|
|
26
|
+
BEFORE UPDATE OF status ON principals
|
|
27
|
+
FOR EACH ROW WHEN NEW.status <> 'active' AND EXISTS (
|
|
28
|
+
SELECT 1 FROM memberships
|
|
29
|
+
WHERE principal_id = OLD.id AND role = 'owner' AND status = 'active'
|
|
30
|
+
)
|
|
31
|
+
BEGIN
|
|
32
|
+
SELECT RAISE(ABORT, 'an active team owner principal cannot be disabled');
|
|
33
|
+
END;
|
|
34
|
+
|
|
35
|
+
CREATE TRIGGER message_recipient_state_is_monotonic
|
|
36
|
+
BEFORE UPDATE OF state ON message_recipients
|
|
37
|
+
FOR EACH ROW WHEN
|
|
38
|
+
(OLD.state = 'available' AND NEW.state NOT IN ('available', 'delivered', 'expired'))
|
|
39
|
+
OR (OLD.state = 'delivered' AND NEW.state <> 'delivered')
|
|
40
|
+
OR (OLD.state = 'expired' AND NEW.state <> 'expired')
|
|
41
|
+
BEGIN
|
|
42
|
+
SELECT RAISE(ABORT, 'message recipient delivery state is monotonic');
|
|
43
|
+
END;
|
|
44
|
+
|
|
45
|
+
CREATE TRIGGER message_receipt_times_are_monotonic
|
|
46
|
+
BEFORE UPDATE ON message_receipts
|
|
47
|
+
FOR EACH ROW WHEN
|
|
48
|
+
(OLD.delivered_at IS NOT NULL AND NEW.delivered_at IS NOT OLD.delivered_at)
|
|
49
|
+
OR (OLD.read_at IS NOT NULL AND NEW.read_at IS NOT OLD.read_at)
|
|
50
|
+
OR (OLD.acknowledged_at IS NOT NULL AND NEW.acknowledged_at IS NOT OLD.acknowledged_at)
|
|
51
|
+
BEGIN
|
|
52
|
+
SELECT RAISE(ABORT, 'message receipt timestamps are monotonic');
|
|
53
|
+
END;
|
|
54
|
+
|
|
55
|
+
CREATE TRIGGER outbox_state_is_forward_only
|
|
56
|
+
BEFORE UPDATE OF state ON outbox_events
|
|
57
|
+
FOR EACH ROW WHEN
|
|
58
|
+
(OLD.state = 'delivered' AND NEW.state <> 'delivered')
|
|
59
|
+
OR (OLD.state = 'dead_letter' AND NEW.state <> 'dead_letter')
|
|
60
|
+
OR (OLD.state = 'pending' AND NEW.state NOT IN ('pending', 'leased', 'dead_letter'))
|
|
61
|
+
OR (OLD.state = 'leased' AND NEW.state NOT IN ('leased', 'pending', 'delivered', 'dead_letter'))
|
|
62
|
+
BEGIN
|
|
63
|
+
SELECT RAISE(ABORT, 'outbox state transition is invalid');
|
|
64
|
+
END;
|
|
65
|
+
|
|
66
|
+
CREATE TABLE bootstrap_claims (
|
|
67
|
+
id TEXT PRIMARY KEY,
|
|
68
|
+
token_hash BLOB NOT NULL UNIQUE
|
|
69
|
+
CHECK(typeof(token_hash) = 'blob' AND length(token_hash) = 32),
|
|
70
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
71
|
+
expires_at INTEGER NOT NULL CHECK(expires_at > created_at),
|
|
72
|
+
consumed_at INTEGER CHECK(consumed_at IS NULL OR consumed_at >= created_at),
|
|
73
|
+
consumed_by_principal_id TEXT REFERENCES human_accounts(principal_id) ON DELETE RESTRICT,
|
|
74
|
+
revoked_at INTEGER CHECK(revoked_at IS NULL OR revoked_at >= created_at),
|
|
75
|
+
CHECK((consumed_at IS NULL) = (consumed_by_principal_id IS NULL)),
|
|
76
|
+
CHECK(consumed_at IS NULL OR revoked_at IS NULL)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
CREATE TRIGGER bootstrap_claim_authority_is_immutable
|
|
80
|
+
BEFORE UPDATE OF id, token_hash, created_at, expires_at ON bootstrap_claims
|
|
81
|
+
BEGIN
|
|
82
|
+
SELECT RAISE(ABORT, 'bootstrap claim authority is immutable');
|
|
83
|
+
END;
|
|
84
|
+
|
|
85
|
+
CREATE TRIGGER bootstrap_claim_consumption_is_one_way
|
|
86
|
+
BEFORE UPDATE ON bootstrap_claims
|
|
87
|
+
FOR EACH ROW WHEN
|
|
88
|
+
(OLD.consumed_at IS NOT NULL AND (
|
|
89
|
+
NEW.consumed_at IS NOT OLD.consumed_at
|
|
90
|
+
OR NEW.consumed_by_principal_id IS NOT OLD.consumed_by_principal_id
|
|
91
|
+
))
|
|
92
|
+
OR (OLD.revoked_at IS NOT NULL AND NEW.revoked_at IS NOT OLD.revoked_at)
|
|
93
|
+
BEGIN
|
|
94
|
+
SELECT RAISE(ABORT, 'bootstrap claim consumption is one-way');
|
|
95
|
+
END;
|
|
96
|
+
|
|
97
|
+
CREATE TRIGGER bootstrap_claims_cannot_be_deleted
|
|
98
|
+
BEFORE DELETE ON bootstrap_claims
|
|
99
|
+
BEGIN
|
|
100
|
+
SELECT RAISE(ABORT, 'bootstrap claim ledger rows cannot be deleted');
|
|
101
|
+
END;
|
|
102
|
+
|
|
103
|
+
CREATE TABLE node_enrollment_bindings (
|
|
104
|
+
grant_id TEXT PRIMARY KEY REFERENCES node_enrollment_grants(id) ON DELETE RESTRICT,
|
|
105
|
+
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,
|
|
106
|
+
expected_server_identity TEXT NOT NULL
|
|
107
|
+
CHECK(length(trim(expected_server_identity)) BETWEEN 8 AND 240),
|
|
108
|
+
expected_display_name TEXT NOT NULL
|
|
109
|
+
CHECK(length(trim(expected_display_name)) BETWEEN 1 AND 160),
|
|
110
|
+
expected_public_material TEXT NOT NULL
|
|
111
|
+
CHECK(length(expected_public_material) BETWEEN 32 AND 16384),
|
|
112
|
+
expected_public_key_fingerprint BLOB NOT NULL
|
|
113
|
+
CHECK(typeof(expected_public_key_fingerprint) = 'blob'
|
|
114
|
+
AND length(expected_public_key_fingerprint) = 32),
|
|
115
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
116
|
+
UNIQUE(team_id, grant_id)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
CREATE TRIGGER node_enrollment_binding_is_immutable
|
|
120
|
+
BEFORE UPDATE ON node_enrollment_bindings
|
|
121
|
+
BEGIN
|
|
122
|
+
SELECT RAISE(ABORT, 'node enrollment binding is immutable');
|
|
123
|
+
END;
|
|
124
|
+
|
|
125
|
+
CREATE TRIGGER node_enrollment_binding_requires_same_team
|
|
126
|
+
BEFORE INSERT ON node_enrollment_bindings
|
|
127
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
128
|
+
SELECT 1 FROM node_enrollment_grants
|
|
129
|
+
WHERE id = NEW.grant_id AND team_id = NEW.team_id
|
|
130
|
+
)
|
|
131
|
+
BEGIN
|
|
132
|
+
SELECT RAISE(ABORT, 'node enrollment binding must match its grant team');
|
|
133
|
+
END;
|
|
134
|
+
|
|
135
|
+
CREATE TRIGGER node_enrollment_bindings_cannot_be_deleted
|
|
136
|
+
BEFORE DELETE ON node_enrollment_bindings
|
|
137
|
+
BEGIN
|
|
138
|
+
SELECT RAISE(ABORT, 'node enrollment binding rows cannot be deleted');
|
|
139
|
+
END;
|
|
140
|
+
|
|
141
|
+
CREATE TABLE node_enrollment_challenges (
|
|
142
|
+
id TEXT PRIMARY KEY,
|
|
143
|
+
grant_id TEXT NOT NULL UNIQUE REFERENCES node_enrollment_grants(id) ON DELETE RESTRICT,
|
|
144
|
+
team_id TEXT NOT NULL,
|
|
145
|
+
public_material TEXT NOT NULL CHECK(length(public_material) BETWEEN 32 AND 16384),
|
|
146
|
+
public_key_fingerprint BLOB NOT NULL
|
|
147
|
+
CHECK(typeof(public_key_fingerprint) = 'blob' AND length(public_key_fingerprint) = 32),
|
|
148
|
+
nonce_hash BLOB NOT NULL UNIQUE
|
|
149
|
+
CHECK(typeof(nonce_hash) = 'blob' AND length(nonce_hash) = 32),
|
|
150
|
+
signing_payload TEXT NOT NULL CHECK(length(signing_payload) BETWEEN 64 AND 4096),
|
|
151
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
152
|
+
expires_at INTEGER NOT NULL CHECK(expires_at > created_at),
|
|
153
|
+
consumed_at INTEGER CHECK(consumed_at IS NULL OR consumed_at >= created_at),
|
|
154
|
+
revoked_at INTEGER CHECK(revoked_at IS NULL OR revoked_at >= created_at),
|
|
155
|
+
FOREIGN KEY(team_id, grant_id)
|
|
156
|
+
REFERENCES node_enrollment_bindings(team_id, grant_id) ON DELETE RESTRICT,
|
|
157
|
+
CHECK(consumed_at IS NULL OR revoked_at IS NULL)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
CREATE TRIGGER node_enrollment_challenge_authority_is_immutable
|
|
161
|
+
BEFORE UPDATE OF id, grant_id, team_id, public_material,
|
|
162
|
+
public_key_fingerprint, nonce_hash, signing_payload, created_at, expires_at
|
|
163
|
+
ON node_enrollment_challenges
|
|
164
|
+
BEGIN
|
|
165
|
+
SELECT RAISE(ABORT, 'node enrollment challenge authority is immutable');
|
|
166
|
+
END;
|
|
167
|
+
|
|
168
|
+
CREATE TRIGGER node_enrollment_challenge_terminal_state_is_one_way
|
|
169
|
+
BEFORE UPDATE ON node_enrollment_challenges
|
|
170
|
+
FOR EACH ROW WHEN
|
|
171
|
+
(OLD.consumed_at IS NOT NULL AND NEW.consumed_at IS NOT OLD.consumed_at)
|
|
172
|
+
OR (OLD.revoked_at IS NOT NULL AND NEW.revoked_at IS NOT OLD.revoked_at)
|
|
173
|
+
BEGIN
|
|
174
|
+
SELECT RAISE(ABORT, 'node enrollment challenge terminal state is one-way');
|
|
175
|
+
END;
|
|
176
|
+
|
|
177
|
+
CREATE TRIGGER node_enrollment_challenges_cannot_be_deleted
|
|
178
|
+
BEFORE DELETE ON node_enrollment_challenges
|
|
179
|
+
BEGIN
|
|
180
|
+
SELECT RAISE(ABORT, 'node enrollment challenge rows cannot be deleted');
|
|
181
|
+
END;
|
|
182
|
+
|
|
183
|
+
CREATE TABLE request_idempotency (
|
|
184
|
+
id TEXT PRIMARY KEY,
|
|
185
|
+
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,
|
|
186
|
+
principal_id TEXT NOT NULL REFERENCES principals(id) ON DELETE RESTRICT,
|
|
187
|
+
operation TEXT NOT NULL CHECK(length(operation) BETWEEN 1 AND 120),
|
|
188
|
+
key_hash BLOB NOT NULL CHECK(typeof(key_hash) = 'blob' AND length(key_hash) = 32),
|
|
189
|
+
request_fingerprint BLOB NOT NULL
|
|
190
|
+
CHECK(typeof(request_fingerprint) = 'blob' AND length(request_fingerprint) = 32),
|
|
191
|
+
resource_type TEXT NOT NULL CHECK(length(resource_type) BETWEEN 1 AND 80),
|
|
192
|
+
resource_id TEXT NOT NULL CHECK(length(resource_id) BETWEEN 1 AND 240),
|
|
193
|
+
response_json TEXT NOT NULL CHECK(json_valid(response_json) AND json_type(response_json) = 'object'),
|
|
194
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
195
|
+
UNIQUE(team_id, principal_id, operation, key_hash)
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
CREATE TRIGGER request_idempotency_is_immutable_update
|
|
199
|
+
BEFORE UPDATE ON request_idempotency
|
|
200
|
+
BEGIN
|
|
201
|
+
SELECT RAISE(ABORT, 'idempotency records are immutable');
|
|
202
|
+
END;
|
|
203
|
+
|
|
204
|
+
CREATE TRIGGER request_idempotency_is_immutable_delete
|
|
205
|
+
BEFORE DELETE ON request_idempotency
|
|
206
|
+
BEGIN
|
|
207
|
+
SELECT RAISE(ABORT, 'idempotency records are immutable');
|
|
208
|
+
END;
|
|
209
|
+
|
|
210
|
+
CREATE TRIGGER request_idempotency_requires_team_principal
|
|
211
|
+
BEFORE INSERT ON request_idempotency
|
|
212
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
213
|
+
SELECT 1 FROM principals AS p
|
|
214
|
+
WHERE p.id = NEW.principal_id
|
|
215
|
+
AND p.status = 'active'
|
|
216
|
+
AND (
|
|
217
|
+
p.scope_team_id = NEW.team_id
|
|
218
|
+
OR EXISTS (
|
|
219
|
+
SELECT 1 FROM memberships AS m
|
|
220
|
+
WHERE m.team_id = NEW.team_id
|
|
221
|
+
AND m.principal_id = p.id
|
|
222
|
+
AND m.status = 'active'
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
BEGIN
|
|
227
|
+
SELECT RAISE(ABORT, 'idempotency principal does not belong to its team');
|
|
228
|
+
END;
|
|
229
|
+
|
|
230
|
+
CREATE TRIGGER message_receipt_times_are_ordered_insert
|
|
231
|
+
BEFORE INSERT ON message_receipts
|
|
232
|
+
FOR EACH ROW WHEN
|
|
233
|
+
(NEW.read_at IS NOT NULL AND NEW.read_at < NEW.delivered_at)
|
|
234
|
+
OR (NEW.acknowledged_at IS NOT NULL AND NEW.acknowledged_at < NEW.delivered_at)
|
|
235
|
+
BEGIN
|
|
236
|
+
SELECT RAISE(ABORT, 'message receipt timestamps are out of order');
|
|
237
|
+
END;
|
|
238
|
+
|
|
239
|
+
CREATE TRIGGER message_receipt_times_are_ordered_update
|
|
240
|
+
BEFORE UPDATE ON message_receipts
|
|
241
|
+
FOR EACH ROW WHEN
|
|
242
|
+
(NEW.read_at IS NOT NULL AND NEW.read_at < NEW.delivered_at)
|
|
243
|
+
OR (NEW.acknowledged_at IS NOT NULL AND NEW.acknowledged_at < NEW.delivered_at)
|
|
244
|
+
BEGIN
|
|
245
|
+
SELECT RAISE(ABORT, 'message receipt timestamps are out of order');
|
|
246
|
+
END;
|
|
247
|
+
|
|
248
|
+
CREATE TRIGGER outbox_row_shape_is_valid_insert
|
|
249
|
+
BEFORE INSERT ON outbox_events
|
|
250
|
+
FOR EACH ROW WHEN
|
|
251
|
+
(NEW.state <> 'leased' AND (NEW.lease_owner IS NOT NULL OR NEW.lease_expires_at IS NOT NULL))
|
|
252
|
+
OR (NEW.state = 'delivered' AND NEW.delivered_at IS NULL)
|
|
253
|
+
OR (NEW.state <> 'delivered' AND NEW.delivered_at IS NOT NULL)
|
|
254
|
+
BEGIN
|
|
255
|
+
SELECT RAISE(ABORT, 'outbox row shape is invalid');
|
|
256
|
+
END;
|
|
257
|
+
|
|
258
|
+
CREATE TRIGGER outbox_row_shape_is_valid_update
|
|
259
|
+
BEFORE UPDATE ON outbox_events
|
|
260
|
+
FOR EACH ROW WHEN
|
|
261
|
+
(NEW.state <> 'leased' AND (NEW.lease_owner IS NOT NULL OR NEW.lease_expires_at IS NOT NULL))
|
|
262
|
+
OR (NEW.state = 'delivered' AND NEW.delivered_at IS NULL)
|
|
263
|
+
OR (NEW.state <> 'delivered' AND NEW.delivered_at IS NOT NULL)
|
|
264
|
+
BEGIN
|
|
265
|
+
SELECT RAISE(ABORT, 'outbox row shape is invalid');
|
|
266
|
+
END;
|
|
267
|
+
|
|
268
|
+
CREATE TABLE hub_metadata (
|
|
269
|
+
singleton INTEGER PRIMARY KEY CHECK(singleton = 1),
|
|
270
|
+
hub_id TEXT NOT NULL UNIQUE CHECK(length(hub_id) BETWEEN 12 AND 80),
|
|
271
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0)
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
CREATE TRIGGER hub_metadata_is_immutable_update
|
|
275
|
+
BEFORE UPDATE ON hub_metadata
|
|
276
|
+
BEGIN
|
|
277
|
+
SELECT RAISE(ABORT, 'Hub identity is immutable');
|
|
278
|
+
END;
|
|
279
|
+
|
|
280
|
+
CREATE TRIGGER hub_metadata_is_immutable_delete
|
|
281
|
+
BEFORE DELETE ON hub_metadata
|
|
282
|
+
BEGIN
|
|
283
|
+
SELECT RAISE(ABORT, 'Hub identity is immutable');
|
|
284
|
+
END;
|
|
285
|
+
|
|
286
|
+
CREATE TABLE owner_recovery_claims (
|
|
287
|
+
id TEXT PRIMARY KEY,
|
|
288
|
+
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,
|
|
289
|
+
owner_principal_id TEXT NOT NULL,
|
|
290
|
+
token_hash BLOB NOT NULL UNIQUE
|
|
291
|
+
CHECK(typeof(token_hash) = 'blob' AND length(token_hash) = 32),
|
|
292
|
+
device_label TEXT NOT NULL CHECK(length(trim(device_label)) BETWEEN 1 AND 160),
|
|
293
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
294
|
+
expires_at INTEGER NOT NULL CHECK(expires_at > created_at),
|
|
295
|
+
consumed_at INTEGER CHECK(consumed_at IS NULL OR consumed_at >= created_at),
|
|
296
|
+
consumed_by_session_id TEXT REFERENCES device_sessions(id) ON DELETE RESTRICT,
|
|
297
|
+
revoked_at INTEGER CHECK(revoked_at IS NULL OR revoked_at >= created_at),
|
|
298
|
+
FOREIGN KEY(team_id, owner_principal_id)
|
|
299
|
+
REFERENCES memberships(team_id, principal_id) ON DELETE RESTRICT,
|
|
300
|
+
CHECK((consumed_at IS NULL) = (consumed_by_session_id IS NULL)),
|
|
301
|
+
CHECK(consumed_at IS NULL OR revoked_at IS NULL)
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
CREATE TRIGGER owner_recovery_claim_authority_is_immutable
|
|
305
|
+
BEFORE UPDATE OF id, team_id, owner_principal_id, token_hash,
|
|
306
|
+
device_label, created_at, expires_at ON owner_recovery_claims
|
|
307
|
+
BEGIN
|
|
308
|
+
SELECT RAISE(ABORT, 'owner recovery claim authority is immutable');
|
|
309
|
+
END;
|
|
310
|
+
|
|
311
|
+
CREATE TRIGGER owner_recovery_claim_terminal_state_is_one_way
|
|
312
|
+
BEFORE UPDATE ON owner_recovery_claims
|
|
313
|
+
FOR EACH ROW WHEN
|
|
314
|
+
(OLD.consumed_at IS NOT NULL AND (
|
|
315
|
+
NEW.consumed_at IS NOT OLD.consumed_at
|
|
316
|
+
OR NEW.consumed_by_session_id IS NOT OLD.consumed_by_session_id
|
|
317
|
+
))
|
|
318
|
+
OR (OLD.revoked_at IS NOT NULL AND NEW.revoked_at IS NOT OLD.revoked_at)
|
|
319
|
+
BEGIN
|
|
320
|
+
SELECT RAISE(ABORT, 'owner recovery claim terminal state is one-way');
|
|
321
|
+
END;
|
|
322
|
+
|
|
323
|
+
CREATE TRIGGER owner_recovery_claims_cannot_be_deleted
|
|
324
|
+
BEFORE DELETE ON owner_recovery_claims
|
|
325
|
+
BEGIN
|
|
326
|
+
SELECT RAISE(ABORT, 'owner recovery claim ledger rows cannot be deleted');
|
|
327
|
+
END;
|
|
328
|
+
|
|
329
|
+
CREATE TABLE audit_chain_heads (
|
|
330
|
+
team_id TEXT PRIMARY KEY REFERENCES teams(id) ON DELETE RESTRICT,
|
|
331
|
+
event_id TEXT NOT NULL REFERENCES audit_events(id) ON DELETE RESTRICT,
|
|
332
|
+
event_hash BLOB NOT NULL
|
|
333
|
+
CHECK(typeof(event_hash) = 'blob' AND length(event_hash) = 32),
|
|
334
|
+
sequence INTEGER NOT NULL CHECK(sequence >= 1),
|
|
335
|
+
updated_at INTEGER NOT NULL CHECK(updated_at >= 0)
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
CREATE TRIGGER audit_chain_head_requires_team_event_insert
|
|
339
|
+
BEFORE INSERT ON audit_chain_heads
|
|
340
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
341
|
+
SELECT 1 FROM audit_events WHERE id = NEW.event_id AND team_id = NEW.team_id
|
|
342
|
+
)
|
|
343
|
+
BEGIN
|
|
344
|
+
SELECT RAISE(ABORT, 'audit chain head event must belong to its team');
|
|
345
|
+
END;
|
|
346
|
+
|
|
347
|
+
CREATE TRIGGER audit_chain_head_requires_team_event_update
|
|
348
|
+
BEFORE UPDATE OF event_id ON audit_chain_heads
|
|
349
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
350
|
+
SELECT 1 FROM audit_events WHERE id = NEW.event_id AND team_id = NEW.team_id
|
|
351
|
+
)
|
|
352
|
+
BEGIN
|
|
353
|
+
SELECT RAISE(ABORT, 'audit chain head event must belong to its team');
|
|
354
|
+
END;
|
|
355
|
+
|
|
356
|
+
CREATE TRIGGER audit_chain_head_is_monotonic
|
|
357
|
+
BEFORE UPDATE ON audit_chain_heads
|
|
358
|
+
FOR EACH ROW WHEN NEW.sequence <> OLD.sequence + 1 OR NEW.updated_at < OLD.updated_at
|
|
359
|
+
BEGIN
|
|
360
|
+
SELECT RAISE(ABORT, 'audit chain head must advance exactly once');
|
|
361
|
+
END;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Bind an embedded Hub database to exactly one stable AgentsServer identity.
|
|
2
|
+
-- Standalone Hubs remain unbound until their first managed adoption.
|
|
3
|
+
|
|
4
|
+
CREATE TABLE managed_host_bindings (
|
|
5
|
+
singleton INTEGER PRIMARY KEY CHECK(singleton = 1),
|
|
6
|
+
hub_id TEXT NOT NULL UNIQUE REFERENCES hub_metadata(hub_id) ON DELETE RESTRICT,
|
|
7
|
+
server_identity TEXT NOT NULL UNIQUE
|
|
8
|
+
CHECK(length(trim(server_identity)) BETWEEN 8 AND 240),
|
|
9
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
CREATE TRIGGER managed_host_binding_is_immutable_update
|
|
13
|
+
BEFORE UPDATE ON managed_host_bindings
|
|
14
|
+
BEGIN
|
|
15
|
+
SELECT RAISE(ABORT, 'managed Hub host binding is immutable');
|
|
16
|
+
END;
|
|
17
|
+
|
|
18
|
+
CREATE TRIGGER managed_host_binding_is_immutable_delete
|
|
19
|
+
BEFORE DELETE ON managed_host_bindings
|
|
20
|
+
BEGIN
|
|
21
|
+
SELECT RAISE(ABORT, 'managed Hub host binding is immutable');
|
|
22
|
+
END;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- Bind first-owner bootstrap proofs issued through the parent AgentsServer to
|
|
2
|
+
-- one verified Tailscale Serve identity and one live managed-server instance.
|
|
3
|
+
-- The proof itself is never stored; bootstrap_claims retains only its digest.
|
|
4
|
+
|
|
5
|
+
CREATE TABLE bootstrap_delegations (
|
|
6
|
+
bootstrap_claim_id TEXT PRIMARY KEY
|
|
7
|
+
REFERENCES bootstrap_claims(id) ON DELETE RESTRICT,
|
|
8
|
+
request_id TEXT NOT NULL UNIQUE
|
|
9
|
+
CHECK(length(request_id) = 36),
|
|
10
|
+
request_fingerprint BLOB NOT NULL
|
|
11
|
+
CHECK(typeof(request_fingerprint) = 'blob'
|
|
12
|
+
AND length(request_fingerprint) = 32),
|
|
13
|
+
server_identity TEXT NOT NULL
|
|
14
|
+
CHECK(length(trim(server_identity)) BETWEEN 8 AND 240),
|
|
15
|
+
server_instance_id TEXT NOT NULL
|
|
16
|
+
CHECK(length(trim(server_instance_id)) BETWEEN 8 AND 240),
|
|
17
|
+
hub_id TEXT NOT NULL REFERENCES hub_metadata(hub_id) ON DELETE RESTRICT,
|
|
18
|
+
hub_url TEXT NOT NULL
|
|
19
|
+
CHECK(length(hub_url) BETWEEN 16 AND 2048),
|
|
20
|
+
tailnet_login_normalized TEXT NOT NULL
|
|
21
|
+
CHECK(length(tailnet_login_normalized) BETWEEN 3 AND 320),
|
|
22
|
+
recipient_email_normalized TEXT NOT NULL
|
|
23
|
+
CHECK(length(recipient_email_normalized) BETWEEN 3 AND 320),
|
|
24
|
+
display_name TEXT NOT NULL
|
|
25
|
+
CHECK(length(trim(display_name)) BETWEEN 1 AND 160),
|
|
26
|
+
device_label TEXT NOT NULL
|
|
27
|
+
CHECK(length(trim(device_label)) BETWEEN 1 AND 160),
|
|
28
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
29
|
+
expires_at INTEGER NOT NULL CHECK(expires_at > created_at)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TRIGGER bootstrap_delegation_is_immutable
|
|
33
|
+
BEFORE UPDATE ON bootstrap_delegations
|
|
34
|
+
BEGIN
|
|
35
|
+
SELECT RAISE(ABORT, 'bootstrap delegation authority is immutable');
|
|
36
|
+
END;
|
|
37
|
+
|
|
38
|
+
CREATE TRIGGER bootstrap_delegations_cannot_be_deleted
|
|
39
|
+
BEFORE DELETE ON bootstrap_delegations
|
|
40
|
+
BEGIN
|
|
41
|
+
SELECT RAISE(ABORT, 'bootstrap delegation ledger rows cannot be deleted');
|
|
42
|
+
END;
|
|
43
|
+
|
|
44
|
+
CREATE TRIGGER bootstrap_delegation_matches_claim_expiry
|
|
45
|
+
BEFORE INSERT ON bootstrap_delegations
|
|
46
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
47
|
+
SELECT 1 FROM bootstrap_claims
|
|
48
|
+
WHERE id = NEW.bootstrap_claim_id
|
|
49
|
+
AND created_at = NEW.created_at
|
|
50
|
+
AND expires_at = NEW.expires_at
|
|
51
|
+
)
|
|
52
|
+
BEGIN
|
|
53
|
+
SELECT RAISE(ABORT, 'bootstrap delegation must match its claim lifetime');
|
|
54
|
+
END;
|
|
55
|
+
|
|
56
|
+
CREATE TRIGGER bootstrap_delegation_matches_hub
|
|
57
|
+
BEFORE INSERT ON bootstrap_delegations
|
|
58
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
59
|
+
SELECT 1 FROM hub_metadata WHERE hub_id = NEW.hub_id
|
|
60
|
+
)
|
|
61
|
+
BEGIN
|
|
62
|
+
SELECT RAISE(ABORT, 'bootstrap delegation must match its Hub');
|
|
63
|
+
END;
|