@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,423 @@
|
|
|
1
|
+
-- Passive Team Networks foundation. The existing channel ledger remains the
|
|
2
|
+
-- storage engine for one reserved Bulletin per team; addressed mailbox items
|
|
3
|
+
-- are deliberately separate from channels and from dispatch_requests.
|
|
4
|
+
|
|
5
|
+
CREATE TRIGGER network_agents_limit_per_server
|
|
6
|
+
BEFORE INSERT ON agents
|
|
7
|
+
FOR EACH ROW WHEN (
|
|
8
|
+
SELECT COUNT(*) FROM agents WHERE node_id = NEW.node_id
|
|
9
|
+
) >= 256
|
|
10
|
+
BEGIN
|
|
11
|
+
SELECT RAISE(ABORT, 'network server agent limit exceeded');
|
|
12
|
+
END;
|
|
13
|
+
|
|
14
|
+
CREATE TABLE network_peer_bindings (
|
|
15
|
+
peer_id TEXT PRIMARY KEY CHECK(length(peer_id) = 36),
|
|
16
|
+
team_id TEXT NOT NULL,
|
|
17
|
+
node_id TEXT NOT NULL,
|
|
18
|
+
service_principal_id TEXT NOT NULL REFERENCES principals(id) ON DELETE RESTRICT,
|
|
19
|
+
peer_server_identity TEXT NOT NULL CHECK(length(trim(peer_server_identity)) BETWEEN 8 AND 240),
|
|
20
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'revoked')),
|
|
21
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
22
|
+
revoked_at INTEGER CHECK(revoked_at IS NULL OR revoked_at >= created_at),
|
|
23
|
+
FOREIGN KEY(team_id, node_id) REFERENCES nodes(team_id, id) ON DELETE RESTRICT,
|
|
24
|
+
UNIQUE(team_id, peer_id)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE UNIQUE INDEX network_one_active_peer_per_server
|
|
28
|
+
ON network_peer_bindings(team_id, node_id)
|
|
29
|
+
WHERE status = 'active';
|
|
30
|
+
|
|
31
|
+
CREATE TRIGGER network_peer_binding_requires_exact_identity
|
|
32
|
+
BEFORE INSERT ON network_peer_bindings
|
|
33
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
34
|
+
SELECT 1
|
|
35
|
+
FROM nodes AS n
|
|
36
|
+
JOIN principals AS p ON p.id = NEW.service_principal_id
|
|
37
|
+
JOIN service_accounts AS s ON s.principal_id = p.id
|
|
38
|
+
JOIN memberships AS m
|
|
39
|
+
ON m.team_id = NEW.team_id AND m.principal_id = p.id
|
|
40
|
+
WHERE n.team_id = NEW.team_id
|
|
41
|
+
AND n.id = NEW.node_id
|
|
42
|
+
AND n.server_identity = NEW.peer_server_identity
|
|
43
|
+
AND n.status <> 'revoked'
|
|
44
|
+
AND p.kind = 'service'
|
|
45
|
+
AND p.status = 'active'
|
|
46
|
+
AND s.service_identifier = 'agentsdock.secure-peer.' || NEW.peer_id
|
|
47
|
+
AND m.role = 'automation'
|
|
48
|
+
AND m.status = 'active'
|
|
49
|
+
)
|
|
50
|
+
BEGIN
|
|
51
|
+
SELECT RAISE(ABORT, 'network peer binding requires exact server authority');
|
|
52
|
+
END;
|
|
53
|
+
|
|
54
|
+
CREATE TRIGGER network_peer_binding_identity_is_immutable
|
|
55
|
+
BEFORE UPDATE OF peer_id, team_id, node_id, service_principal_id,
|
|
56
|
+
peer_server_identity, created_at
|
|
57
|
+
ON network_peer_bindings
|
|
58
|
+
BEGIN
|
|
59
|
+
SELECT RAISE(ABORT, 'network peer binding identity is immutable');
|
|
60
|
+
END;
|
|
61
|
+
|
|
62
|
+
CREATE TRIGGER network_peer_binding_revocation_is_terminal
|
|
63
|
+
BEFORE UPDATE ON network_peer_bindings
|
|
64
|
+
FOR EACH ROW WHEN
|
|
65
|
+
(OLD.status = 'revoked' AND NEW.status <> 'revoked')
|
|
66
|
+
OR (OLD.revoked_at IS NOT NULL AND NEW.revoked_at IS NOT OLD.revoked_at)
|
|
67
|
+
OR (NEW.status = 'revoked' AND NEW.revoked_at IS NULL)
|
|
68
|
+
OR (NEW.status = 'active' AND NEW.revoked_at IS NOT NULL)
|
|
69
|
+
BEGIN
|
|
70
|
+
SELECT RAISE(ABORT, 'network peer revocation is terminal');
|
|
71
|
+
END;
|
|
72
|
+
|
|
73
|
+
CREATE TABLE network_boards (
|
|
74
|
+
team_id TEXT PRIMARY KEY REFERENCES teams(id) ON DELETE CASCADE,
|
|
75
|
+
channel_id TEXT NOT NULL UNIQUE,
|
|
76
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
77
|
+
FOREIGN KEY(team_id, channel_id) REFERENCES channels(team_id, id) ON DELETE RESTRICT
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
CREATE TRIGGER network_board_requires_shared_board
|
|
81
|
+
BEFORE INSERT ON network_boards
|
|
82
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
83
|
+
SELECT 1 FROM channels AS c
|
|
84
|
+
WHERE c.team_id = NEW.team_id
|
|
85
|
+
AND c.id = NEW.channel_id
|
|
86
|
+
AND c.kind = 'board'
|
|
87
|
+
AND c.visibility = 'team'
|
|
88
|
+
AND c.archived_at IS NULL
|
|
89
|
+
)
|
|
90
|
+
BEGIN
|
|
91
|
+
SELECT RAISE(ABORT, 'network Bulletin requires one live shared board');
|
|
92
|
+
END;
|
|
93
|
+
|
|
94
|
+
CREATE TRIGGER network_board_binding_replacement_is_guarded
|
|
95
|
+
BEFORE UPDATE ON network_boards
|
|
96
|
+
FOR EACH ROW WHEN
|
|
97
|
+
NEW.team_id IS NOT OLD.team_id
|
|
98
|
+
OR NEW.created_at IS NOT OLD.created_at
|
|
99
|
+
OR (
|
|
100
|
+
NEW.channel_id IS NOT OLD.channel_id
|
|
101
|
+
AND (
|
|
102
|
+
EXISTS (
|
|
103
|
+
SELECT 1 FROM channels AS old_channel
|
|
104
|
+
WHERE old_channel.team_id = OLD.team_id
|
|
105
|
+
AND old_channel.id = OLD.channel_id
|
|
106
|
+
AND old_channel.kind = 'board'
|
|
107
|
+
AND old_channel.visibility = 'team'
|
|
108
|
+
AND old_channel.archived_at IS NULL
|
|
109
|
+
)
|
|
110
|
+
OR NOT EXISTS (
|
|
111
|
+
SELECT 1 FROM channels AS new_channel
|
|
112
|
+
WHERE new_channel.team_id = NEW.team_id
|
|
113
|
+
AND new_channel.id = NEW.channel_id
|
|
114
|
+
AND new_channel.kind = 'board'
|
|
115
|
+
AND new_channel.visibility = 'team'
|
|
116
|
+
AND new_channel.archived_at IS NULL
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
BEGIN
|
|
121
|
+
SELECT RAISE(ABORT, 'network Bulletin binding replacement is not permitted');
|
|
122
|
+
END;
|
|
123
|
+
|
|
124
|
+
CREATE TRIGGER network_bulletin_body_limit_on_insert
|
|
125
|
+
BEFORE INSERT ON messages
|
|
126
|
+
FOR EACH ROW WHEN EXISTS (
|
|
127
|
+
SELECT 1 FROM network_boards AS b
|
|
128
|
+
WHERE b.team_id = NEW.team_id AND b.channel_id = NEW.channel_id
|
|
129
|
+
) AND NOT length(CAST(NEW.body AS BLOB)) BETWEEN 1 AND 8192
|
|
130
|
+
BEGIN
|
|
131
|
+
SELECT RAISE(ABORT, 'network Bulletin body exceeds its byte limit');
|
|
132
|
+
END;
|
|
133
|
+
|
|
134
|
+
CREATE TRIGGER network_bulletin_body_limit_on_update
|
|
135
|
+
BEFORE UPDATE OF body ON messages
|
|
136
|
+
FOR EACH ROW WHEN EXISTS (
|
|
137
|
+
SELECT 1 FROM network_boards AS b
|
|
138
|
+
WHERE b.team_id = NEW.team_id AND b.channel_id = NEW.channel_id
|
|
139
|
+
) AND NOT length(CAST(NEW.body AS BLOB)) BETWEEN 1 AND 8192
|
|
140
|
+
BEGIN
|
|
141
|
+
SELECT RAISE(ABORT, 'network Bulletin body exceeds its byte limit');
|
|
142
|
+
END;
|
|
143
|
+
|
|
144
|
+
CREATE TABLE network_mailbox_items (
|
|
145
|
+
queue_ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
146
|
+
id TEXT NOT NULL UNIQUE,
|
|
147
|
+
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,
|
|
148
|
+
kind TEXT NOT NULL CHECK(kind IN ('message', 'request', 'reply')),
|
|
149
|
+
sender_kind TEXT NOT NULL CHECK(sender_kind IN ('human', 'server', 'agent')),
|
|
150
|
+
sender_principal_id TEXT NOT NULL REFERENCES principals(id) ON DELETE RESTRICT,
|
|
151
|
+
sender_node_id TEXT,
|
|
152
|
+
sender_agent_id TEXT,
|
|
153
|
+
recipient_kind TEXT NOT NULL CHECK(recipient_kind IN ('human', 'server', 'agent')),
|
|
154
|
+
recipient_principal_id TEXT NOT NULL REFERENCES principals(id) ON DELETE RESTRICT,
|
|
155
|
+
recipient_node_id TEXT,
|
|
156
|
+
recipient_agent_id TEXT,
|
|
157
|
+
root_request_item_id TEXT,
|
|
158
|
+
body_format TEXT NOT NULL DEFAULT 'markdown' CHECK(body_format IN ('plain', 'markdown')),
|
|
159
|
+
body TEXT NOT NULL CHECK(length(CAST(body AS BLOB)) BETWEEN 1 AND 8192),
|
|
160
|
+
idempotency_key BLOB NOT NULL
|
|
161
|
+
CHECK(typeof(idempotency_key) = 'blob' AND length(idempotency_key) = 32),
|
|
162
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
163
|
+
expires_at INTEGER CHECK(expires_at IS NULL OR expires_at > created_at),
|
|
164
|
+
FOREIGN KEY(team_id, sender_node_id) REFERENCES nodes(team_id, id) ON DELETE RESTRICT,
|
|
165
|
+
FOREIGN KEY(team_id, sender_agent_id) REFERENCES agents(team_id, id) ON DELETE RESTRICT,
|
|
166
|
+
FOREIGN KEY(team_id, recipient_node_id) REFERENCES nodes(team_id, id) ON DELETE RESTRICT,
|
|
167
|
+
FOREIGN KEY(team_id, recipient_agent_id) REFERENCES agents(team_id, id) ON DELETE RESTRICT,
|
|
168
|
+
FOREIGN KEY(team_id, root_request_item_id)
|
|
169
|
+
REFERENCES network_mailbox_items(team_id, id) ON DELETE RESTRICT,
|
|
170
|
+
UNIQUE(team_id, id),
|
|
171
|
+
UNIQUE(team_id, idempotency_key),
|
|
172
|
+
CHECK(
|
|
173
|
+
(sender_kind = 'human' AND sender_node_id IS NULL AND sender_agent_id IS NULL)
|
|
174
|
+
OR (sender_kind = 'server' AND sender_node_id IS NOT NULL AND sender_agent_id IS NULL)
|
|
175
|
+
OR (sender_kind = 'agent' AND sender_node_id IS NOT NULL AND sender_agent_id IS NOT NULL)
|
|
176
|
+
),
|
|
177
|
+
CHECK(
|
|
178
|
+
(recipient_kind = 'human' AND recipient_node_id IS NULL AND recipient_agent_id IS NULL)
|
|
179
|
+
OR (recipient_kind = 'server' AND recipient_node_id IS NOT NULL AND recipient_agent_id IS NULL)
|
|
180
|
+
OR (recipient_kind = 'agent' AND recipient_node_id IS NOT NULL AND recipient_agent_id IS NOT NULL)
|
|
181
|
+
),
|
|
182
|
+
CHECK(
|
|
183
|
+
(kind IN ('message', 'request') AND root_request_item_id IS NULL)
|
|
184
|
+
OR (kind = 'reply' AND root_request_item_id IS NOT NULL)
|
|
185
|
+
),
|
|
186
|
+
CHECK(kind = 'request' OR expires_at IS NULL)
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
CREATE UNIQUE INDEX network_one_reply_per_request
|
|
190
|
+
ON network_mailbox_items(root_request_item_id)
|
|
191
|
+
WHERE kind = 'reply';
|
|
192
|
+
|
|
193
|
+
CREATE INDEX network_mailbox_recipient_fifo
|
|
194
|
+
ON network_mailbox_items(
|
|
195
|
+
team_id, recipient_kind, recipient_node_id, recipient_agent_id, queue_ordinal
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
CREATE TRIGGER network_mailbox_sender_is_authorized
|
|
199
|
+
BEFORE INSERT ON network_mailbox_items
|
|
200
|
+
FOR EACH ROW WHEN NOT (
|
|
201
|
+
(
|
|
202
|
+
NEW.sender_kind = 'human'
|
|
203
|
+
AND EXISTS (
|
|
204
|
+
SELECT 1
|
|
205
|
+
FROM principals AS p
|
|
206
|
+
JOIN memberships AS m
|
|
207
|
+
ON m.team_id = NEW.team_id AND m.principal_id = p.id
|
|
208
|
+
WHERE p.id = NEW.sender_principal_id
|
|
209
|
+
AND p.kind = 'human'
|
|
210
|
+
AND p.status = 'active'
|
|
211
|
+
AND m.status = 'active'
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
OR (
|
|
215
|
+
NEW.sender_kind IN ('server', 'agent')
|
|
216
|
+
AND EXISTS (
|
|
217
|
+
SELECT 1
|
|
218
|
+
FROM network_peer_bindings AS b
|
|
219
|
+
JOIN nodes AS n
|
|
220
|
+
ON n.team_id = b.team_id AND n.id = b.node_id
|
|
221
|
+
WHERE b.team_id = NEW.team_id
|
|
222
|
+
AND b.node_id = NEW.sender_node_id
|
|
223
|
+
AND b.service_principal_id = NEW.sender_principal_id
|
|
224
|
+
AND b.status = 'active'
|
|
225
|
+
AND n.status = 'active'
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
BEGIN
|
|
230
|
+
SELECT RAISE(ABORT, 'network mailbox sender is not authorized');
|
|
231
|
+
END;
|
|
232
|
+
|
|
233
|
+
CREATE TRIGGER network_mailbox_sender_agent_matches_server
|
|
234
|
+
BEFORE INSERT ON network_mailbox_items
|
|
235
|
+
FOR EACH ROW WHEN NEW.sender_kind = 'agent' AND NOT EXISTS (
|
|
236
|
+
SELECT 1 FROM agents AS a
|
|
237
|
+
JOIN principals AS p ON p.id = a.principal_id
|
|
238
|
+
WHERE a.team_id = NEW.team_id
|
|
239
|
+
AND a.node_id = NEW.sender_node_id
|
|
240
|
+
AND a.id = NEW.sender_agent_id
|
|
241
|
+
AND a.status = 'active'
|
|
242
|
+
AND p.status = 'active'
|
|
243
|
+
)
|
|
244
|
+
BEGIN
|
|
245
|
+
SELECT RAISE(ABORT, 'network mailbox sender agent does not belong to server');
|
|
246
|
+
END;
|
|
247
|
+
|
|
248
|
+
CREATE TRIGGER network_mailbox_recipient_is_available
|
|
249
|
+
BEFORE INSERT ON network_mailbox_items
|
|
250
|
+
FOR EACH ROW WHEN NOT (
|
|
251
|
+
(
|
|
252
|
+
NEW.recipient_kind = 'human'
|
|
253
|
+
AND EXISTS (
|
|
254
|
+
SELECT 1 FROM principals AS p
|
|
255
|
+
JOIN memberships AS m
|
|
256
|
+
ON m.team_id = NEW.team_id AND m.principal_id = p.id
|
|
257
|
+
WHERE p.id = NEW.recipient_principal_id
|
|
258
|
+
AND p.kind = 'human'
|
|
259
|
+
AND p.status = 'active'
|
|
260
|
+
AND m.status = 'active'
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
OR (
|
|
264
|
+
NEW.recipient_kind = 'server'
|
|
265
|
+
AND EXISTS (
|
|
266
|
+
SELECT 1 FROM nodes AS n
|
|
267
|
+
WHERE n.team_id = NEW.team_id
|
|
268
|
+
AND n.id = NEW.recipient_node_id
|
|
269
|
+
AND n.principal_id = NEW.recipient_principal_id
|
|
270
|
+
AND n.status = 'active'
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
OR (
|
|
274
|
+
NEW.recipient_kind = 'agent'
|
|
275
|
+
AND EXISTS (
|
|
276
|
+
SELECT 1 FROM agents AS a
|
|
277
|
+
JOIN principals AS p ON p.id = a.principal_id
|
|
278
|
+
JOIN nodes AS n ON n.team_id = a.team_id AND n.id = a.node_id
|
|
279
|
+
WHERE a.team_id = NEW.team_id
|
|
280
|
+
AND a.node_id = NEW.recipient_node_id
|
|
281
|
+
AND a.id = NEW.recipient_agent_id
|
|
282
|
+
AND a.principal_id = NEW.recipient_principal_id
|
|
283
|
+
AND a.status = 'active'
|
|
284
|
+
AND p.status = 'active'
|
|
285
|
+
AND n.status = 'active'
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
)
|
|
289
|
+
BEGIN
|
|
290
|
+
SELECT RAISE(ABORT, 'network mailbox recipient is unavailable');
|
|
291
|
+
END;
|
|
292
|
+
|
|
293
|
+
CREATE TRIGGER network_mailbox_reply_requires_request
|
|
294
|
+
BEFORE INSERT ON network_mailbox_items
|
|
295
|
+
FOR EACH ROW WHEN NEW.kind = 'reply' AND NOT EXISTS (
|
|
296
|
+
SELECT 1 FROM network_mailbox_items AS request_item
|
|
297
|
+
WHERE request_item.team_id = NEW.team_id
|
|
298
|
+
AND request_item.id = NEW.root_request_item_id
|
|
299
|
+
AND request_item.kind = 'request'
|
|
300
|
+
)
|
|
301
|
+
BEGIN
|
|
302
|
+
SELECT RAISE(ABORT, 'network reply requires an exact passive request');
|
|
303
|
+
END;
|
|
304
|
+
|
|
305
|
+
CREATE TRIGGER network_mailbox_authority_is_immutable
|
|
306
|
+
BEFORE UPDATE ON network_mailbox_items
|
|
307
|
+
BEGIN
|
|
308
|
+
SELECT RAISE(ABORT, 'network mailbox items are immutable');
|
|
309
|
+
END;
|
|
310
|
+
|
|
311
|
+
CREATE TRIGGER network_mailbox_items_cannot_be_deleted
|
|
312
|
+
BEFORE DELETE ON network_mailbox_items
|
|
313
|
+
BEGIN
|
|
314
|
+
SELECT RAISE(ABORT, 'network mailbox items cannot be deleted');
|
|
315
|
+
END;
|
|
316
|
+
|
|
317
|
+
CREATE TABLE network_deliveries (
|
|
318
|
+
id TEXT PRIMARY KEY,
|
|
319
|
+
team_id TEXT NOT NULL,
|
|
320
|
+
item_id TEXT NOT NULL UNIQUE,
|
|
321
|
+
state TEXT NOT NULL DEFAULT 'available'
|
|
322
|
+
CHECK(state IN ('available', 'delivered', 'read')),
|
|
323
|
+
available_at INTEGER NOT NULL CHECK(available_at >= 0),
|
|
324
|
+
delivered_at INTEGER CHECK(delivered_at IS NULL OR delivered_at >= available_at),
|
|
325
|
+
read_at INTEGER CHECK(read_at IS NULL OR read_at >= delivered_at),
|
|
326
|
+
FOREIGN KEY(team_id, item_id)
|
|
327
|
+
REFERENCES network_mailbox_items(team_id, id) ON DELETE RESTRICT,
|
|
328
|
+
UNIQUE(team_id, id),
|
|
329
|
+
CHECK(
|
|
330
|
+
(state = 'available' AND delivered_at IS NULL AND read_at IS NULL)
|
|
331
|
+
OR (state = 'delivered' AND delivered_at IS NOT NULL AND read_at IS NULL)
|
|
332
|
+
OR (state = 'read' AND delivered_at IS NOT NULL AND read_at IS NOT NULL)
|
|
333
|
+
)
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
CREATE TRIGGER network_delivery_identity_is_immutable
|
|
337
|
+
BEFORE UPDATE OF id, team_id, item_id, available_at ON network_deliveries
|
|
338
|
+
BEGIN
|
|
339
|
+
SELECT RAISE(ABORT, 'network delivery identity is immutable');
|
|
340
|
+
END;
|
|
341
|
+
|
|
342
|
+
CREATE TRIGGER network_delivery_state_is_monotonic
|
|
343
|
+
BEFORE UPDATE ON network_deliveries
|
|
344
|
+
FOR EACH ROW WHEN
|
|
345
|
+
(OLD.state = 'delivered' AND NEW.state = 'available')
|
|
346
|
+
OR (OLD.state = 'read' AND NEW.state <> 'read')
|
|
347
|
+
OR (OLD.delivered_at IS NOT NULL AND NEW.delivered_at IS NOT OLD.delivered_at)
|
|
348
|
+
OR (OLD.read_at IS NOT NULL AND NEW.read_at IS NOT OLD.read_at)
|
|
349
|
+
BEGIN
|
|
350
|
+
SELECT RAISE(ABORT, 'network delivery receipts are monotonic');
|
|
351
|
+
END;
|
|
352
|
+
|
|
353
|
+
CREATE TRIGGER network_deliveries_cannot_be_deleted
|
|
354
|
+
BEFORE DELETE ON network_deliveries
|
|
355
|
+
BEGIN
|
|
356
|
+
SELECT RAISE(ABORT, 'network deliveries cannot be deleted');
|
|
357
|
+
END;
|
|
358
|
+
|
|
359
|
+
CREATE TABLE network_passive_requests (
|
|
360
|
+
request_item_id TEXT PRIMARY KEY,
|
|
361
|
+
team_id TEXT NOT NULL,
|
|
362
|
+
status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'replied', 'expired')),
|
|
363
|
+
reply_item_id TEXT UNIQUE,
|
|
364
|
+
created_at INTEGER NOT NULL CHECK(created_at >= 0),
|
|
365
|
+
expires_at INTEGER NOT NULL CHECK(expires_at > created_at),
|
|
366
|
+
replied_at INTEGER CHECK(replied_at IS NULL OR replied_at >= created_at),
|
|
367
|
+
FOREIGN KEY(team_id, request_item_id)
|
|
368
|
+
REFERENCES network_mailbox_items(team_id, id) ON DELETE RESTRICT,
|
|
369
|
+
FOREIGN KEY(team_id, reply_item_id)
|
|
370
|
+
REFERENCES network_mailbox_items(team_id, id) ON DELETE RESTRICT,
|
|
371
|
+
UNIQUE(team_id, request_item_id),
|
|
372
|
+
CHECK(
|
|
373
|
+
(status = 'open' AND reply_item_id IS NULL AND replied_at IS NULL)
|
|
374
|
+
OR (status = 'replied' AND reply_item_id IS NOT NULL AND replied_at IS NOT NULL)
|
|
375
|
+
OR (status = 'expired' AND reply_item_id IS NULL AND replied_at IS NULL)
|
|
376
|
+
)
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
CREATE TRIGGER network_request_requires_request_item
|
|
380
|
+
BEFORE INSERT ON network_passive_requests
|
|
381
|
+
FOR EACH ROW WHEN NOT EXISTS (
|
|
382
|
+
SELECT 1 FROM network_mailbox_items AS item
|
|
383
|
+
WHERE item.team_id = NEW.team_id
|
|
384
|
+
AND item.id = NEW.request_item_id
|
|
385
|
+
AND item.kind = 'request'
|
|
386
|
+
AND item.created_at = NEW.created_at
|
|
387
|
+
AND item.expires_at = NEW.expires_at
|
|
388
|
+
)
|
|
389
|
+
BEGIN
|
|
390
|
+
SELECT RAISE(ABORT, 'passive request metadata requires its exact item');
|
|
391
|
+
END;
|
|
392
|
+
|
|
393
|
+
CREATE TRIGGER network_request_reply_is_exact
|
|
394
|
+
BEFORE UPDATE OF status, reply_item_id, replied_at ON network_passive_requests
|
|
395
|
+
FOR EACH ROW WHEN NEW.status = 'replied' AND NOT EXISTS (
|
|
396
|
+
SELECT 1 FROM network_mailbox_items AS reply
|
|
397
|
+
WHERE reply.team_id = NEW.team_id
|
|
398
|
+
AND reply.id = NEW.reply_item_id
|
|
399
|
+
AND reply.kind = 'reply'
|
|
400
|
+
AND reply.root_request_item_id = NEW.request_item_id
|
|
401
|
+
)
|
|
402
|
+
BEGIN
|
|
403
|
+
SELECT RAISE(ABORT, 'passive request reply does not match');
|
|
404
|
+
END;
|
|
405
|
+
|
|
406
|
+
CREATE TRIGGER network_request_state_is_forward_only
|
|
407
|
+
BEFORE UPDATE ON network_passive_requests
|
|
408
|
+
FOR EACH ROW WHEN
|
|
409
|
+
OLD.status <> 'open'
|
|
410
|
+
OR NEW.status NOT IN ('replied', 'expired')
|
|
411
|
+
OR NEW.request_item_id IS NOT OLD.request_item_id
|
|
412
|
+
OR NEW.team_id IS NOT OLD.team_id
|
|
413
|
+
OR NEW.created_at IS NOT OLD.created_at
|
|
414
|
+
OR NEW.expires_at IS NOT OLD.expires_at
|
|
415
|
+
BEGIN
|
|
416
|
+
SELECT RAISE(ABORT, 'passive request state is forward-only');
|
|
417
|
+
END;
|
|
418
|
+
|
|
419
|
+
CREATE TRIGGER network_requests_cannot_be_deleted
|
|
420
|
+
BEFORE DELETE ON network_passive_requests
|
|
421
|
+
BEGIN
|
|
422
|
+
SELECT RAISE(ABORT, 'passive requests cannot be deleted');
|
|
423
|
+
END;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- Permit the process-local Team Hub control principal to author passive mail
|
|
2
|
+
-- only as the immutable managed host node. It still cannot authenticate over
|
|
3
|
+
-- HTTP, claim another server, or bypass recipient/agent ownership triggers.
|
|
4
|
+
|
|
5
|
+
DROP TRIGGER network_mailbox_sender_is_authorized;
|
|
6
|
+
|
|
7
|
+
CREATE TRIGGER network_mailbox_sender_is_authorized
|
|
8
|
+
BEFORE INSERT ON network_mailbox_items
|
|
9
|
+
FOR EACH ROW WHEN NOT (
|
|
10
|
+
(
|
|
11
|
+
NEW.sender_kind = 'human'
|
|
12
|
+
AND EXISTS (
|
|
13
|
+
SELECT 1
|
|
14
|
+
FROM principals AS p
|
|
15
|
+
JOIN memberships AS m
|
|
16
|
+
ON m.team_id = NEW.team_id AND m.principal_id = p.id
|
|
17
|
+
WHERE p.id = NEW.sender_principal_id
|
|
18
|
+
AND p.kind = 'human'
|
|
19
|
+
AND p.status = 'active'
|
|
20
|
+
AND m.status = 'active'
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
OR (
|
|
24
|
+
NEW.sender_kind IN ('server', 'agent')
|
|
25
|
+
AND (
|
|
26
|
+
EXISTS (
|
|
27
|
+
SELECT 1
|
|
28
|
+
FROM network_peer_bindings AS b
|
|
29
|
+
JOIN nodes AS n
|
|
30
|
+
ON n.team_id = b.team_id AND n.id = b.node_id
|
|
31
|
+
WHERE b.team_id = NEW.team_id
|
|
32
|
+
AND b.node_id = NEW.sender_node_id
|
|
33
|
+
AND b.service_principal_id = NEW.sender_principal_id
|
|
34
|
+
AND b.status = 'active'
|
|
35
|
+
AND n.status = 'active'
|
|
36
|
+
)
|
|
37
|
+
OR EXISTS (
|
|
38
|
+
SELECT 1
|
|
39
|
+
FROM managed_host_bindings AS managed
|
|
40
|
+
JOIN nodes AS n
|
|
41
|
+
ON n.team_id = NEW.team_id
|
|
42
|
+
AND n.id = NEW.sender_node_id
|
|
43
|
+
AND n.server_identity = managed.server_identity
|
|
44
|
+
JOIN principals AS p ON p.id = NEW.sender_principal_id
|
|
45
|
+
JOIN service_accounts AS s ON s.principal_id = p.id
|
|
46
|
+
JOIN memberships AS m
|
|
47
|
+
ON m.team_id = NEW.team_id
|
|
48
|
+
AND m.principal_id = p.id
|
|
49
|
+
WHERE managed.singleton = 1
|
|
50
|
+
AND NEW.sender_principal_id = 'service_local_control'
|
|
51
|
+
AND n.status = 'active'
|
|
52
|
+
AND p.kind = 'service'
|
|
53
|
+
AND p.status = 'active'
|
|
54
|
+
AND s.service_identifier = 'agentsdock.team-hub.local-control'
|
|
55
|
+
AND m.role = 'automation'
|
|
56
|
+
AND m.status = 'active'
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
BEGIN
|
|
62
|
+
SELECT RAISE(ABORT, 'network mailbox sender is not authorized');
|
|
63
|
+
END;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
-- Permit both process-local managed-host actors to author passive mail as the
|
|
2
|
+
-- immutable host node. The interactive managed-server session remains a
|
|
3
|
+
-- distinct service principal from local agent mail and recovery control.
|
|
4
|
+
|
|
5
|
+
DROP TRIGGER network_mailbox_sender_is_authorized;
|
|
6
|
+
|
|
7
|
+
CREATE TRIGGER network_mailbox_sender_is_authorized
|
|
8
|
+
BEFORE INSERT ON network_mailbox_items
|
|
9
|
+
FOR EACH ROW WHEN NOT (
|
|
10
|
+
(
|
|
11
|
+
NEW.sender_kind = 'human'
|
|
12
|
+
AND EXISTS (
|
|
13
|
+
SELECT 1
|
|
14
|
+
FROM principals AS p
|
|
15
|
+
JOIN memberships AS m
|
|
16
|
+
ON m.team_id = NEW.team_id AND m.principal_id = p.id
|
|
17
|
+
WHERE p.id = NEW.sender_principal_id
|
|
18
|
+
AND p.kind = 'human'
|
|
19
|
+
AND p.status = 'active'
|
|
20
|
+
AND m.status = 'active'
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
OR (
|
|
24
|
+
NEW.sender_kind IN ('server', 'agent')
|
|
25
|
+
AND (
|
|
26
|
+
EXISTS (
|
|
27
|
+
SELECT 1
|
|
28
|
+
FROM network_peer_bindings AS b
|
|
29
|
+
JOIN nodes AS n
|
|
30
|
+
ON n.team_id = b.team_id AND n.id = b.node_id
|
|
31
|
+
WHERE b.team_id = NEW.team_id
|
|
32
|
+
AND b.node_id = NEW.sender_node_id
|
|
33
|
+
AND b.service_principal_id = NEW.sender_principal_id
|
|
34
|
+
AND b.status = 'active'
|
|
35
|
+
AND n.status = 'active'
|
|
36
|
+
)
|
|
37
|
+
OR EXISTS (
|
|
38
|
+
SELECT 1
|
|
39
|
+
FROM managed_host_bindings AS managed
|
|
40
|
+
JOIN nodes AS n
|
|
41
|
+
ON n.team_id = NEW.team_id
|
|
42
|
+
AND n.id = NEW.sender_node_id
|
|
43
|
+
AND n.server_identity = managed.server_identity
|
|
44
|
+
JOIN principals AS p ON p.id = NEW.sender_principal_id
|
|
45
|
+
JOIN service_accounts AS s ON s.principal_id = p.id
|
|
46
|
+
JOIN memberships AS m
|
|
47
|
+
ON m.team_id = NEW.team_id
|
|
48
|
+
AND m.principal_id = p.id
|
|
49
|
+
WHERE managed.singleton = 1
|
|
50
|
+
AND (
|
|
51
|
+
(
|
|
52
|
+
NEW.sender_principal_id = 'service_local_control'
|
|
53
|
+
AND s.service_identifier =
|
|
54
|
+
'agentsdock.team-hub.local-control'
|
|
55
|
+
)
|
|
56
|
+
OR (
|
|
57
|
+
NEW.sender_principal_id = 'service_managed_server'
|
|
58
|
+
AND s.service_identifier =
|
|
59
|
+
'agentsdock.team-hub.managed-server'
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
AND n.status = 'active'
|
|
63
|
+
AND p.kind = 'service'
|
|
64
|
+
AND p.status = 'active'
|
|
65
|
+
AND m.role = 'automation'
|
|
66
|
+
AND m.status = 'active'
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
BEGIN
|
|
72
|
+
SELECT RAISE(ABORT, 'network mailbox sender is not authorized');
|
|
73
|
+
END;
|