borgmcp-shared 0.3.0 → 0.4.2

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.
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  ErrorCode,
3
- ProtocolContractError,
4
3
  compareLogCursor,
5
4
  createProtocolEnvelope,
6
5
  decodeCreateCubeResponseEnvelope,
@@ -8,16 +7,19 @@ import {
8
7
  decodeDecisionResultEnvelope,
9
8
  decodeDecisionsResultEnvelope,
10
9
  decodeEnrollmentExchangeResponseEnvelope,
10
+ decodeEvictDroneResultEnvelope,
11
+ decodeProtocolEnvelope,
11
12
  decodeProtocolErrorEnvelope,
12
- decodeProtocolInfoEnvelope,
13
+ decodeProtocolTagPreflight,
13
14
  decodeReadLogResultEnvelope,
15
+ decodeReassignDroneResultEnvelope,
14
16
  decodeSseFrames,
15
- negotiateProtocol,
17
+ PROTOCOL_LIMIT_CEILINGS,
18
+ PROTOCOL_HTTP_CONTRACT,
19
+ PROTOCOL_VERSION,
16
20
  utf8ByteLength,
17
- type Capability,
18
21
  type CreateCubeResponse,
19
22
  type LogCursor,
20
- type ProtocolInfo,
21
23
  type StreamEvent,
22
24
  } from '../protocol/index.js';
23
25
  import { ENROLLMENT_RETRY_CONFORMANCE } from './index.js';
@@ -35,6 +37,29 @@ export interface ConformanceCube {
35
37
  readonly id: string;
36
38
  }
37
39
 
