borgmcp-shared 0.7.0 → 0.8.0
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 +8 -56
- package/RELEASES.md +33 -0
- package/dist/conformance/adapter.d.ts +22 -0
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +169 -2
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +22 -1
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +78 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +32 -1
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +212 -2
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/errors.d.ts +1 -0
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +1 -0
- package/dist/protocol/errors.js.map +1 -1
- package/dist/protocol/sse.d.ts +6 -2
- package/dist/protocol/sse.d.ts.map +1 -1
- package/dist/protocol/sse.js +8 -2
- package/dist/protocol/sse.js.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/templates.d.ts +1 -0
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +14 -3
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +15 -4
- package/docs/enrollment.md +5 -4
- package/docs/releasing.md +4 -3
- package/package.json +2 -1
- package/src/conformance/adapter.ts +321 -0
- package/src/conformance/index.ts +102 -1
- package/src/protocol/contract.ts +260 -2
- package/src/protocol/errors.ts +1 -0
- package/src/protocol/sse.ts +17 -2
- package/src/protocol/version.ts +2 -2
- package/src/templates.ts +15 -3
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ functions and contracts include API documentation in their TypeScript sources.
|
|
|
59
59
|
|
|
60
60
|
## Handshake
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Key paths in the repository-first HTTP contract include:
|
|
63
63
|
|
|
64
64
|
- `GET /healthz` is the only unauthenticated liveness probe. Success is `204`
|
|
65
65
|
with no body or identifying metadata.
|
|
@@ -87,6 +87,10 @@ The repository-first HTTP contract has six shared paths:
|
|
|
87
87
|
`manage` authority. The same binding is idempotent; repository and cube
|
|
88
88
|
conflicts return distinct non-enumerating error codes, and a conflict,
|
|
89
89
|
validation failure, or permission failure performs no mutation.
|
|
90
|
+
- `DELETE /api/cubes/:cubeId` requires cube `manage` authority and atomically
|
|
91
|
+
deletes the cube and its scoped state. Formerly authorized callers receive
|
|
92
|
+
`410 CUBE_DELETED`, while callers that were never authorized still receive
|
|
93
|
+
`404 NOT_FOUND`.
|
|
90
94
|
|
|
91
95
|
`decodeProtocolTagPreflight` fails closed on any tag other than the exact
|
|
92
96
|
expected version, on any extra field, or on a non-object body — before any
|
|
@@ -120,7 +124,7 @@ ordinary ungranted enrollment, cube creation, pending enrollment, and retry cont
|
|
|
120
124
|
Server and client implementations should run the vectors exported from
|
|
121
125
|
`borgmcp-shared/conformance` against their adapters. The vectors and runner are
|
|
122
126
|
readonly data rather than Vitest-specific helpers, so they work with any test
|
|
123
|
-
runner
|
|
127
|
+
runner supported by the implementation. Cases cover HTTP and canonical errors,
|
|
124
128
|
credential misuse, isolation and revocation, SSE framing/replay/cursor ordering,
|
|
125
129
|
executable enrollment authority/retry/mismatch/redaction and cube-create
|
|
126
130
|
idempotency, explicit repository resolution/adoption and conflict atomicity,
|
|
@@ -134,7 +138,7 @@ authority matrix: managing parents may mutate; known same-cube read/write
|
|
|
134
138
|
parents receive `403 ACCESS_DENIED`; drone sessions remain non-managing; and
|
|
135
139
|
no-grant, foreign, or unknown cubes remain hidden behind `404 NOT_FOUND`.
|
|
136
140
|
|
|
137
|
-
Implement `
|
|
141
|
+
Implement `ConformanceEnvironment` with raw responses from the target adapter,
|
|
138
142
|
then call `runAdapterConformance`. The runner creates and decodes envelopes,
|
|
139
143
|
drives state transitions, and decides pass/fail; adapters do not submit expected
|
|
140
144
|
results. Each server or client adapter runs this single portable suite against
|
|
@@ -157,47 +161,7 @@ together in a coordinated release, with no mixed-version window.
|
|
|
157
161
|
See [docs/compatibility.md](docs/compatibility.md) for the exact-tag policy and
|
|
158
162
|
the coordinated rollout order for introducing protocol changes.
|
|
159
163
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`borgmcp-shared@0.4.0` is the published clean-slate v2 baseline and is
|
|
163
|
-
immutable, but its artifact predates the local/self-hosted package cleanup. The
|
|
164
|
-
immutable `v0.4.1` verification artifact exposed a platform-dependent SBOM audit
|
|
165
|
-
and must never be published, moved, reused, rerun, or substituted.
|
|
166
|
-
`borgmcp-shared@0.4.2` is published and immutable. `borgmcp-shared@0.4.3` is the
|
|
167
|
-
immutable protocol v2 session-lifecycle release. `borgmcp-shared@0.5.0` is the published protocol v3 attach-session contract
|
|
168
|
-
release: attach responses carry the exact non-expiring `session: { id }` shape.
|
|
169
|
-
`borgmcp-shared@0.5.1` is published and immutable under the accepted attempt-1
|
|
170
|
-
registry-propagation incident: annotated tag object `045268aa8873da330819860012ecaddb4bc2883c` peels to
|
|
171
|
-
`1981d7373e77f6edb4567872c1544bdbe2b1ef20`; workflow run `29984423571` published exact integrity
|
|
172
|
-
`sha512-XUJq+FjY/cbarU9V1dIWnhNYcqyURTiGb6KyIzg99gy5hk/fEs5ee/8X/qvp7pw1Rshqt2J6I3TVbwJdlde2tA==` and must never be rerun. Independent npm verification
|
|
173
|
-
confirmed its registry signature and SLSA provenance. `borgmcp-shared@0.6.0` was
|
|
174
|
-
never published: annotated tag object `90a1cf686a0ce32a7aef836b0b82a930191b9030`
|
|
175
|
-
peels to protected-main commit `fd69b08586481a60c88099dede8e4e066f73f2f2`;
|
|
176
|
-
attempt-1 workflow run `30054936226` failed in tests before build, packaging,
|
|
177
|
-
authentication, or registry mutation and must never be rerun or moved.
|
|
178
|
-
`borgmcp-shared@0.6.1`, `borgmcp-shared@0.6.2`, and
|
|
179
|
-
`borgmcp-shared@0.6.3` are published and immutable. `borgmcp-shared@0.6.4` is
|
|
180
|
-
published and immutable: annotated tag object
|
|
181
|
-
`f79b0683686d3c359023a17f6e8a92efd888104a` peels to protected-main commit
|
|
182
|
-
`fa8a2dc072d4ffe2a16d5f02576fead822a2f72e`; successful attempt-1 workflow run
|
|
183
|
-
`30169732628` published registry integrity
|
|
184
|
-
`sha512-Wm4b0uoOAw9JCz5OTHD0Q2uXKkeWYdkVksdeZvRG8l62XGMY+G8GkNEsZT9L533LbVbQ29GhgF0htjDenQThDg==`.
|
|
185
|
-
Never rerun or move that tag. This source now identifies the unpublished `0.7.0`
|
|
186
|
-
protocol-v6 local-model-template release candidate. This version bump grants no
|
|
187
|
-
tag or publish authority; creating annotated `v0.7.0` and publishing its exact
|
|
188
|
-
reviewed artifact remain separate, independently gated steps.
|
|
189
|
-
Consumers update shared, server, and client together: peers carrying protocol v2,
|
|
190
|
-
v3, v4, v5, and v6 reject incompatible tags during credential-free preflight. No
|
|
191
|
-
registry token belongs in this repository, package metadata, lockfiles, or a
|
|
192
|
-
committed `.npmrc`;
|
|
193
|
-
publishing uses protected external credentials and provenance. Successful
|
|
194
|
-
`npm publish` is the workflow terminal boundary; post-publication registry reads
|
|
195
|
-
are outside the immutable release gate.
|
|
196
|
-
|
|
197
|
-
The first client and server releases must consume the reviewed registry release,
|
|
198
|
-
not a Git, tag, or local-path dependency. Public release requires the
|
|
199
|
-
packed-artifact sensitivity audit, ownership and Trusted Publisher checks,
|
|
200
|
-
provenance verification, and explicit release approval.
|
|
164
|
+
See [RELEASES.md](RELEASES.md) for release history.
|
|
201
165
|
|
|
202
166
|
## Security Posture
|
|
203
167
|
|
|
@@ -217,16 +181,4 @@ calling a decoder only after an unbounded response has been buffered is unsafe.
|
|
|
217
181
|
Please report vulnerabilities through the private process in
|
|
218
182
|
[SECURITY.md](SECURITY.md), not through a public issue.
|
|
219
183
|
|
|
220
|
-
## Development
|
|
221
|
-
|
|
222
|
-
```sh
|
|
223
|
-
npm install
|
|
224
|
-
npm test
|
|
225
|
-
npm run check
|
|
226
|
-
npm run build
|
|
227
|
-
npm pack --dry-run
|
|
228
|
-
```
|
|
229
|
-
|
|
230
184
|
See [CONTRIBUTING.md](CONTRIBUTING.md) before proposing a change.
|
|
231
|
-
Release operators must follow the protected [publishing runbook](docs/releasing.md);
|
|
232
|
-
the runbook does not authorize a release.
|
package/RELEASES.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Release History
|
|
2
|
+
|
|
3
|
+
`borgmcp-shared@0.4.0` is the published clean-slate v2 baseline and is
|
|
4
|
+
immutable, but its artifact predates the local/self-hosted package cleanup. The
|
|
5
|
+
immutable `v0.4.1` verification artifact exposed a platform-dependent SBOM audit
|
|
6
|
+
and must never be published, moved, reused, rerun, or substituted.
|
|
7
|
+
`borgmcp-shared@0.4.2` is published and immutable. `borgmcp-shared@0.4.3` is the
|
|
8
|
+
immutable protocol v2 session-lifecycle release. `borgmcp-shared@0.5.0` is the published protocol v3 attach-session contract
|
|
9
|
+
release: attach responses carry the exact non-expiring `session: { id }` shape.
|
|
10
|
+
`borgmcp-shared@0.5.1` is published and immutable under the accepted attempt-1
|
|
11
|
+
registry-propagation incident: annotated tag object `045268aa8873da330819860012ecaddb4bc2883c` peels to
|
|
12
|
+
`1981d7373e77f6edb4567872c1544bdbe2b1ef20`; workflow run `29984423571` published exact integrity
|
|
13
|
+
`sha512-XUJq+FjY/cbarU9V1dIWnhNYcqyURTiGb6KyIzg99gy5hk/fEs5ee/8X/qvp7pw1Rshqt2J6I3TVbwJdlde2tA==` and must never be rerun. Independent npm verification
|
|
14
|
+
confirmed its registry signature and SLSA provenance. `borgmcp-shared@0.6.0` was
|
|
15
|
+
never published: annotated tag object `90a1cf686a0ce32a7aef836b0b82a930191b9030`
|
|
16
|
+
peels to protected-main commit `fd69b08586481a60c88099dede8e4e066f73f2f2`;
|
|
17
|
+
attempt-1 workflow run `30054936226` failed in tests before build, packaging,
|
|
18
|
+
authentication, or registry mutation and must never be rerun or moved.
|
|
19
|
+
`borgmcp-shared@0.6.1`, `borgmcp-shared@0.6.2`, and
|
|
20
|
+
`borgmcp-shared@0.6.3` are published and immutable. `borgmcp-shared@0.6.4` is
|
|
21
|
+
published and immutable: annotated tag object
|
|
22
|
+
`f79b0683686d3c359023a17f6e8a92efd888104a` peels to protected-main commit
|
|
23
|
+
`fa8a2dc072d4ffe2a16d5f02576fead822a2f72e`; successful attempt-1 workflow run
|
|
24
|
+
`30169732628` published registry integrity
|
|
25
|
+
`sha512-Wm4b0uoOAw9JCz5OTHD0Q2uXKkeWYdkVksdeZvRG8l62XGMY+G8GkNEsZT9L533LbVbQ29GhgF0htjDenQThDg==`.
|
|
26
|
+
Never rerun or move that tag.
|
|
27
|
+
`borgmcp-shared@0.7.0` is published and immutable: annotated tag object
|
|
28
|
+
`5ddaf5821c49ac0893fdffbac6115b48d0795281` peels to protected-main commit
|
|
29
|
+
`30af2629052014feba3f3ebf4d9eb29e977e38a7`. Never rerun or move that tag.
|
|
30
|
+
`borgmcp-shared@0.7.1` is published and immutable: annotated tag object
|
|
31
|
+
`672423566ed75cf7704775cb480a75767e7c6851` peels to protected-main commit
|
|
32
|
+
`f4870596ba79702f2e4eb3a6620802d5e538052d`; successful attempt-1 workflow run
|
|
33
|
+
`30625736845` published it. Never rerun or move that tag.
|
|
@@ -82,6 +82,19 @@ export interface ConformanceCreatedCubeState {
|
|
|
82
82
|
human_seat_role_matches: boolean;
|
|
83
83
|
default_worker_role_matches: boolean;
|
|
84
84
|
}
|
|
85
|
+
export interface ConformanceDeletedCubeState {
|
|
86
|
+
readonly cube_exists: boolean;
|
|
87
|
+
readonly role_count: number;
|
|
88
|
+
readonly drone_count: number;
|
|
89
|
+
readonly log_count: number;
|
|
90
|
+
readonly claim_count: number;
|
|
91
|
+
readonly decision_count: number;
|
|
92
|
+
readonly grant_count: number;
|
|
93
|
+
readonly cube_create_binding_count: number;
|
|
94
|
+
readonly repository_association_count: number;
|
|
95
|
+
readonly active_stream_count: number;
|
|
96
|
+
readonly terminal_credential_count: number;
|
|
97
|
+
}
|
|
85
98
|
export interface ConformanceRepositoryCubeFixture {
|
|
86
99
|
cube_id: string;
|
|
87
100
|
name: string;
|
|
@@ -104,6 +117,7 @@ export interface ConformanceStreamResponse extends ConformanceHttpResponse {
|
|
|
104
117
|
}
|
|
105
118
|
export interface ConformanceAdmin {
|
|
106
119
|
reset(): Promise<void>;
|
|
120
|
+
restartAuthority(): Promise<void>;
|
|
107
121
|
createPrincipal(name: string): Promise<ConformancePrincipal>;
|
|
108
122
|
createCube(name: string): Promise<ConformanceCube>;
|
|
109
123
|
grantCube(principal: ConformancePrincipal, cube: ConformanceCube, access?: ConformanceCubeAccess): Promise<void>;
|
|
@@ -128,6 +142,7 @@ export interface ConformanceAdmin {
|
|
|
128
142
|
issueSingleUseInvitation(principal: ConformancePrincipal, purpose: 'owner' | 'client'): Promise<string>;
|
|
129
143
|
observeAuthorityState(): Promise<ConformanceAuthorityState>;
|
|
130
144
|
inspectCreatedCube(creator: ConformancePrincipal, response: CreateCubeResponse): Promise<ConformanceCreatedCubeState>;
|
|
145
|
+
inspectDeletedCube(cube: ConformanceCube): Promise<ConformanceDeletedCubeState>;
|
|
131
146
|
prepareRepositoryCube(cube: ConformanceCube, input: {
|
|
132
147
|
name: string;
|
|
133
148
|
template: CubeTemplate;
|
|
@@ -142,6 +157,7 @@ export interface ConformanceOperations {
|
|
|
142
157
|
protocol(credential: string | null): Promise<ConformanceHttpResponse>;
|
|
143
158
|
enroll(request: unknown): Promise<ConformanceHttpResponse>;
|
|
144
159
|
createCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
160
|
+
deleteCube(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
145
161
|
resolveRepositoryCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
146
162
|
associateRepositoryCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
147
163
|
attach(credential: string, request: unknown): Promise<ConformanceHttpResponse>;
|
|
@@ -170,6 +186,9 @@ export declare const ADAPTER_CONFORMANCE_FIXTURES: readonly [{
|
|
|
170
186
|
}, {
|
|
171
187
|
readonly id: "protocol.credential-free-preflight";
|
|
172
188
|
readonly area: "protocol";
|
|
189
|
+
}, {
|
|
190
|
+
readonly id: "enrollment.invitation-artifact";
|
|
191
|
+
readonly area: "enrollment";
|
|
173
192
|
}, {
|
|
174
193
|
readonly id: "enrollment.retry-authority";
|
|
175
194
|
readonly area: "enrollment";
|
|
@@ -245,6 +264,9 @@ export declare const ADAPTER_CONFORMANCE_FIXTURES: readonly [{
|
|
|
245
264
|
}, {
|
|
246
265
|
readonly id: "security.active-stream-revocation";
|
|
247
266
|
readonly area: "security";
|
|
267
|
+
}, {
|
|
268
|
+
readonly id: "cubes.delete-terminal-cascade";
|
|
269
|
+
readonly area: "cubes";
|
|
248
270
|
}];
|
|
249
271
|
export type AdapterConformanceFixtureId = (typeof ADAPTER_CONFORMANCE_FIXTURES)[number]['id'];
|
|
250
272
|
export interface AdapterConformanceResult {
|
|
@@ -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,8 +1,9 @@
|
|
|
1
|
-
import { ErrorCode, compareLogCursor, createProtocolEnvelope, decodeAssociateRepositoryCubeResponseEnvelope, decodeCreateCubeResponseEnvelope, decodeAppendLogResultEnvelope, decodeDecisionResultEnvelope, decodeDecisionsResultEnvelope, decodeEnrollmentExchangeResponseEnvelope, decodeAttachResponseEnvelope, decodeDroneRuntimeMetadataPatch, decodeEvictDroneResultEnvelope, decodeProtocolEnvelope, decodeProtocolErrorEnvelope, decodeProtocolTagPreflight, decodeReadLogResultEnvelope, decodeResolveRepositoryCubeResponseEnvelope, decodeReassignDroneResultEnvelope, decodeUpdateDroneRuntimeMetadataResponseEnvelope, decodeSseFrames, PROTOCOL_LIMIT_CEILINGS, PROTOCOL_HTTP_CONTRACT, PROTOCOL_VERSION, utf8ByteLength, } from '../protocol/index.js';
|
|
2
|
-
import { CREATE_CUBE_ASSOCIATION_CONFORMANCE, CREATE_CUBE_RETRY_CONFORMANCE, ENROLLMENT_RETRY_CONFORMANCE, } from './index.js';
|
|
1
|
+
import { ErrorCode, compareLogCursor, createProtocolEnvelope, decodeAssociateRepositoryCubeResponseEnvelope, decodeCreateCubeResponseEnvelope, decodeDeleteCubeResponseEnvelope, decodeAppendLogResultEnvelope, decodeDecisionResultEnvelope, decodeDecisionsResultEnvelope, decodeEnrollmentExchangeResponseEnvelope, decodeInvitationArtifact, decodeAttachResponseEnvelope, decodeDroneRuntimeMetadataPatch, decodeEvictDroneResultEnvelope, decodeProtocolEnvelope, decodeProtocolErrorEnvelope, decodeProtocolTagPreflight, decodeReadLogResultEnvelope, decodeResolveRepositoryCubeResponseEnvelope, decodeReassignDroneResultEnvelope, decodeUpdateDroneRuntimeMetadataResponseEnvelope, decodeSseFrames, PROTOCOL_LIMIT_CEILINGS, PROTOCOL_HTTP_CONTRACT, PROTOCOL_VERSION, utf8ByteLength, } from '../protocol/index.js';
|
|
2
|
+
import { CREATE_CUBE_ASSOCIATION_CONFORMANCE, CREATE_CUBE_RETRY_CONFORMANCE, INVITATION_ARTIFACT_CONFORMANCE, ENROLLMENT_RETRY_CONFORMANCE, } from './index.js';
|
|
3
3
|
export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
4
4
|
{ id: 'http.unauthenticated-liveness', area: 'http' },
|
|
5
5
|
{ id: 'protocol.credential-free-preflight', area: 'protocol' },
|
|
6
|
+
{ id: 'enrollment.invitation-artifact', area: 'enrollment' },
|
|
6
7
|
{ id: 'enrollment.retry-authority', area: 'enrollment' },
|
|
7
8
|
{ id: 'repository.explicit-association', area: 'repository' },
|
|
8
9
|
{ id: 'security.adapter-boundary-injection', area: 'security' },
|
|
@@ -28,6 +29,7 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
|
28
29
|
{ id: 'security.metadata-noninterference', area: 'security' },
|
|
29
30
|
{ id: 'security.metadata-secret-non-echo', area: 'security' },
|
|
30
31
|
{ id: 'security.active-stream-revocation', area: 'security' },
|
|
32
|
+
{ id: 'cubes.delete-terminal-cascade', area: 'cubes' },
|
|
31
33
|
];
|
|
32
34
|
const DEFAULT_STREAM_DEADLINE_MS = 5_000;
|
|
33
35
|
const DEFAULT_PENDING_PROBE_MS = 25;
|
|
@@ -208,6 +210,32 @@ export async function runAdapterConformance(environment, options = {}) {
|
|
|
208
210
|
assertStateDelta(before, await environment.admin.observeAuthorityState(), {}, 'Protocol-tag preflight');
|
|
209
211
|
return { authenticated: false, mutation_free: true, protocol_version: preflight.protocol_version };
|
|
210
212
|
});
|
|
213
|
+
await record('enrollment.invitation-artifact', async () => {
|
|
214
|
+
const errors = [];
|
|
215
|
+
for (const vector of INVITATION_ARTIFACT_CONFORMANCE) {
|
|
216
|
+
if (vector.expected === null) {
|
|
217
|
+
let rejected = false;
|
|
218
|
+
try {
|
|
219
|
+
decodeInvitationArtifact(vector.input);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
rejected = true;
|
|
223
|
+
}
|
|
224
|
+
if (!rejected)
|
|
225
|
+
errors.push(`${vector.name}: permissive decoder accepted the invalid artifact.`);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const decoded = decodeInvitationArtifact(vector.input);
|
|
230
|
+
invariant(same(decoded, vector.expected), `${vector.name} decoded a different artifact.`);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
errors.push(`${vector.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
invariant(errors.length === 0, errors.join(' | '));
|
|
237
|
+
return { vectors: INVITATION_ARTIFACT_CONFORMANCE.length };
|
|
238
|
+
});
|
|
211
239
|
await record('enrollment.retry-authority', async () => {
|
|
212
240
|
const retryVectorErrors = [];
|
|
213
241
|
for (const [index, vector] of ENROLLMENT_RETRY_CONFORMANCE.entries()) {
|
|
@@ -1249,6 +1277,145 @@ export async function runAdapterConformance(environment, options = {}) {
|
|
|
1249
1277
|
expectError(rejected, 401, ErrorCode.SESSION_REVOKED, 'Post-revocation request');
|
|
1250
1278
|
return { stream_terminated: true, subsequent_status: 401, subsequent_code: ErrorCode.SESSION_REVOKED };
|
|
1251
1279
|
});
|
|
1280
|
+
await record('cubes.delete-terminal-cascade', async () => {
|
|
1281
|
+
const enrollParent = async (name, marker, retrySuffix) => {
|
|
1282
|
+
const principal = await environment.admin.createPrincipal(name);
|
|
1283
|
+
const invitation = await environment.admin.issueSingleUseInvitation(principal, 'client');
|
|
1284
|
+
const credential = `${marker.repeat(41)}AQ`;
|
|
1285
|
+
expectStatus(await environment.operations.enroll(createProtocolEnvelope(`delete-${name}-enroll`, {
|
|
1286
|
+
invitation,
|
|
1287
|
+
retry_key: `00000000-0000-4000-8000-${retrySuffix.padStart(12, '0')}`,
|
|
1288
|
+
client_credential: credential,
|
|
1289
|
+
client_name: name,
|
|
1290
|
+
})), 201, `${name} enrollment`);
|
|
1291
|
+
return { principal, credential };
|
|
1292
|
+
};
|
|
1293
|
+
const creator = await enrollParent('delete-creator', 'C', '701');
|
|
1294
|
+
const manager = await enrollParent('delete-manager', 'M', '702');
|
|
1295
|
+
const reader = await enrollParent('delete-reader', 'R', '703');
|
|
1296
|
+
const writer = await enrollParent('delete-writer', 'W', '704');
|
|
1297
|
+
const outsider = await enrollParent('delete-outsider', 'O', '705');
|
|
1298
|
+
await environment.admin.grantCreateCubeCapability(creator.principal);
|
|
1299
|
+
const createResponse = await environment.operations.createCube(creator.credential, createProtocolEnvelope('delete-cube-create', {
|
|
1300
|
+
retry_key: '00000000-0000-4000-8000-000000000706',
|
|
1301
|
+
name: 'Disposable Cube',
|
|
1302
|
+
working_repo_name: 'disposable-cube',
|
|
1303
|
+
repository: { kind: 'local', value: '00000000-0000-4000-8000-000000000707' },
|
|
1304
|
+
template: 'default',
|
|
1305
|
+
}));
|
|
1306
|
+
expectStatus(createResponse, 201, 'Disposable cube creation');
|
|
1307
|
+
const created = decodeCreateCubeResponseEnvelope(createResponse.body).payload;
|
|
1308
|
+
const cube = { id: created.cube_id };
|
|
1309
|
+
const role = { id: created.default_worker_role_id };
|
|
1310
|
+
await environment.admin.grantCube(manager.principal, cube, 'manage');
|
|
1311
|
+
await environment.admin.grantCube(reader.principal, cube, 'read');
|
|
1312
|
+
await environment.admin.grantCube(writer.principal, cube, 'write');
|
|
1313
|
+
const drone = await environment.admin.createDrone(creator.principal, cube, role);
|
|
1314
|
+
const droneCredential = await environment.admin.issueManagedDroneSession(drone);
|
|
1315
|
+
const append = await environment.operations.append(creator.credential, cube, createProtocolEnvelope('delete-log', { message: 'deleted with cube' }));
|
|
1316
|
+
expectStatus(append, 201, 'Deletion fixture log');
|
|
1317
|
+
const entry = decodeAppendLogResultEnvelope(append.body).payload.entry;
|
|
1318
|
+
expectStatus(await environment.operations.ack(creator.credential, cube, createProtocolEnvelope('delete-claim', { entry_id: entry.id, kind: 'claim' })), 204, 'Deletion fixture claim');
|
|
1319
|
+
expectStatus(await environment.operations.recordDecision(manager.credential, cube, createProtocolEnvelope('delete-decision', { topic: 'cleanup', decision: 'delete' })), 201, 'Deletion fixture decision');
|
|
1320
|
+
const beforeDenied = await environment.admin.inspectCubeManagementState(cube);
|
|
1321
|
+
for (const [kind, credential] of [
|
|
1322
|
+
['read', reader.credential],
|
|
1323
|
+
['write', writer.credential],
|
|
1324
|
+
['drone-session', droneCredential],
|
|
1325
|
+
]) {
|
|
1326
|
+
expectError(await environment.operations.deleteCube(credential, cube, createProtocolEnvelope(`delete-${kind}-denied`, {})), 403, ErrorCode.ACCESS_DENIED, `${kind} cube deletion`);
|
|
1327
|
+
invariant(same(await environment.admin.inspectCubeManagementState(cube), beforeDenied), `${kind} cube deletion denial mutated the cube.`);
|
|
1328
|
+
}
|
|
1329
|
+
expectError(await environment.operations.deleteCube(outsider.credential, cube, createProtocolEnvelope('delete-outsider-denied', {})), 404, ErrorCode.NOT_FOUND, 'Never-authorized cube deletion');
|
|
1330
|
+
const parentStream = await environment.operations.openStream(manager.credential, cube, null);
|
|
1331
|
+
const droneStream = await environment.operations.openStream(droneCredential, cube, null);
|
|
1332
|
+
expectStatus(parentStream, 200, 'Deletion parent stream');
|
|
1333
|
+
expectStatus(droneStream, 200, 'Deletion drone stream');
|
|
1334
|
+
invariant(parentStream.stream && droneStream.stream, 'Deletion fixture streams did not open.');
|
|
1335
|
+
const parentEvents = new SseEventReader(parentStream.stream);
|
|
1336
|
+
const droneEvents = new SseEventReader(droneStream.stream);
|
|
1337
|
+
for (const streamReader of [parentEvents, droneEvents]) {
|
|
1338
|
+
invariant((await streamReader.next()).type === 'log', 'Deletion stream omitted replay log.');
|
|
1339
|
+
const bookmark = await streamReader.next();
|
|
1340
|
+
invariant(bookmark.type === 'bookmark' && bookmark.replay_complete, 'Deletion stream omitted bookmark.');
|
|
1341
|
+
}
|
|
1342
|
+
const parentTerminal = parentEvents.next();
|
|
1343
|
+
const droneTerminal = droneEvents.next();
|
|
1344
|
+
await provePending(parentTerminal, 'Parent deletion terminal event', pendingProbeMs);
|
|
1345
|
+
await provePending(droneTerminal, 'Drone deletion terminal event', pendingProbeMs);
|
|
1346
|
+
const deleted = await environment.operations.deleteCube(manager.credential, cube, createProtocolEnvelope('delete-managed-cube', {}));
|
|
1347
|
+
expectStatus(deleted, 200, 'Managed cube deletion');
|
|
1348
|
+
invariant(same(decodeDeleteCubeResponseEnvelope(deleted.body).payload, {
|
|
1349
|
+
cube_id: cube.id,
|
|
1350
|
+
deleted: true,
|
|
1351
|
+
}), 'Cube deletion response did not identify the terminal cube.');
|
|
1352
|
+
for (const [kind, event] of [
|
|
1353
|
+
['parent', await within(parentTerminal, 'Parent deletion terminal event', streamDeadlineMs)],
|
|
1354
|
+
['drone', await within(droneTerminal, 'Drone deletion terminal event', streamDeadlineMs)],
|
|
1355
|
+
]) {
|
|
1356
|
+
invariant(event.type === 'error' && event.error.error.code === ErrorCode.CUBE_DELETED, `${kind} stream did not receive CUBE_DELETED.`);
|
|
1357
|
+
}
|
|
1358
|
+
for (const streamReader of [parentEvents, droneEvents]) {
|
|
1359
|
+
let closed = false;
|
|
1360
|
+
try {
|
|
1361
|
+
await within(streamReader.next(), 'Deleted cube stream close', streamDeadlineMs);
|
|
1362
|
+
}
|
|
1363
|
+
catch (error) {
|
|
1364
|
+
if (error instanceof Error && error.message.includes('did not settle'))
|
|
1365
|
+
throw error;
|
|
1366
|
+
closed = true;
|
|
1367
|
+
}
|
|
1368
|
+
invariant(closed, 'Deleted cube stream yielded after its terminal error.');
|
|
1369
|
+
await streamReader.close();
|
|
1370
|
+
}
|
|
1371
|
+
invariant(same(await environment.admin.inspectDeletedCube(cube), {
|
|
1372
|
+
cube_exists: false,
|
|
1373
|
+
role_count: 0,
|
|
1374
|
+
drone_count: 0,
|
|
1375
|
+
log_count: 0,
|
|
1376
|
+
claim_count: 0,
|
|
1377
|
+
decision_count: 0,
|
|
1378
|
+
grant_count: 0,
|
|
1379
|
+
cube_create_binding_count: 0,
|
|
1380
|
+
repository_association_count: 0,
|
|
1381
|
+
active_stream_count: 0,
|
|
1382
|
+
terminal_credential_count: 5,
|
|
1383
|
+
}), 'Cube deletion did not atomically remove cube-owned state and preserve only terminal credentials.');
|
|
1384
|
+
for (const [kind, credential] of [
|
|
1385
|
+
['creator', creator.credential],
|
|
1386
|
+
['manager', manager.credential],
|
|
1387
|
+
['reader', reader.credential],
|
|
1388
|
+
['writer', writer.credential],
|
|
1389
|
+
['drone', droneCredential],
|
|
1390
|
+
]) {
|
|
1391
|
+
expectError(await environment.operations.read(credential, cube, createProtocolEnvelope(`delete-${kind}-terminal`, { cursor: null, limit: 1 })), PROTOCOL_HTTP_CONTRACT.cube_deleted_status, ErrorCode.CUBE_DELETED, `${kind} post-delete request`);
|
|
1392
|
+
expectError(await environment.operations.deleteCube(credential, cube, createProtocolEnvelope(`delete-${kind}-repeat-terminal`, {})), PROTOCOL_HTTP_CONTRACT.cube_deleted_status, ErrorCode.CUBE_DELETED, `${kind} post-delete DELETE`);
|
|
1393
|
+
}
|
|
1394
|
+
expectError(await environment.operations.deleteCube(outsider.credential, cube, createProtocolEnvelope('delete-outsider-hidden-repeat', {})), 404, ErrorCode.NOT_FOUND, 'Never-authorized post-delete DELETE');
|
|
1395
|
+
expectError(await environment.operations.read(outsider.credential, cube, createProtocolEnvelope('delete-outsider-hidden', { cursor: null, limit: 1 })), 404, ErrorCode.NOT_FOUND, 'Never-authorized post-delete request');
|
|
1396
|
+
await environment.admin.restartAuthority();
|
|
1397
|
+
for (const [kind, credential] of [
|
|
1398
|
+
['creator', creator.credential],
|
|
1399
|
+
['manager', manager.credential],
|
|
1400
|
+
['reader', reader.credential],
|
|
1401
|
+
['writer', writer.credential],
|
|
1402
|
+
['drone', droneCredential],
|
|
1403
|
+
]) {
|
|
1404
|
+
expectError(await environment.operations.read(credential, cube, createProtocolEnvelope(`delete-${kind}-after-restart`, { cursor: null, limit: 1 })), PROTOCOL_HTTP_CONTRACT.cube_deleted_status, ErrorCode.CUBE_DELETED, `${kind} post-restart deleted-cube request`);
|
|
1405
|
+
expectError(await environment.operations.deleteCube(credential, cube, createProtocolEnvelope(`delete-${kind}-repeat-after-restart`, {})), PROTOCOL_HTTP_CONTRACT.cube_deleted_status, ErrorCode.CUBE_DELETED, `${kind} post-restart DELETE`);
|
|
1406
|
+
}
|
|
1407
|
+
expectError(await environment.operations.deleteCube(outsider.credential, cube, createProtocolEnvelope('delete-outsider-hidden-repeat-after-restart', {})), 404, ErrorCode.NOT_FOUND, 'Never-authorized post-restart DELETE');
|
|
1408
|
+
expectError(await environment.operations.read(outsider.credential, cube, createProtocolEnvelope('delete-outsider-hidden-after-restart', { cursor: null, limit: 1 })), 404, ErrorCode.NOT_FOUND, 'Never-authorized post-restart request');
|
|
1409
|
+
return {
|
|
1410
|
+
deletion_status: 200,
|
|
1411
|
+
cascade_complete: true,
|
|
1412
|
+
terminal_stream_error: ErrorCode.CUBE_DELETED,
|
|
1413
|
+
terminal_http_status: 410,
|
|
1414
|
+
terminal_state_durable_after_restart: true,
|
|
1415
|
+
unknown_hidden_status: 404,
|
|
1416
|
+
non_member_manager: true,
|
|
1417
|
+
};
|
|
1418
|
+
});
|
|
1252
1419
|
const normalizedTranscript = results.map(({ id, observations }) => ({ id, observations }));
|
|
1253
1420
|
return { ok: results.every((result) => result.ok), results, normalizedTranscript };
|
|
1254
1421
|
}
|