borgmcp-shared 0.5.1 → 0.6.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.
- package/README.md +30 -8
- package/dist/conformance/adapter.d.ts +54 -1
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +265 -1
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +9 -0
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +57 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +30 -2
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +62 -4
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/types.d.ts +16 -5
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/runtime-metadata.d.ts +28 -0
- package/dist/runtime-metadata.d.ts.map +1 -0
- package/dist/runtime-metadata.js +251 -0
- package/dist/runtime-metadata.js.map +1 -0
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +5 -3
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +28 -0
- package/docs/enrollment.md +13 -4
- package/docs/releasing.md +16 -5
- package/package.json +5 -1
- package/src/conformance/adapter.ts +457 -0
- package/src/conformance/index.ts +66 -1
- package/src/index.ts +1 -0
- package/src/protocol/contract.ts +110 -5
- package/src/protocol/types.ts +18 -5
- package/src/runtime-metadata.ts +304 -0
- package/src/templates.ts +5 -3
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ import { TEMPLATES } from 'borgmcp-shared/templates';
|
|
|
34
34
|
import { patchRoleSectionText } from 'borgmcp-shared/role-section';
|
|
35
35
|
import { compareBroadcastHwm } from 'borgmcp-shared/log-stream-hwm';
|
|
36
36
|
import { formatDroneAddressToken } from 'borgmcp-shared/drone-address';
|
|
37
|
+
import { canonicalizeRepositoryIdentity } from 'borgmcp-shared/runtime-metadata';
|
|
37
38
|
import type { AppendLogRequest } from 'borgmcp-shared/protocol';
|
|
38
39
|
import { ADAPTER_CONFORMANCE_FIXTURES } from 'borgmcp-shared/conformance';
|
|
39
40
|
```
|
|
@@ -50,6 +51,8 @@ The supported subpaths are:
|
|
|
50
51
|
- `borgmcp-shared/role-section`: lossless role-text parsing and patching.
|
|
51
52
|
- `borgmcp-shared/log-stream-hwm`: broadcast cursor ordering.
|
|
52
53
|
- `borgmcp-shared/drone-address`: stable short drone-address rendering.
|
|
54
|
+
- `borgmcp-shared/runtime-metadata`: pure validation and canonical repository
|
|
55
|
+
identity helpers for advisory local seat metadata.
|
|
53
56
|
|
|
54
57
|
Generated declaration files are included in every published package. Public
|
|
55
58
|
functions and contracts include API documentation in their TypeScript sources.
|
|
@@ -94,6 +97,12 @@ versioned request and success envelopes. An evicted seat's former bearer receive
|
|
|
94
97
|
the terminal `410 DRONE_EVICTED` signal; revoked sessions return
|
|
95
98
|
`401 SESSION_REVOKED`, while expired sessions return the only recoverable
|
|
96
99
|
authentication outcome, `401 AUTH_EXPIRED`.
|
|
100
|
+
An authenticated drone session updates only its own advisory identity with
|
|
101
|
+
`PATCH /api/cubes/:cubeId/drones/self/metadata`. The strict patch carries no
|
|
102
|
+
target seat ID. Metadata never grants authority or changes role, wake, liveness,
|
|
103
|
+
timestamp, log, routing, or model-execution state.
|
|
104
|
+
Identity responses include `runtime_metadata_reported`, keeping an omitted
|
|
105
|
+
attach report distinct from a reported all-null or explicitly cleared state.
|
|
97
106
|
See [docs/enrollment.md](docs/enrollment.md) for purpose-bound owner enrollment,
|
|
98
107
|
ordinary ungranted enrollment, cube creation, pending enrollment, and retry contracts.
|
|
99
108
|
|
|
@@ -107,6 +116,9 @@ credential misuse, isolation and revocation, SSE framing/replay/cursor ordering,
|
|
|
107
116
|
executable enrollment authority/retry/mismatch/redaction and cube-create
|
|
108
117
|
idempotency, acks, claims, decisions, cube-scoped drone reassignment, role-class
|
|
109
118
|
and single-seat invariants, eviction exclusion, and terminal bearer signaling.
|
|
119
|
+
The same runner covers complete attach reports, own-seat metadata self-healing,
|
|
120
|
+
canonical repository identity, invalid-patch atomicity, cross-cube isolation,
|
|
121
|
+
secret non-echo, and authority/liveness/log non-interference.
|
|
110
122
|
Manage-scoped cube, role, taxonomy, decision, and drone operations also share an
|
|
111
123
|
authority matrix: managing parents may mutate; known same-cube read/write
|
|
112
124
|
parents receive `403 ACCESS_DENIED`; drone sessions remain non-managing; and
|
|
@@ -120,8 +132,8 @@ its local/self-hosted implementation. The package does not define a second
|
|
|
120
132
|
authority, migration target, or fallback implementation.
|
|
121
133
|
|
|
122
134
|
The package's own suite covers built-in templates, role-section patching,
|
|
123
|
-
broadcast high-water-mark ordering, drone-address formatting,
|
|
124
|
-
response shapes.
|
|
135
|
+
broadcast high-water-mark ordering, drone-address formatting, runtime metadata
|
|
136
|
+
canonicalization, and current public response shapes.
|
|
125
137
|
|
|
126
138
|
## Compatibility
|
|
127
139
|
|
|
@@ -142,12 +154,22 @@ immutable, but its artifact predates the local/self-hosted package cleanup. The
|
|
|
142
154
|
immutable `v0.4.1` verification artifact exposed a platform-dependent SBOM audit
|
|
143
155
|
and must never be published, moved, reused, rerun, or substituted.
|
|
144
156
|
`borgmcp-shared@0.4.2` is published and immutable. `borgmcp-shared@0.4.3` is the
|
|
145
|
-
immutable protocol v2 session-lifecycle release. `borgmcp-shared@0.5.0` is the
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
immutable protocol v2 session-lifecycle release. `borgmcp-shared@0.5.0` is the published protocol v3 attach-session contract
|
|
158
|
+
release: attach responses carry the exact non-expiring `session: { id }` shape.
|
|
159
|
+
`borgmcp-shared@0.5.1` is published and immutable under the accepted attempt-1
|
|
160
|
+
registry-propagation incident: annotated tag object `045268aa8873da330819860012ecaddb4bc2883c` peels to
|
|
161
|
+
`1981d7373e77f6edb4567872c1544bdbe2b1ef20`; workflow run `29984423571` published exact integrity
|
|
162
|
+
`sha512-XUJq+FjY/cbarU9V1dIWnhNYcqyURTiGb6KyIzg99gy5hk/fEs5ee/8X/qvp7pw1Rshqt2J6I3TVbwJdlde2tA==` and must never be rerun. Independent npm verification
|
|
163
|
+
confirmed its registry signature and SLSA provenance. `borgmcp-shared@0.6.0` was
|
|
164
|
+
never published: annotated tag object `90a1cf686a0ce32a7aef836b0b82a930191b9030`
|
|
165
|
+
peels to protected-main commit `fd69b08586481a60c88099dede8e4e066f73f2f2`;
|
|
166
|
+
attempt-1 workflow run `30054936226` failed in tests before build, packaging,
|
|
167
|
+
authentication, or registry mutation and must never be rerun or moved.
|
|
168
|
+
`borgmcp-shared@0.6.1` is published and immutable. This source now identifies the
|
|
169
|
+
unpublished `0.6.2` Coordinator activation-cadence release. This
|
|
170
|
+
reviewed version bump grants no tag or publish authority; creating annotated
|
|
171
|
+
`v0.6.2` and publishing
|
|
172
|
+
its exact reviewed artifact remain separate, independently gated steps.
|
|
151
173
|
Consumers update shared, server, and client together: peers carrying protocol v2
|
|
152
174
|
and v3 reject each other during credential-free preflight. No registry token
|
|
153
175
|
belongs in this repository, package metadata, lockfiles, or a committed `.npmrc`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CreateCubeResponse, type LogCursor } from '../protocol/index.js';
|
|
1
|
+
import { type CreateCubeResponse, type LogCursor, type DroneRuntimeMetadata } from '../protocol/index.js';
|
|
2
2
|
export interface ConformanceHttpResponse {
|
|
3
3
|
status: number;
|
|
4
4
|
body: unknown;
|
|
@@ -15,6 +15,35 @@ export interface ConformanceRole {
|
|
|
15
15
|
export interface ConformanceDrone {
|
|
16
16
|
readonly id: string;
|
|
17
17
|
}
|
|
18
|
+
export interface ConformanceDroneRuntimeState {
|
|
19
|
+
readonly metadata: DroneRuntimeMetadata;
|
|
20
|
+
readonly metadata_reported: boolean;
|
|
21
|
+
readonly metadata_revision: number;
|
|
22
|
+
readonly cube_id: string;
|
|
23
|
+
readonly role_id: string;
|
|
24
|
+
readonly session_state: 'active' | 'revoked' | 'expired';
|
|
25
|
+
readonly evicted: boolean;
|
|
26
|
+
readonly last_seen: string;
|
|
27
|
+
readonly heartbeat_count: number;
|
|
28
|
+
readonly wake_count: number;
|
|
29
|
+
readonly log_count: number;
|
|
30
|
+
readonly model_turn_count: number;
|
|
31
|
+
readonly grant_access: ConformanceCubeAccess | null;
|
|
32
|
+
readonly server_capabilities: readonly string[];
|
|
33
|
+
readonly principal_revoked: boolean;
|
|
34
|
+
readonly session_bound: boolean;
|
|
35
|
+
readonly last_log_post: string | null;
|
|
36
|
+
readonly last_regen_at: string | null;
|
|
37
|
+
readonly last_read_log_at: string | null;
|
|
38
|
+
readonly last_event_received_at: string | null;
|
|
39
|
+
readonly wake_path: 'live';
|
|
40
|
+
readonly wake_alert: null;
|
|
41
|
+
readonly monitor_armed: boolean;
|
|
42
|
+
readonly sse_connected: boolean;
|
|
43
|
+
readonly claim_count: number;
|
|
44
|
+
readonly decision_count: number;
|
|
45
|
+
readonly routing_eligible: boolean;
|
|
46
|
+
}
|
|
18
47
|
export type ConformanceCubeAccess = 'read' | 'write' | 'manage';
|
|
19
48
|
export interface ConformanceCubeManagementState {
|
|
20
49
|
readonly directive: string;
|
|
@@ -75,6 +104,7 @@ export interface ConformanceAdmin {
|
|
|
75
104
|
readonly evicted: boolean;
|
|
76
105
|
readonly session_revoked: boolean;
|
|
77
106
|
}>;
|
|
107
|
+
inspectDroneRuntimeState(drone: ConformanceDrone): Promise<ConformanceDroneRuntimeState>;
|
|
78
108
|
inspectCubeManagementState(cube: ConformanceCube): Promise<ConformanceCubeManagementState>;
|
|
79
109
|
grantCreateCubeCapability(principal: ConformancePrincipal): Promise<void>;
|
|
80
110
|
issueDroneSession(principal: ConformancePrincipal): Promise<string>;
|
|
@@ -91,6 +121,8 @@ export interface ConformanceOperations {
|
|
|
91
121
|
protocol(credential: string | null): Promise<ConformanceHttpResponse>;
|
|
92
122
|
enroll(request: unknown): Promise<ConformanceHttpResponse>;
|
|
93
123
|
createCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
124
|
+
attach(credential: string, request: unknown): Promise<ConformanceHttpResponse>;
|
|
125
|
+
selfMetadataUpdate(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
94
126
|
append(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
95
127
|
appendRaw(credential: string, cube: ConformanceCube, body: string): Promise<ConformanceHttpResponse>;
|
|
96
128
|
read(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
@@ -163,6 +195,27 @@ export declare const ADAPTER_CONFORMANCE_FIXTURES: readonly [{
|
|
|
163
195
|
}, {
|
|
164
196
|
readonly id: "security.drone-session-rejection-causes";
|
|
165
197
|
readonly area: "security";
|
|
198
|
+
}, {
|
|
199
|
+
readonly id: "metadata.attach-report";
|
|
200
|
+
readonly area: "metadata";
|
|
201
|
+
}, {
|
|
202
|
+
readonly id: "metadata.self-heal-patch";
|
|
203
|
+
readonly area: "metadata";
|
|
204
|
+
}, {
|
|
205
|
+
readonly id: "security.metadata-invalid-atomic";
|
|
206
|
+
readonly area: "security";
|
|
207
|
+
}, {
|
|
208
|
+
readonly id: "security.metadata-own-seat";
|
|
209
|
+
readonly area: "security";
|
|
210
|
+
}, {
|
|
211
|
+
readonly id: "security.metadata-cross-cube-isolation";
|
|
212
|
+
readonly area: "security";
|
|
213
|
+
}, {
|
|
214
|
+
readonly id: "security.metadata-noninterference";
|
|
215
|
+
readonly area: "security";
|
|
216
|
+
}, {
|
|
217
|
+
readonly id: "security.metadata-secret-non-echo";
|
|
218
|
+
readonly area: "security";
|
|
166
219
|
}, {
|
|
167
220
|
readonly id: "security.active-stream-revocation";
|
|
168
221
|
readonly area: "security";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/conformance/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/conformance/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorCode, compareLogCursor, createProtocolEnvelope, decodeCreateCubeResponseEnvelope, decodeAppendLogResultEnvelope, decodeDecisionResultEnvelope, decodeDecisionsResultEnvelope, decodeEnrollmentExchangeResponseEnvelope, decodeEvictDroneResultEnvelope, decodeProtocolEnvelope, decodeProtocolErrorEnvelope, decodeProtocolTagPreflight, decodeReadLogResultEnvelope, decodeReassignDroneResultEnvelope, decodeSseFrames, PROTOCOL_LIMIT_CEILINGS, PROTOCOL_HTTP_CONTRACT, PROTOCOL_VERSION, utf8ByteLength, } from '../protocol/index.js';
|
|
1
|
+
import { ErrorCode, compareLogCursor, createProtocolEnvelope, decodeCreateCubeResponseEnvelope, decodeAppendLogResultEnvelope, decodeDecisionResultEnvelope, decodeDecisionsResultEnvelope, decodeEnrollmentExchangeResponseEnvelope, decodeAttachResponseEnvelope, decodeDroneRuntimeMetadataPatch, decodeEvictDroneResultEnvelope, decodeProtocolEnvelope, decodeProtocolErrorEnvelope, decodeProtocolTagPreflight, decodeReadLogResultEnvelope, decodeReassignDroneResultEnvelope, decodeUpdateDroneRuntimeMetadataResponseEnvelope, decodeSseFrames, PROTOCOL_LIMIT_CEILINGS, PROTOCOL_HTTP_CONTRACT, PROTOCOL_VERSION, utf8ByteLength, } from '../protocol/index.js';
|
|
2
2
|
import { ENROLLMENT_RETRY_CONFORMANCE } from './index.js';
|
|
3
3
|
export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
4
4
|
{ id: 'http.unauthenticated-liveness', area: 'http' },
|
|
@@ -19,6 +19,13 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
|
19
19
|
{ id: 'security.cross-cube-drone-management', area: 'security' },
|
|
20
20
|
{ id: 'drones.evict-terminal-signal', area: 'drones' },
|
|
21
21
|
{ id: 'security.drone-session-rejection-causes', area: 'security' },
|
|
22
|
+
{ id: 'metadata.attach-report', area: 'metadata' },
|
|
23
|
+
{ id: 'metadata.self-heal-patch', area: 'metadata' },
|
|
24
|
+
{ id: 'security.metadata-invalid-atomic', area: 'security' },
|
|
25
|
+
{ id: 'security.metadata-own-seat', area: 'security' },
|
|
26
|
+
{ id: 'security.metadata-cross-cube-isolation', area: 'security' },
|
|
27
|
+
{ id: 'security.metadata-noninterference', area: 'security' },
|
|
28
|
+
{ id: 'security.metadata-secret-non-echo', area: 'security' },
|
|
22
29
|
{ id: 'security.active-stream-revocation', area: 'security' },
|
|
23
30
|
];
|
|
24
31
|
const DEFAULT_STREAM_DEADLINE_MS = 5_000;
|
|
@@ -554,6 +561,9 @@ export async function runAdapterConformance(environment, options = {}) {
|
|
|
554
561
|
let readCredential = '';
|
|
555
562
|
let writeCredential = '';
|
|
556
563
|
let workerSession = '';
|
|
564
|
+
let metadataDroneA;
|
|
565
|
+
let metadataDroneB;
|
|
566
|
+
let metadataSessionA = '';
|
|
557
567
|
await record('security.drone-management-authorization', async () => {
|
|
558
568
|
workerRoleA = await environment.admin.createRole(cubeA, {
|
|
559
569
|
roleClass: 'worker', isHumanSeat: false,
|
|
@@ -788,6 +798,260 @@ export async function runAdapterConformance(environment, options = {}) {
|
|
|
788
798
|
expired_code: ErrorCode.AUTH_EXPIRED,
|
|
789
799
|
};
|
|
790
800
|
});
|
|
801
|
+
const knownMetadata = {
|
|
802
|
+
agent_kind: 'opencode',
|
|
803
|
+
reported_model: 'openai/gpt-5.6-sol',
|
|
804
|
+
working_repo_name: 'Byte-Ventures/borg-mcp',
|
|
805
|
+
working_repo_origin: 'https://github.com/Byte-Ventures/borg-mcp',
|
|
806
|
+
};
|
|
807
|
+
await record('metadata.attach-report', async () => {
|
|
808
|
+
metadataSessionA = 'M'.repeat(43);
|
|
809
|
+
const created = await environment.operations.attach(credentialA, createProtocolEnvelope('metadata-attach-created', {
|
|
810
|
+
cube_id: cubeA.id,
|
|
811
|
+
role_id: workerRoleA.id,
|
|
812
|
+
session_credential: metadataSessionA,
|
|
813
|
+
runtime_metadata: {
|
|
814
|
+
...knownMetadata,
|
|
815
|
+
working_repo_origin: 'git@github.com:Byte-Ventures/borg-mcp.git',
|
|
816
|
+
},
|
|
817
|
+
}));
|
|
818
|
+
expectStatus(created, 200, 'Metadata attach create');
|
|
819
|
+
const createdPayload = decodeAttachResponseEnvelope(created.body).payload;
|
|
820
|
+
invariant(createdPayload.result === 'created', 'First metadata attach did not create a seat.');
|
|
821
|
+
invariant(same(createdPayload.drone.runtime_metadata, knownMetadata), 'Attach did not echo canonical metadata.');
|
|
822
|
+
invariant(createdPayload.drone.runtime_metadata_reported, 'Present attach report was not marked reported.');
|
|
823
|
+
metadataDroneA = { id: createdPayload.drone.id };
|
|
824
|
+
const unavailableSession = 'N'.repeat(43);
|
|
825
|
+
const unavailable = await environment.operations.attach(credentialA, createProtocolEnvelope('metadata-attach-unavailable', {
|
|
826
|
+
cube_id: cubeA.id,
|
|
827
|
+
role_id: workerRoleA.id,
|
|
828
|
+
session_credential: unavailableSession,
|
|
829
|
+
}));
|
|
830
|
+
expectStatus(unavailable, 200, 'Unavailable metadata attach');
|
|
831
|
+
const unavailableDrone = decodeAttachResponseEnvelope(unavailable.body).payload.drone;
|
|
832
|
+
invariant(same(unavailableDrone.runtime_metadata, {
|
|
833
|
+
agent_kind: null,
|
|
834
|
+
reported_model: null,
|
|
835
|
+
working_repo_name: null,
|
|
836
|
+
working_repo_origin: null,
|
|
837
|
+
}), 'Omitted attach report synthesized metadata.');
|
|
838
|
+
invariant(!unavailableDrone.runtime_metadata_reported, 'Omitted attach report was marked reported.');
|
|
839
|
+
const explicitUnknown = await environment.operations.attach(credentialA, createProtocolEnvelope('metadata-attach-explicit-unknown', {
|
|
840
|
+
cube_id: cubeA.id,
|
|
841
|
+
role_id: workerRoleA.id,
|
|
842
|
+
session_credential: 'U'.repeat(43),
|
|
843
|
+
runtime_metadata: {
|
|
844
|
+
agent_kind: null,
|
|
845
|
+
reported_model: null,
|
|
846
|
+
working_repo_name: null,
|
|
847
|
+
working_repo_origin: null,
|
|
848
|
+
},
|
|
849
|
+
}));
|
|
850
|
+
expectStatus(explicitUnknown, 200, 'Explicit unknown metadata attach');
|
|
851
|
+
const explicitUnknownDrone = decodeAttachResponseEnvelope(explicitUnknown.body).payload.drone;
|
|
852
|
+
invariant(explicitUnknownDrone.runtime_metadata_reported, 'All-null attach report was not marked reported.');
|
|
853
|
+
const reused = await environment.operations.attach(credentialA, createProtocolEnvelope('metadata-attach-reused', {
|
|
854
|
+
cube_id: cubeA.id,
|
|
855
|
+
role_id: workerRoleA.id,
|
|
856
|
+
prior_drone_id: metadataDroneA.id,
|
|
857
|
+
session_credential: metadataSessionA,
|
|
858
|
+
runtime_metadata: knownMetadata,
|
|
859
|
+
}));
|
|
860
|
+
expectStatus(reused, 200, 'Metadata attach reuse');
|
|
861
|
+
invariant(decodeAttachResponseEnvelope(reused.body).payload.result === 'reused', 'Prior seat was not reused.');
|
|
862
|
+
const cleared = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope('metadata-clear-all', {
|
|
863
|
+
agent_kind: null,
|
|
864
|
+
reported_model: null,
|
|
865
|
+
working_repo_name: null,
|
|
866
|
+
working_repo_origin: null,
|
|
867
|
+
}));
|
|
868
|
+
expectStatus(cleared, 200, 'Clear-all metadata patch');
|
|
869
|
+
const clearedPayload = decodeUpdateDroneRuntimeMetadataResponseEnvelope(cleared.body).payload;
|
|
870
|
+
invariant(clearedPayload.runtime_metadata_reported, 'Clear-all patch lost reported state.');
|
|
871
|
+
invariant(same(clearedPayload.runtime_metadata, explicitUnknownDrone.runtime_metadata), 'Clear-all patch did not produce explicit unknown metadata.');
|
|
872
|
+
expectStatus(await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope('metadata-restore-known', knownMetadata)), 200, 'Restore known metadata');
|
|
873
|
+
return {
|
|
874
|
+
created: true,
|
|
875
|
+
reused: true,
|
|
876
|
+
report_states: [
|
|
877
|
+
unavailableDrone.runtime_metadata_reported,
|
|
878
|
+
explicitUnknownDrone.runtime_metadata_reported,
|
|
879
|
+
clearedPayload.runtime_metadata_reported,
|
|
880
|
+
],
|
|
881
|
+
canonical_response: true,
|
|
882
|
+
};
|
|
883
|
+
});
|
|
884
|
+
await record('metadata.self-heal-patch', async () => {
|
|
885
|
+
const updates = [
|
|
886
|
+
{ request_id: 'metadata-known-new', patch: { reported_model: 'openai/gpt-5.6-terra' } },
|
|
887
|
+
{ request_id: 'metadata-null-clear', patch: { reported_model: null } },
|
|
888
|
+
{ request_id: 'metadata-null-repeat', patch: { reported_model: null } },
|
|
889
|
+
{ request_id: 'metadata-same-value', patch: { agent_kind: 'opencode' } },
|
|
890
|
+
];
|
|
891
|
+
for (const update of updates) {
|
|
892
|
+
const response = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope(update.request_id, update.patch));
|
|
893
|
+
expectStatus(response, 200, update.request_id);
|
|
894
|
+
decodeUpdateDroneRuntimeMetadataResponseEnvelope(response.body);
|
|
895
|
+
}
|
|
896
|
+
const state = await environment.admin.inspectDroneRuntimeState(metadataDroneA);
|
|
897
|
+
invariant(state.metadata.reported_model === null, 'Explicit null did not clear model metadata.');
|
|
898
|
+
invariant(state.metadata.agent_kind === 'opencode', 'Omitted field was not preserved.');
|
|
899
|
+
return { known_replace: true, omitted_unchanged: true, null_clear: true, repeat_safe: true };
|
|
900
|
+
});
|
|
901
|
+
await record('security.metadata-invalid-atomic', async () => {
|
|
902
|
+
const invalidPatches = [
|
|
903
|
+
{},
|
|
904
|
+
{ agent_kind: 'OpenCode' },
|
|
905
|
+
{ reported_model: '' },
|
|
906
|
+
{ reported_model: 'a'.repeat(161) },
|
|
907
|
+
{ reported_model: 'safe\u001b[2J' },
|
|
908
|
+
{ reported_model: 'safe\u061cnext' },
|
|
909
|
+
{ working_repo_name: 'owner/repo' },
|
|
910
|
+
{ working_repo_name: 'owner/repo', working_repo_origin: 'https://user:secret@github.com/owner/repo' },
|
|
911
|
+
{
|
|
912
|
+
working_repo_name: 'owner/repo',
|
|
913
|
+
working_repo_origin: ['file:/', '', 'Users', 'secret', 'repo'].join('/'),
|
|
914
|
+
},
|
|
915
|
+
{ role_id: workerRoleA.id },
|
|
916
|
+
{ last_seen: '2026-07-14T10:00:00.000Z' },
|
|
917
|
+
];
|
|
918
|
+
for (const [index, patch] of invalidPatches.entries()) {
|
|
919
|
+
const before = await environment.admin.inspectDroneRuntimeState(metadataDroneA);
|
|
920
|
+
const response = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope(`metadata-invalid-${index}`, patch));
|
|
921
|
+
expectError(response, 400, ErrorCode.INVALID_INPUT, `Invalid metadata patch ${index}`);
|
|
922
|
+
invariant(same(await environment.admin.inspectDroneRuntimeState(metadataDroneA), before), `Invalid metadata patch ${index} partially mutated state.`);
|
|
923
|
+
}
|
|
924
|
+
return { cases: invalidPatches.length, atomic: true, prior_safe_state_retained: true };
|
|
925
|
+
});
|
|
926
|
+
await record('security.metadata-own-seat', async () => {
|
|
927
|
+
const peer = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
928
|
+
const peerBefore = await environment.admin.inspectDroneRuntimeState(peer);
|
|
929
|
+
const managedBefore = await environment.admin.inspectDroneRuntimeState(managedWorker);
|
|
930
|
+
const ownBefore = await environment.admin.inspectDroneRuntimeState(metadataDroneA);
|
|
931
|
+
const response = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope('metadata-own-seat', { agent_kind: 'claude' }));
|
|
932
|
+
expectStatus(response, 200, 'Own-seat metadata update');
|
|
933
|
+
invariant(same(await environment.admin.inspectDroneRuntimeState(peer), peerBefore), 'Own-seat update mutated a peer seat.');
|
|
934
|
+
invariant(same(await environment.admin.inspectDroneRuntimeState(managedWorker), managedBefore), 'Own-seat update mutated another existing seat.');
|
|
935
|
+
invariant((await environment.admin.inspectDroneRuntimeState(metadataDroneA)).role_id === ownBefore.role_id, 'Metadata update changed the current seat role.');
|
|
936
|
+
expectError(await environment.operations.selfMetadataUpdate(credentialA, cubeA, createProtocolEnvelope('metadata-manager-denied', { agent_kind: 'codex' })), 403, ErrorCode.ACCESS_DENIED, 'Manager metadata update');
|
|
937
|
+
expectError(await environment.operations.selfMetadataUpdate('Z'.repeat(43), cubeA, createProtocolEnvelope('metadata-invalid-session', { agent_kind: null })), 401, ErrorCode.AUTH_INVALID, 'Unknown metadata session');
|
|
938
|
+
const rejectedStates = [];
|
|
939
|
+
const revoked = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
940
|
+
const revokedSession = await environment.admin.issueManagedDroneSession(revoked);
|
|
941
|
+
await environment.admin.revokeManagedDroneSession(revoked);
|
|
942
|
+
rejectedStates.push(['revoked', revoked, revokedSession, 401, ErrorCode.SESSION_REVOKED]);
|
|
943
|
+
const expired = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
944
|
+
const expiredSession = await environment.admin.issueManagedDroneSession(expired);
|
|
945
|
+
await environment.admin.expireManagedDroneSession(expired);
|
|
946
|
+
rejectedStates.push(['expired', expired, expiredSession, 401, ErrorCode.AUTH_EXPIRED]);
|
|
947
|
+
const evicted = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
948
|
+
const evictedSession = await environment.admin.issueManagedDroneSession(evicted);
|
|
949
|
+
expectStatus(await environment.operations.evictDrone(credentialA, cubeA, evicted, createProtocolEnvelope('metadata-evict-seat', {})), 200, 'Metadata seat eviction');
|
|
950
|
+
rejectedStates.push(['evicted', evicted, evictedSession, 410, ErrorCode.DRONE_EVICTED]);
|
|
951
|
+
for (const [label, drone, session, status, code] of rejectedStates) {
|
|
952
|
+
const before = await environment.admin.inspectDroneRuntimeState(drone);
|
|
953
|
+
expectError(await environment.operations.selfMetadataUpdate(session, cubeA, createProtocolEnvelope(`metadata-${label}-denied`, { agent_kind: null })), status, code, `${label} metadata session`);
|
|
954
|
+
invariant(same(await environment.admin.inspectDroneRuntimeState(drone), before), `${label} metadata denial mutated state.`);
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
own_seat_only: true,
|
|
958
|
+
manager_denied: true,
|
|
959
|
+
unknown_session_denied: true,
|
|
960
|
+
revoked_expired_evicted_denied: true,
|
|
961
|
+
};
|
|
962
|
+
});
|
|
963
|
+
await record('security.metadata-cross-cube-isolation', async () => {
|
|
964
|
+
const sessionB = 'P'.repeat(43);
|
|
965
|
+
const metadataRoleB = await environment.admin.createRole(cubeB, {
|
|
966
|
+
roleClass: 'worker', isHumanSeat: false,
|
|
967
|
+
});
|
|
968
|
+
const attachedB = await environment.operations.attach(credentialB, createProtocolEnvelope('metadata-attach-b', {
|
|
969
|
+
cube_id: cubeB.id,
|
|
970
|
+
role_id: metadataRoleB.id,
|
|
971
|
+
session_credential: sessionB,
|
|
972
|
+
runtime_metadata: {
|
|
973
|
+
agent_kind: 'codex',
|
|
974
|
+
reported_model: null,
|
|
975
|
+
working_repo_name: 'Byte-Ventures/borg-mcp-server',
|
|
976
|
+
working_repo_origin: 'https://github.com/Byte-Ventures/borg-mcp-server',
|
|
977
|
+
},
|
|
978
|
+
}));
|
|
979
|
+
expectStatus(attachedB, 200, 'Cube B metadata attach');
|
|
980
|
+
metadataDroneB = { id: decodeAttachResponseEnvelope(attachedB.body).payload.drone.id };
|
|
981
|
+
const beforeB = await environment.admin.inspectDroneRuntimeState(metadataDroneB);
|
|
982
|
+
const foreign = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeB, createProtocolEnvelope('metadata-cross-cube', { reported_model: 'foreign/probe' }));
|
|
983
|
+
expectError(foreign, 404, ErrorCode.NOT_FOUND, 'Cross-cube metadata update');
|
|
984
|
+
const unknown = await environment.operations.selfMetadataUpdate(metadataSessionA, { id: '90000000-0000-4000-8000-000000000001' }, createProtocolEnvelope('metadata-unknown-cube', { reported_model: 'foreign/probe' }));
|
|
985
|
+
expectError(unknown, 404, ErrorCode.NOT_FOUND, 'Unknown-cube metadata update');
|
|
986
|
+
invariant(same(foreign.body, unknown.body), 'Foreign and unknown metadata probes were distinguishable.');
|
|
987
|
+
invariant(same(await environment.admin.inspectDroneRuntimeState(metadataDroneB), beforeB), 'Cross-cube metadata update mutated the foreign seat.');
|
|
988
|
+
const listedA = await environment.operations.listDrones(credentialA, cubeA);
|
|
989
|
+
invariant(!listedDroneIds(listedA).includes(metadataDroneB.id), 'Cube A roster disclosed cube B metadata seat.');
|
|
990
|
+
return { update_status: 404, foreign_unknown_indistinguishable: true, foreign_unchanged: true, roster_isolated: true };
|
|
991
|
+
});
|
|
992
|
+
await record('security.metadata-noninterference', async () => {
|
|
993
|
+
const before = await environment.admin.inspectDroneRuntimeState(metadataDroneA);
|
|
994
|
+
for (const [requestId, patch] of [
|
|
995
|
+
['metadata-noninterference-known', { reported_model: 'openai/gpt-5.6-sol' }],
|
|
996
|
+
['metadata-noninterference-repeat', { reported_model: 'openai/gpt-5.6-sol' }],
|
|
997
|
+
['metadata-noninterference-null', { reported_model: null }],
|
|
998
|
+
]) {
|
|
999
|
+
expectStatus(await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope(requestId, patch)), 200, requestId);
|
|
1000
|
+
}
|
|
1001
|
+
expectError(await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope('metadata-noninterference-reject', { authority: 'manage' })), 400, ErrorCode.INVALID_INPUT, 'Rejected metadata non-interference update');
|
|
1002
|
+
const reattached = await environment.operations.attach(credentialA, createProtocolEnvelope('metadata-noninterference-reattach', {
|
|
1003
|
+
cube_id: cubeA.id,
|
|
1004
|
+
role_id: workerRoleA.id,
|
|
1005
|
+
prior_drone_id: metadataDroneA.id,
|
|
1006
|
+
session_credential: metadataSessionA,
|
|
1007
|
+
}));
|
|
1008
|
+
expectStatus(reattached, 200, 'Metadata non-interference reattach');
|
|
1009
|
+
const after = await environment.admin.inspectDroneRuntimeState(metadataDroneA);
|
|
1010
|
+
const { metadata: _beforeMetadata, metadata_revision: _beforeRevision, ...beforeInvariant } = before;
|
|
1011
|
+
const { metadata: _afterMetadata, metadata_revision: _afterRevision, ...afterInvariant } = after;
|
|
1012
|
+
invariant(same(beforeInvariant, afterInvariant), 'Metadata update changed authority/liveness/log state.');
|
|
1013
|
+
return {
|
|
1014
|
+
authority_unchanged: true,
|
|
1015
|
+
role_unchanged: true,
|
|
1016
|
+
liveness_unchanged: true,
|
|
1017
|
+
logs_unchanged: true,
|
|
1018
|
+
model_turns_unchanged: true,
|
|
1019
|
+
};
|
|
1020
|
+
});
|
|
1021
|
+
await record('security.metadata-secret-non-echo', async () => {
|
|
1022
|
+
const marker = 'SECRET-METADATA-MARKER';
|
|
1023
|
+
const before = await environment.admin.inspectDroneRuntimeState(metadataDroneA);
|
|
1024
|
+
const response = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope('metadata-secret', {
|
|
1025
|
+
working_repo_name: 'owner/repo',
|
|
1026
|
+
working_repo_origin: `https://user:${marker}@github.com/owner/repo?token=${marker}`,
|
|
1027
|
+
}));
|
|
1028
|
+
expectError(response, 400, ErrorCode.INVALID_INPUT, 'Secret-bearing metadata update');
|
|
1029
|
+
invariant(!JSON.stringify(response.body).includes(marker), 'Metadata error echoed hostile secret input.');
|
|
1030
|
+
invariant(same(await environment.admin.inspectDroneRuntimeState(metadataDroneA), before), 'Secret-bearing metadata input was persisted.');
|
|
1031
|
+
const hostileKeys = [
|
|
1032
|
+
'SECRET-METADATA-KEY-MARKER',
|
|
1033
|
+
`control\u001b[2J`,
|
|
1034
|
+
`bidi\u061cmarker`,
|
|
1035
|
+
`token_${'a'.repeat(48)}`,
|
|
1036
|
+
];
|
|
1037
|
+
for (const [index, key] of hostileKeys.entries()) {
|
|
1038
|
+
const rejected = await environment.operations.selfMetadataUpdate(metadataSessionA, cubeA, createProtocolEnvelope(`metadata-secret-key-${index}`, { [key]: 'x' }));
|
|
1039
|
+
expectError(rejected, 400, ErrorCode.INVALID_INPUT, `Hostile metadata key ${index}`);
|
|
1040
|
+
invariant(!JSON.stringify(rejected.body).includes(key), `Metadata error echoed hostile key ${index}.`);
|
|
1041
|
+
}
|
|
1042
|
+
const logs = await environment.operations.read(credentialA, cubeA, createProtocolEnvelope('metadata-secret-log-read', { cursor: null, limit: 500 }));
|
|
1043
|
+
expectStatus(logs, 200, 'Metadata secret log read');
|
|
1044
|
+
for (const hostile of [marker, ...hostileKeys]) {
|
|
1045
|
+
invariant(!JSON.stringify(logs.body).includes(hostile), 'Metadata input leaked into activity logs.');
|
|
1046
|
+
}
|
|
1047
|
+
return {
|
|
1048
|
+
rejected_pre_persistence: true,
|
|
1049
|
+
response_secret_free: true,
|
|
1050
|
+
state_secret_free: true,
|
|
1051
|
+
log_secret_free: true,
|
|
1052
|
+
unknown_key_secret_free: true,
|
|
1053
|
+
};
|
|
1054
|
+
});
|
|
791
1055
|
await record('security.active-stream-revocation', async () => {
|
|
792
1056
|
invariant(liveCursor, 'Stream fixture did not produce a live cursor.');
|
|
793
1057
|
const opened = await environment.operations.openStream(credentialA, cubeA, liveCursor);
|