40
+ export interface ConformanceRole {
41
+ readonly id: string;
42
+ }
43
+
44
+ export interface ConformanceDrone {
45
+ readonly id: string;
46
+ }
47
+
48
+ export type ConformanceCubeAccess = 'read' | 'write' | 'manage';
49
+
50
+ export interface ConformanceCubeManagementState {
51
+ readonly directive: string;
52
+ readonly taxonomy_marker: string | null;
53
+ readonly role_ids: readonly string[];
54
+ readonly active_decision_ids: readonly string[];
55
+ readonly drones: ReadonlyArray<{
56
+ readonly id: string;
57
+ readonly role_id: string;
58
+ readonly evicted: boolean;
59
+ readonly session_revoked: boolean;
60
+ }>;
61
+ }
62
+
38
63
  export interface ConformanceAuthorityState {
39
64
  enrolled_clients: number;
40
65
  enrollment_claims: number;
@@ -57,6 +82,13 @@ export interface ConformanceCreatedCubeState {
57
82
  export interface ConformanceEnrollmentPrincipalState {
58
83
  response_client_matches: boolean;
59
84
  active_credential_bindings: number;
85
+ /**
86
+ * The principal's currently-bound credential still equals the one it enrolled
87
+ * with. Since the credential-free tag preflight cannot probe credentials, this
88
+ * out-of-band authority check is what proves a rejected mismatch retry did not
89
+ * overwrite the good credential.
90
+ */
91
+ bound_credential_matches_enrollment: boolean;
60
92
  }
61
93
 
62
94
  export interface ConformanceReplayBarrier {
@@ -78,7 +110,31 @@ export interface ConformanceAdmin {
78
110
  reset(): Promise<void>;
79
111
  createPrincipal(name: string): Promise<ConformancePrincipal>;
80
112
  createCube(name: string): Promise<ConformanceCube>;
81
- grantCube(principal: ConformancePrincipal, cube: ConformanceCube): Promise<void>;
113
+ /** Grants the requested cube authority; omitted access defaults to manage. */
114
+ grantCube(
115
+ principal: ConformancePrincipal,
116
+ cube: ConformanceCube,
117
+ access?: ConformanceCubeAccess,
118
+ ): Promise<void>;
119
+ createRole(cube: ConformanceCube, input: {
120
+ readonly roleClass: 'queen' | 'worker';
121
+ readonly isHumanSeat: boolean;
122
+ }): Promise<ConformanceRole>;
123
+ createDrone(
124
+ principal: ConformancePrincipal,
125
+ cube: ConformanceCube,
126
+ role: ConformanceRole,
127
+ ): Promise<ConformanceDrone>;
128
+ issueManagedDroneSession(drone: ConformanceDrone): Promise<string>;
129
+ revokeManagedDroneSession(drone: ConformanceDrone): Promise<void>;
130
+ expireManagedDroneSession(drone: ConformanceDrone): Promise<void>;
131
+ inspectManagedDrone(drone: ConformanceDrone): Promise<{
132
+ readonly role_id: string;
133
+ readonly evicted: boolean;
134
+ readonly session_revoked: boolean;
135
+ }>;
136
+ /** Observes every cube field mutated by a represented manage-scoped operation. */
137
+ inspectCubeManagementState(cube: ConformanceCube): Promise<ConformanceCubeManagementState>;
82
138
  grantCreateCubeCapability(principal: ConformancePrincipal): Promise<void>;
83
139
  issueDroneSession(principal: ConformancePrincipal): Promise<string>;
84
140
  issueSingleUseInvitation(principal: ConformancePrincipal, purpose: 'owner' | 'client'): Promise<string>;
@@ -123,6 +179,21 @@ export interface ConformanceOperations {
123
179
  cube: ConformanceCube,
124
180
  request: unknown,
125
181
  ): Promise<ConformanceHttpResponse>;
182
+ updateCube(
183
+ credential: string,
184
+ cube: ConformanceCube,
185
+ request: unknown,
186
+ ): Promise<ConformanceHttpResponse>;
187
+ createRole(
188
+ credential: string,
189
+ cube: ConformanceCube,
190
+ request: unknown,
191
+ ): Promise<ConformanceHttpResponse>;
192
+ patchTaxonomy(
193
+ credential: string,
194
+ cube: ConformanceCube,
195
+ request: unknown,
196
+ ): Promise<ConformanceHttpResponse>;
126
197
  recordDecision(
127
198
  credential: string,
128
199
  cube: ConformanceCube,
@@ -133,6 +204,22 @@ export interface ConformanceOperations {
133
204
  cube: ConformanceCube,
134
205
  request: unknown,
135
206
  ): Promise<ConformanceHttpResponse>;
207
+ listDrones(
208
+ credential: string,
209
+ cube: ConformanceCube,
210
+ ): Promise<ConformanceHttpResponse>;
211
+ reassignDrone(
212
+ credential: string,
213
+ cube: ConformanceCube,
214
+ drone: ConformanceDrone,
215
+ request: unknown,
216
+ ): Promise<ConformanceHttpResponse>;
217
+ evictDrone(
218
+ credential: string,
219
+ cube: ConformanceCube,
220
+ drone: ConformanceDrone,
221
+ request: unknown,
222
+ ): Promise<ConformanceHttpResponse>;
136
223
  openStream(
137
224
  credential: string,
138
225
  cube: ConformanceCube,
@@ -147,7 +234,8 @@ export interface ConformanceEnvironment {
147
234
 
148
235
  export const ADAPTER_CONFORMANCE_FIXTURES = [
149
236
  { id: 'http.unauthenticated-liveness', area: 'http' },
150
- { id: 'protocol.enrollment-auth', area: 'protocol' },
237
+ { id: 'protocol.credential-free-preflight', area: 'protocol' },
238
+ { id: 'enrollment.retry-authority', area: 'enrollment' },
151
239
  { id: 'security.adapter-boundary-injection', area: 'security' },
152
240
  { id: 'security.oversize-request', area: 'security' },
153
241
  { id: 'security.cross-cube-isolation', area: 'security' },
@@ -157,7 +245,12 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
157
245
  { id: 'acks.idempotent', area: 'acks' },
158
246
  { id: 'claims.durable-noncursor', area: 'claims' },
159
247
  { id: 'decisions.topic-supersession', area: 'decisions' },
160
- { id: 'capabilities.unsupported-fails-closed', area: 'capabilities' },
248
+ { id: 'security.drone-management-authorization', area: 'security' },
249
+ { id: 'security.manage-access-matrix', area: 'security' },
250
+ { id: 'drones.reassign-invariants', area: 'drones' },
251
+ { id: 'security.cross-cube-drone-management', area: 'security' },
252
+ { id: 'drones.evict-terminal-signal', area: 'drones' },
253
+ { id: 'security.drone-session-rejection-causes', area: 'security' },
161
254
  { id: 'security.active-stream-revocation', area: 'security' },
162
255
  ] as const;
163
256
 
@@ -181,13 +274,6 @@ export interface AdapterConformanceReport {
181
274
  }>;
182
275
  }
183
276
 
184
- export interface EquivalentAdapterConformanceReport {
185
- ok: boolean;
186
- cloud: AdapterConformanceReport;
187
- local: AdapterConformanceReport;
188
- equivalent: boolean;
189
- }
190
-
191
277
  export interface AdapterConformanceOptions {
192
278
  /** Maximum wait for a replay, live delivery, or revocation close. */
193
279
  streamDeadlineMs?: number;
@@ -333,6 +419,20 @@ function same(left: unknown, right: unknown): boolean {
333
419
  return JSON.stringify(left) === JSON.stringify(right);
334
420
  }
335
421
 
422
+ function listedDroneIds(response: ConformanceHttpResponse): string[] {
423
+ return decodeProtocolEnvelope(response.body, (payload) => {
424
+ invariant(typeof payload === 'object' && payload !== null, 'Roster payload is not an object.');
425
+ const drones = (payload as { drones?: unknown }).drones;
426
+ invariant(Array.isArray(drones), 'Roster payload omitted drones.');
427
+ return drones.map((drone) => {
428
+ invariant(typeof drone === 'object' && drone !== null, 'Roster contains an invalid drone.');
429
+ const id = (drone as { id?: unknown }).id;
430
+ invariant(typeof id === 'string', 'Roster drone omitted its id.');
431
+ return id;
432
+ });
433
+ }).payload;
434
+ }
435
+
336
436
  function assertStateDelta(
337
437
  before: ConformanceAuthorityState,
338
438
  after: ConformanceAuthorityState,
@@ -410,8 +510,6 @@ export async function runAdapterConformance(
410
510
 
411
511
  let credentialA = '';
412
512
  let credentialB = '';
413
- let protocolBody: unknown;
414
- let protocolInfo: ProtocolInfo | null = null;
415
513
  await record('http.unauthenticated-liveness', async () => {
416
514
  const response = await environment.operations.health();
417
515
  expectStatus(response, 204, 'Unauthenticated liveness');
@@ -419,9 +517,23 @@ export async function runAdapterConformance(
419
517
  return { status: 204, bodyless: true };
420
518
  });
421
519
 
422
- await record('protocol.enrollment-auth', async () => {
423
- expectError(await environment.operations.protocol(null), 401, ErrorCode.AUTH_MISSING, 'Unauthenticated protocol request');
520
+ await record('protocol.credential-free-preflight', async () => {
521
+ // The tag preflight is credential-free (no bearer) and mutation-free: a
522
+ // client verifies pinned TLS and the exact tag before it creates or sends any
523
+ // credential. The server must answer 200 with ONLY the exact tag.
524
+ const before = await environment.admin.observeAuthorityState();
525
+ const response = await environment.operations.protocol(null);
526
+ expectStatus(response, 200, 'Credential-free protocol-tag preflight');
527
+ const preflight = decodeProtocolTagPreflight(response.body);
528
+ invariant(
529
+ Object.keys(preflight).length === 1 && preflight.protocol_version === PROTOCOL_VERSION,
530
+ 'Protocol-tag preflight exposed more than the exact tag.',
531
+ );
532
+ assertStateDelta(before, await environment.admin.observeAuthorityState(), {}, 'Protocol-tag preflight');
533
+ return { authenticated: false, mutation_free: true, protocol_version: preflight.protocol_version };
534
+ });
424
535
 
536
+ await record('enrollment.retry-authority', async () => {
425
537
  const retryVectorErrors: string[] = [];
426
538
  for (const [index, vector] of ENROLLMENT_RETRY_CONFORMANCE.entries()) {
427
539
  for (const purpose of ['client', 'owner'] as const) {
@@ -474,23 +586,11 @@ export async function runAdapterConformance(
474
586
  );
475
587
  }
476
588
  assertStateDelta(beforeRetry, await environment.admin.observeAuthorityState(), {}, `${purpose} ${vector.name} retry`);
477
- expectStatus(
478
- await environment.operations.protocol(vector.initial.client_credential),
479
- 200,
480
- `${purpose} ${vector.name} original credential continuity`,
481
- );
482
- if (vector.retry.client_credential !== vector.initial.client_credential) {
483
- expectError(
484
- await environment.operations.protocol(vector.retry.client_credential),
485
- 401,
486
- ErrorCode.AUTH_INVALID,
487
- `${purpose} ${vector.name} mismatched credential rejection`,
488
- );
489
- }
490
589
  invariant(
491
590
  same(await environment.admin.inspectEnrollmentPrincipal(principal, initial.client_id), {
492
591
  response_client_matches: true,
493
592
  active_credential_bindings: 1,
593
+ bound_credential_matches_enrollment: true,
494
594
  }),
495
595
  `${purpose} ${vector.name} changed enrollment binding ownership.`,
496
596
  );
@@ -710,37 +810,23 @@ export async function runAdapterConformance(
710
810
  ErrorCode.AUTH_INVALID,
711
811
  'Enrollment retry mismatch',
712
812
  );
713
- const protocolResponse = await environment.operations.protocol(credentialA);
714
- expectStatus(protocolResponse, 200, 'Authenticated protocol request');
715
- protocolBody = protocolResponse.body;
716
- protocolInfo = negotiateProtocol(decodeProtocolInfoEnvelope(protocolBody).payload, [
717
- 'log.cursor',
718
- 'stream.sse',
719
- 'stream.replay',
720
- 'acks',
721
- 'claims',
722
- 'decisions',
723
- ]);
724
813
  return {
725
- unauthenticated: ErrorCode.AUTH_MISSING,
726
814
  enrollment_status: 201,
727
815
  exact_retry_status: 201,
728
816
  mismatched_retry: ErrorCode.AUTH_INVALID,
729
817
  response_secret_free: true,
730
- protocol_version: protocolInfo.protocol_version,
731
818
  };
732
819
  });
733
820
 
734
821
  await record('security.adapter-boundary-injection', async () => {
735
- invariant(protocolInfo, 'Protocol fixture did not produce request limits.');
736
822
  const injectedMessage = "'); DROP TABLE log_entries; --\r\ndata: forged-sse-frame";
737
823
  const injectedBody = JSON.stringify(
738
824
  createProtocolEnvelope('inject-b1', { message: injectedMessage }),
739
825
  );
740
826
  invariant(
741
- utf8ByteLength(injectedBody) <= protocolInfo.limits.max_request_bytes &&
742
- utf8ByteLength(injectedMessage) <= protocolInfo.limits.max_log_message_bytes,
743
- 'Injection fixture exceeded an advertised request limit.',
827
+ utf8ByteLength(injectedBody) <= PROTOCOL_LIMIT_CEILINGS.max_request_bytes &&
828
+ utf8ByteLength(injectedMessage) <= PROTOCOL_LIMIT_CEILINGS.max_log_message_bytes,
829
+ 'Injection fixture exceeded the shared request-limit ceiling.',
744
830
  );
745
831
  const injected = await environment.operations.appendRaw(
746
832
  credentialB,
@@ -800,15 +886,14 @@ export async function runAdapterConformance(
800
886
  });
801
887
 
802
888
  await record('security.oversize-request', async () => {
803
- invariant(protocolInfo, 'Protocol fixture did not produce request limits.');
804
889
  const baseBody = JSON.stringify(
805
890
  createProtocolEnvelope('oversize-a1', { message: 'must-not-persist' }),
806
891
  );
807
892
  const oversizedBody = baseBody + ' '.repeat(
808
- Math.max(0, protocolInfo.limits.max_request_bytes - utf8ByteLength(baseBody) + 1),
893
+ Math.max(0, PROTOCOL_LIMIT_CEILINGS.max_request_bytes - utf8ByteLength(baseBody) + 1),
809
894
  );
810
895
  invariant(
811
- utf8ByteLength(oversizedBody) > protocolInfo.limits.max_request_bytes,
896
+ utf8ByteLength(oversizedBody) > PROTOCOL_LIMIT_CEILINGS.max_request_bytes,
812
897
  'Oversize fixture did not exceed max_request_bytes.',
813
898
  );
814
899
  const response = await environment.operations.appendRaw(credentialA, cubeA, oversizedBody);
@@ -995,20 +1080,489 @@ export async function runAdapterConformance(
995
1080
  return { active_count: 1, active_decision: 'second', supersedes_first: true };
996
1081
  });
997
1082
 
998
- await record('capabilities.unsupported-fails-closed', async () => {
999
- invariant(protocolBody, 'Protocol fixture did not produce an envelope.');
1000
- let code: ErrorCode | null = null;
1001
- try {
1002
- negotiateProtocol(
1003
- decodeProtocolInfoEnvelope(protocolBody).payload,
1004
- ['future.required' as Capability],
1083
+ let workerRoleA!: ConformanceRole;
1084
+ let workerRoleB!: ConformanceRole;
1085
+ let managedWorker!: ConformanceDrone;
1086
+ let readCredential = '';
1087
+ let writeCredential = '';
1088
+ let workerSession = '';
1089
+ await record('security.drone-management-authorization', async () => {
1090
+ workerRoleA = await environment.admin.createRole(cubeA, {
1091
+ roleClass: 'worker', isHumanSeat: false,
1092
+ });
1093
+ managedWorker = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
1094
+ const deniedCredentials: Array<{ access: 'read' | 'write'; credential: string }> = [];
1095
+ for (const [index, access] of ['read', 'write'].entries()) {
1096
+ const principalName = access === 'read' ? 'Coordinator' : 'write-principal';
1097
+ const principal = await environment.admin.createPrincipal(principalName);
1098
+ await environment.admin.grantCube(principal, cubeA, access as 'read' | 'write');
1099
+ const invitation = await environment.admin.issueSingleUseInvitation(principal, 'client');
1100
+ const credential = `${(access === 'read' ? 'R' : 'W').repeat(42)}Q`;
1101
+ if (access === 'read') readCredential = credential;
1102
+ else writeCredential = credential;
1103
+ const enrollment = await environment.operations.enroll(createProtocolEnvelope(
1104
+ `${access}-principal-enroll`,
1105
+ {
1106
+ invitation,
1107
+ retry_key: `00000000-0000-4000-8000-${String(301 + index).padStart(12, '0')}`,
1108
+ client_credential: credential,
1109
+ client_name: principalName,
1110
+ },
1111
+ ));
1112
+ expectStatus(enrollment, 201, `${access} principal enrollment`);
1113
+ deniedCredentials.push({ access: access as 'read' | 'write', credential });
1114
+ }
1115
+ for (const { access, credential } of deniedCredentials) {
1116
+ expectError(
1117
+ await environment.operations.reassignDrone(
1118
+ credential,
1119
+ cubeA,
1120
+ managedWorker,
1121
+ createProtocolEnvelope(`${access}-reassign-denied`, { role_id: workerRoleA.id }),
1122
+ ),
1123
+ 403,
1124
+ ErrorCode.ACCESS_DENIED,
1125
+ `${access} principal reassignment`,
1126
+ );
1127
+ expectError(
1128
+ await environment.operations.evictDrone(
1129
+ credential,
1130
+ cubeA,
1131
+ managedWorker,
1132
+ createProtocolEnvelope(`${access}-evict-denied`, {}),
1133
+ ),
1134
+ 403,
1135
+ ErrorCode.ACCESS_DENIED,
1136
+ `${access} principal eviction`,
1137
+ );
1138
+ }
1139
+ return { read_status: 403, write_status: 403, manage_required: true };
1140
+ });
1141
+
1142
+ await record('security.manage-access-matrix', async () => {
1143
+ const noGrantPrincipal = await environment.admin.createPrincipal('Coordinator role without cube grant');
1144
+ const noGrantInvitation = await environment.admin.issueSingleUseInvitation(noGrantPrincipal, 'client');
1145
+ const noGrantCredential = `${'N'.repeat(42)}Q`;
1146
+ expectStatus(await environment.operations.enroll(createProtocolEnvelope(
1147
+ 'no-grant-principal-enroll',
1148
+ {
1149
+ invitation: noGrantInvitation,
1150
+ retry_key: '00000000-0000-4000-8000-000000000303',
1151
+ client_credential: noGrantCredential,
1152
+ client_name: 'Coordinator',
1153
+ },
1154
+ )), 201, 'No-grant principal enrollment');
1155
+
1156
+ const droneCredential = await environment.admin.issueManagedDroneSession(managedWorker);
1157
+ const matrixTargetRole = await environment.admin.createRole(cubeA, {
1158
+ roleClass: 'worker', isHumanSeat: false,
1159
+ });
1160
+ const evictionTarget = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
1161
+ await environment.admin.issueManagedDroneSession(evictionTarget);
1162
+ const unknownCube = { id: '00000000-0000-4000-8000-000000000399' };
1163
+ const snapshot = async (): Promise<unknown> => ({
1164
+ cubeA: await environment.admin.inspectCubeManagementState(cubeA),
1165
+ cubeB: await environment.admin.inspectCubeManagementState(cubeB),
1166
+ });
1167
+ const operations: ReadonlyArray<{
1168
+ name: string;
1169
+ successStatus: number;
1170
+ invoke: (credential: string, cube: ConformanceCube) => Promise<ConformanceHttpResponse>;
1171
+ }> = [
1172
+ {
1173
+ name: 'cube-update',
1174
+ successStatus: 200,
1175
+ invoke: (credential, cube) => environment.operations.updateCube(
1176
+ credential,
1177
+ cube,
1178
+ createProtocolEnvelope('matrix-cube-update', { cube_directive: 'matrix-directive' }),
1179
+ ),
1180
+ },
1181
+ {
1182
+ name: 'role-create',
1183
+ successStatus: 201,
1184
+ invoke: (credential, cube) => environment.operations.createRole(
1185
+ credential,
1186
+ cube,
1187
+ createProtocolEnvelope('matrix-role-create', { name: 'Matrix Role' }),
1188
+ ),
1189
+ },
1190
+ {
1191
+ name: 'taxonomy-patch',
1192
+ successStatus: 200,
1193
+ invoke: (credential, cube) => environment.operations.patchTaxonomy(
1194
+ credential,
1195
+ cube,
1196
+ createProtocolEnvelope('matrix-taxonomy-patch', { marker: 'matrix-taxonomy' }),
1197
+ ),
1198
+ },
1199
+ {
1200
+ name: 'decision-record',
1201
+ successStatus: 201,
1202
+ invoke: (credential, cube) => environment.operations.recordDecision(
1203
+ credential,
1204
+ cube,
1205
+ createProtocolEnvelope('matrix-decision-record', {
1206
+ topic: 'matrix-authority', decision: 'manage only',
1207
+ }),
1208
+ ),
1209
+ },
1210
+ {
1211
+ name: 'drone-reassign',
1212
+ successStatus: 200,
1213
+ invoke: (credential, cube) => environment.operations.reassignDrone(
1214
+ credential,
1215
+ cube,
1216
+ managedWorker,
1217
+ createProtocolEnvelope('matrix-drone-reassign', { role_id: matrixTargetRole.id }),
1218
+ ),
1219
+ },
1220
+ {
1221
+ name: 'drone-evict',
1222
+ successStatus: 200,
1223
+ invoke: (credential, cube) => environment.operations.evictDrone(
1224
+ credential,
1225
+ cube,
1226
+ evictionTarget,
1227
+ createProtocolEnvelope('matrix-drone-evict', {}),
1228
+ ),
1229
+ },
1230
+ ];
1231
+
1232
+ for (const operation of operations) {
1233
+ for (const [kind, credential] of [
1234
+ ['read', readCredential],
1235
+ ['write', writeCredential],
1236
+ ['drone-session', droneCredential],
1237
+ ] as const) {
1238
+ const before = await snapshot();
1239
+ expectError(
1240
+ await operation.invoke(credential, cubeA),
1241
+ 403,
1242
+ ErrorCode.ACCESS_DENIED,
1243
+ `${operation.name} by ${kind} principal`,
1244
+ );
1245
+ invariant(same(await snapshot(), before), `${operation.name} mutated state after ${kind} denial.`);
1246
+ }
1247
+ for (const [kind, credential, cube] of [
1248
+ ['no-grant', noGrantCredential, cubeA],
1249
+ ['foreign-principal', credentialB, cubeA],
1250
+ ['foreign-cube', credentialA, cubeB],
1251
+ ['unknown-cube', credentialA, unknownCube],
1252
+ ] as const) {
1253
+ const before = await snapshot();
1254
+ expectError(
1255
+ await operation.invoke(credential, cube),
1256
+ 404,
1257
+ ErrorCode.NOT_FOUND,
1258
+ `${operation.name} against ${kind}`,
1259
+ );
1260
+ invariant(same(await snapshot(), before), `${operation.name} mutated state after ${kind} denial.`);
1261
+ }
1262
+ const beforeSuccess = await environment.admin.inspectCubeManagementState(cubeA);
1263
+ const success = await operation.invoke(credentialA, cubeA);
1264
+ expectStatus(success, operation.successStatus, `${operation.name} by managing principal`);
1265
+ invariant(
1266
+ !same(await environment.admin.inspectCubeManagementState(cubeA), beforeSuccess),
1267
+ `${operation.name} managing success did not mutate its declared state.`,
1268
+ );
1269
+ }
1270
+ return {
1271
+ operation_count: operations.length,
1272
+ manage_success: true,
1273
+ read_write_status: 403,
1274
+ drone_session_status: 403,
1275
+ hidden_status: 404,
1276
+ denied_mutations: 0,
1277
+ role_labels_authoritative: false,
1278
+ };
1279
+ });
1280
+
1281
+ await record('drones.reassign-invariants', async () => {
1282
+ workerRoleB = await environment.admin.createRole(cubeA, {
1283
+ roleClass: 'worker', isHumanSeat: false,
1284
+ });
1285
+ const humanSourceRole = await environment.admin.createRole(cubeA, {
1286
+ roleClass: 'worker', isHumanSeat: true,
1287
+ });
1288
+ const queenRole = await environment.admin.createRole(cubeA, {
1289
+ roleClass: 'queen', isHumanSeat: false,
1290
+ });
1291
+ const occupiedHumanRole = await environment.admin.createRole(cubeA, {
1292
+ roleClass: 'worker', isHumanSeat: true,
1293
+ });
1294
+ const humanSource = await environment.admin.createDrone(principalA, cubeA, humanSourceRole);
1295
+ await environment.admin.createDrone(principalA, cubeA, occupiedHumanRole);
1296
+ const contender = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
1297
+ workerSession = await environment.admin.issueManagedDroneSession(managedWorker);
1298
+
1299
+ const reassigned = await environment.operations.reassignDrone(
1300
+ credentialA,
1301
+ cubeA,
1302
+ managedWorker,
1303
+ createProtocolEnvelope('reassign-worker', { role_id: workerRoleB.id }),
1304
+ );
1305
+ expectStatus(reassigned, 200, 'Worker reassignment');
1306
+ const reassignedDrone = decodeReassignDroneResultEnvelope(reassigned.body).payload.drone;
1307
+ invariant(
1308
+ reassignedDrone.id === managedWorker.id && reassignedDrone.role_id === workerRoleB.id,
1309
+ 'Reassignment response did not identify the persisted target role.',
1310
+ );
1311
+
1312
+ expectError(
1313
+ await environment.operations.reassignDrone(
1314
+ workerSession,
1315
+ cubeA,
1316
+ managedWorker,
1317
+ createProtocolEnvelope('reassign-drone-session-denied', { role_id: workerRoleA.id }),
1318
+ ),
1319
+ 403,
1320
+ ErrorCode.ACCESS_DENIED,
1321
+ 'Drone-session reassignment',
1322
+ );
1323
+ expectError(
1324
+ await environment.operations.reassignDrone(
1325
+ credentialA,
1326
+ cubeA,
1327
+ managedWorker,
1328
+ createProtocolEnvelope('reassign-queen-denied', { role_id: queenRole.id }),
1329
+ ),
1330
+ 403,
1331
+ ErrorCode.ACCESS_DENIED,
1332
+ 'Worker-to-queen reassignment',
1333
+ );
1334
+ const promoted = await environment.operations.reassignDrone(
1335
+ credentialA,
1336
+ cubeA,
1337
+ humanSource,
1338
+ createProtocolEnvelope('reassign-queen-allowed', { role_id: queenRole.id }),
1339
+ );
1340
+ expectStatus(promoted, 200, 'Human-seat-to-queen reassignment');
1341
+ invariant(
1342
+ decodeReassignDroneResultEnvelope(promoted.body).payload.drone.role_id === queenRole.id,
1343
+ 'Human-seat-to-queen reassignment did not persist.',
1344
+ );
1345
+ expectError(
1346
+ await environment.operations.reassignDrone(
1347
+ credentialA,
1348
+ cubeA,
1349
+ contender,
1350
+ createProtocolEnvelope('reassign-occupied-denied', { role_id: occupiedHumanRole.id }),
1351
+ ),
1352
+ 409,
1353
+ ErrorCode.ROLE_IN_USE,
1354
+ 'Occupied human-seat reassignment',
1355
+ );
1356
+ return {
1357
+ worker_reassigned: true,
1358
+ drone_session_denied: true,
1359
+ queen_requires_human_seat: true,
1360
+ occupied_human_seat_denied: true,
1361
+ };
1362
+ });
1363
+
1364
+ await record('security.cross-cube-drone-management', async () => {
1365
+ const foreignRole = await environment.admin.createRole(cubeB, {
1366
+ roleClass: 'worker', isHumanSeat: false,
1367
+ });
1368
+ const foreignDrone = await environment.admin.createDrone(principalB, cubeB, foreignRole);
1369
+ expectError(
1370
+ await environment.operations.reassignDrone(
1371
+ credentialA,
1372
+ cubeB,
1373
+ foreignDrone,
1374
+ createProtocolEnvelope('cross-cube-reassign', { role_id: foreignRole.id }),
1375
+ ),
1376
+ 404,
1377
+ ErrorCode.NOT_FOUND,
1378
+ 'Cross-cube reassignment',
1379
+ );
1380
+ expectError(
1381
+ await environment.operations.evictDrone(
1382
+ credentialA,
1383
+ cubeB,
1384
+ foreignDrone,
1385
+ createProtocolEnvelope('cross-cube-evict', {}),
1386
+ ),
1387
+ 404,
1388
+ ErrorCode.NOT_FOUND,
1389
+ 'Cross-cube eviction',
1390
+ );
1391
+ expectError(
1392
+ await environment.operations.reassignDrone(
1393
+ credentialA,
1394
+ cubeA,
1395
+ foreignDrone,
1396
+ createProtocolEnvelope('foreign-drone-local-route', { role_id: workerRoleA.id }),
1397
+ ),
1398
+ 404,
1399
+ ErrorCode.NOT_FOUND,
1400
+ 'Foreign drone reassignment through authorized cube route',
1401
+ );
1402
+ expectError(
1403
+ await environment.operations.evictDrone(
1404
+ credentialA,
1405
+ cubeA,
1406
+ foreignDrone,
1407
+ createProtocolEnvelope('foreign-drone-local-evict', {}),
1408
+ ),
1409
+ 404,
1410
+ ErrorCode.NOT_FOUND,
1411
+ 'Foreign drone eviction through authorized cube route',
1412
+ );
1413
+ expectError(
1414
+ await environment.operations.reassignDrone(
1415
+ credentialA,
1416
+ cubeA,
1417
+ managedWorker,
1418
+ createProtocolEnvelope('foreign-role-local-drone', { role_id: foreignRole.id }),
1419
+ ),
1420
+ 404,
1421
+ ErrorCode.NOT_FOUND,
1422
+ 'Foreign role reassignment through authorized cube route',
1423
+ );
1424
+ await environment.admin.grantCube(principalA, cubeB, 'read');
1425
+ const foreignDroneBefore = await environment.admin.inspectManagedDrone(foreignDrone);
1426
+ expectError(
1427
+ await environment.operations.reassignDrone(
1428
+ workerSession,
1429
+ cubeB,
1430
+ foreignDrone,
1431
+ createProtocolEnvelope('bound-drone-cross-cube-reassign', { role_id: foreignRole.id }),
1432
+ ),
1433
+ 404,
1434
+ ErrorCode.NOT_FOUND,
1435
+ 'Bound drone cross-cube reassignment',
1436
+ );
1437
+ invariant(
1438
+ same(await environment.admin.inspectManagedDrone(foreignDrone), foreignDroneBefore),
1439
+ 'Bound drone cross-cube reassignment mutated the foreign target.',
1440
+ );
1441
+ expectError(
1442
+ await environment.operations.evictDrone(
1443
+ workerSession,
1444
+ cubeB,
1445
+ foreignDrone,
1446
+ createProtocolEnvelope('bound-drone-cross-cube-evict', {}),
1447
+ ),
1448
+ 404,
1449
+ ErrorCode.NOT_FOUND,
1450
+ 'Bound drone cross-cube eviction',
1451
+ );
1452
+ invariant(
1453
+ same(await environment.admin.inspectManagedDrone(foreignDrone), foreignDroneBefore),
1454
+ 'Bound drone cross-cube eviction mutated the foreign target.',
1455
+ );
1456
+ return {
1457
+ unauthorized_cube_status: 404,
1458
+ foreign_drone_status: 404,
1459
+ foreign_role_status: 404,
1460
+ bound_drone_cross_cube_status: 404,
1461
+ code: ErrorCode.NOT_FOUND,
1462
+ };
1463
+ });
1464
+
1465
+ await record('drones.evict-terminal-signal', async () => {
1466
+ const evictedDrone = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
1467
+ const evictedCredential = await environment.admin.issueManagedDroneSession(evictedDrone);
1468
+ expectStatus(
1469
+ await environment.operations.read(
1470
+ evictedCredential,
1471
+ cubeA,
1472
+ createProtocolEnvelope('evict-probe-before', { cursor: null, limit: 1 }),
1473
+ ),
1474
+ 200,
1475
+ 'Pre-eviction seat probe',
1476
+ );
1477
+ const before = await environment.operations.listDrones(credentialA, cubeA);
1478
+ expectStatus(before, 200, 'Pre-eviction roster');
1479
+ invariant(listedDroneIds(before).includes(evictedDrone.id), 'Active drone was absent from roster.');
1480
+
1481
+ const evicted = await environment.operations.evictDrone(
1482
+ credentialA,
1483
+ cubeA,
1484
+ evictedDrone,
1485
+ createProtocolEnvelope('evict-managed-drone', {}),
1486
+ );
1487
+ expectStatus(evicted, 200, 'Drone eviction');
1488
+ invariant(
1489
+ same(decodeEvictDroneResultEnvelope(evicted.body).payload, {
1490
+ drone_id: evictedDrone.id,
1491
+ evicted: true,
1492
+ }),
1493
+ 'Eviction response did not identify the terminal seat.',
1494
+ );
1495
+ invariant(
1496
+ same(await environment.admin.inspectManagedDrone(evictedDrone), {
1497
+ role_id: workerRoleA.id,
1498
+ evicted: true,
1499
+ session_revoked: true,
1500
+ }),
1501
+ 'Eviction did not atomically mark the drone evicted and revoke its session.',
1502
+ );
1503
+ const after = await environment.operations.listDrones(credentialA, cubeA);
1504
+ expectStatus(after, 200, 'Post-eviction roster');
1505
+ invariant(!listedDroneIds(after).includes(evictedDrone.id), 'Evicted drone remained in roster.');
1506
+ expectError(
1507
+ await environment.operations.append(
1508
+ credentialA,
1509
+ cubeA,
1510
+ createProtocolEnvelope('evict-direct-target', {
1511
+ message: 'must-not-fan-out',
1512
+ visibility: 'direct',
1513
+ recipientDroneIds: [evictedDrone.id],
1514
+ }),
1515
+ ),
1516
+ 404,
1517
+ ErrorCode.NOT_FOUND,
1518
+ 'Evicted direct recipient',
1519
+ );
1520
+ expectError(
1521
+ await environment.operations.read(
1522
+ evictedCredential,
1523
+ cubeA,
1524
+ createProtocolEnvelope('evict-probe-after', { cursor: null, limit: 1 }),
1525
+ ),
1526
+ PROTOCOL_HTTP_CONTRACT.drone_evicted_status,
1527
+ ErrorCode.DRONE_EVICTED,
1528
+ 'Evicted seat probe',
1529
+ );
1530
+ return {
1531
+ eviction_status: 200,
1532
+ roster_visible: false,
1533
+ fanout_reachable: false,
1534
+ old_bearer_status: 410,
1535
+ old_bearer_code: ErrorCode.DRONE_EVICTED,
1536
+ };
1537
+ });
1538
+
1539
+ await record('security.drone-session-rejection-causes', async () => {
1540
+ const revokedDrone = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
1541
+ const expiredDrone = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
1542
+ const revokedCredential = await environment.admin.issueManagedDroneSession(revokedDrone);
1543
+ const expiredCredential = await environment.admin.issueManagedDroneSession(expiredDrone);
1544
+ await environment.admin.revokeManagedDroneSession(revokedDrone);
1545
+ await environment.admin.expireManagedDroneSession(expiredDrone);
1546
+ for (const [label, credential] of [
1547
+ ['revoked', revokedCredential],
1548
+ ['expired', expiredCredential],
1549
+ ] as const) {
1550
+ expectError(
1551
+ await environment.operations.read(
1552
+ credential,
1553
+ cubeA,
1554
+ createProtocolEnvelope(`${label}-seat-probe`, { cursor: null, limit: 1 }),
1555
+ ),
1556
+ 401,
1557
+ ErrorCode.SESSION_REVOKED,
1558
+ `${label} seat probe`,
1005
1559
  );
1006
- } catch (error) {
1007
- if (error instanceof ProtocolContractError) code = error.code;
1008
- else throw error;
1009
1560
  }
1010
- invariant(code === ErrorCode.UNSUPPORTED_CAPABILITY, 'Unsupported capability did not fail closed client-side.');
1011
- return { code };
1561
+ return {
1562
+ revoked_status: 401,
1563
+ expired_status: 401,
1564
+ code: ErrorCode.SESSION_REVOKED,
1565
+ };
1012
1566
  });
1013
1567
 
1014
1568
  await record('security.active-stream-revocation', async () => {
@@ -1046,21 +1600,3 @@ export async function runAdapterConformance(
1046
1600
  const normalizedTranscript = results.map(({ id, observations }) => ({ id, observations }));
1047
1601
  return { ok: results.every((result) => result.ok), results, normalizedTranscript };
1048
1602
  }
1049
-
1050
- export async function runEquivalentAdapterConformance(
1051
- cloud: ConformanceEnvironment,
1052
- local: ConformanceEnvironment,
1053
- options: AdapterConformanceOptions = {},
1054
- ): Promise<EquivalentAdapterConformanceReport> {
1055
- const [cloudReport, localReport] = await Promise.all([
1056
- runAdapterConformance(cloud, options),
1057
- runAdapterConformance(local, options),
1058
- ]);
1059
- const equivalent = same(cloudReport.normalizedTranscript, localReport.normalizedTranscript);
1060
- return {
1061
- ok: cloudReport.ok && localReport.ok && equivalent,
1062
- cloud: cloudReport,
1063
- local: localReport,
1064
- equivalent,
1065
- };
1066
- }