borgmcp-shared 0.12.2 → 0.13.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 +14 -0
- package/RELEASES.md +16 -0
- package/dist/conformance/adapter.d.ts +7 -0
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +157 -3
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +33 -0
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +10 -0
- package/dist/conformance/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +36 -2
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +19 -5
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/coordination.d.ts.map +1 -1
- package/dist/protocol/coordination.js +11 -2
- package/dist/protocol/coordination.js.map +1 -1
- package/dist/protocol/documents.d.ts +78 -0
- package/dist/protocol/documents.d.ts.map +1 -0
- package/dist/protocol/documents.js +196 -0
- package/dist/protocol/documents.js.map +1 -0
- package/dist/protocol/errors.d.ts +5 -0
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +5 -0
- package/dist/protocol/errors.js.map +1 -1
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.d.ts.map +1 -1
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/sse.d.ts +2 -2
- package/dist/protocol/sse.d.ts.map +1 -1
- package/dist/protocol/sse.js +33 -4
- package/dist/protocol/sse.js.map +1 -1
- package/dist/protocol/types.d.ts +7 -0
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.d.ts.map +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/protocol/version.js.map +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +8 -4
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +5 -0
- package/docs/cube-documents.md +35 -0
- package/docs/release-records.json +29 -0
- package/docs/releases/0.13.0.md +7 -0
- package/package.json +1 -1
- package/src/conformance/adapter.ts +340 -1
- package/src/conformance/index.ts +11 -0
- package/src/protocol/contract.ts +19 -5
- package/src/protocol/coordination.ts +15 -1
- package/src/protocol/documents.ts +256 -0
- package/src/protocol/errors.ts +5 -0
- package/src/protocol/index.ts +1 -0
- package/src/protocol/sse.ts +38 -3
- package/src/protocol/types.ts +7 -0
- package/src/protocol/version.ts +2 -2
- package/src/templates.ts +8 -4
package/README.md
CHANGED
|
@@ -91,6 +91,13 @@ Key paths in the repository-first HTTP contract include:
|
|
|
91
91
|
deletes the cube and its scoped state. Formerly authorized callers receive
|
|
92
92
|
`410 CUBE_DELETED`, while callers that were never authorized still receive
|
|
93
93
|
`404 NOT_FOUND`.
|
|
94
|
+
- `PUT /api/cubes/:cubeId/documents` stores immutable UTF-8 `text/markdown` or
|
|
95
|
+
`text/plain` content under one opaque full id. Titles are required and bounded
|
|
96
|
+
to 120 characters. Optional `supersedes` links form a same-cube linear chain.
|
|
97
|
+
`GET` by full id remains audit-resolvable after author-or-manager removal,
|
|
98
|
+
while active listings delist removed documents. Log posts cite documents with
|
|
99
|
+
the structured `documents` id array; reads render id, title, UTF-8 size, and
|
|
100
|
+
superseded or removed state.
|
|
94
101
|
|
|
95
102
|
`decodeProtocolTagPreflight` fails closed on any tag other than the exact
|
|
96
103
|
expected version, on any extra field, or on a non-object body — before any
|
|
@@ -111,6 +118,9 @@ recipient set, and class routing, then returns the same entry with
|
|
|
111
118
|
`deduplicated: true`. Reusing an author's `post_id` with a changed tuple returns
|
|
112
119
|
`POST_ID_CONFLICT`; another author may independently use the same UUID. Attach responses include the nullable
|
|
113
120
|
`initial_log_cursor` that anchors subsequent log replay.
|
|
121
|
+
At the configurable defaults, log messages up to 1 KiB are accepted silently,
|
|
122
|
+
messages from 1 KiB through 4 KiB return a store-as-document advisory, and
|
|
123
|
+
larger messages are rejected.
|
|
114
124
|
Cube managers reassign a seat with `PATCH /api/cubes/:cubeId/drones/:droneId`
|
|
115
125
|
and evict one with `DELETE` on the same path. Both operations use strict
|
|
116
126
|
versioned request and success envelopes. An evicted seat's former bearer receives
|
|
@@ -124,6 +134,8 @@ Identity responses include `runtime_metadata_reported`, keeping an omitted
|
|
|
124
134
|
attach report distinct from a reported all-null or explicitly cleared state.
|
|
125
135
|
See [docs/enrollment.md](docs/enrollment.md) for purpose-bound owner enrollment,
|
|
126
136
|
ordinary ungranted enrollment, cube creation, pending enrollment, and retry contracts.
|
|
137
|
+
See [docs/cube-documents.md](docs/cube-documents.md) for document budgets,
|
|
138
|
+
supersession, removal, citation, and log-length contracts.
|
|
127
139
|
|
|
128
140
|
## Conformance
|
|
129
141
|
|
|
@@ -139,6 +151,8 @@ and single-seat invariants, eviction exclusion, and terminal bearer signaling.
|
|
|
139
151
|
The same runner covers complete attach reports, own-seat metadata self-healing,
|
|
140
152
|
canonical repository identity, invalid-patch atomicity, cross-cube isolation,
|
|
141
153
|
secret non-echo, and authority/liveness/log non-interference.
|
|
154
|
+
The document lifecycle fixture covers immutable put/get, metadata-only listing,
|
|
155
|
+
removal delisting, and retained exact-id forensic resolution.
|
|
142
156
|
Manage-scoped cube, role, taxonomy, decision, and drone operations also share an
|
|
143
157
|
authority matrix: managing parents may mutate; known same-cube read/write
|
|
144
158
|
parents receive `403 ACCESS_DENIED`; drone sessions remain non-managing; and
|
package/RELEASES.md
CHANGED
|
@@ -50,3 +50,19 @@ external-surface single-owner directive rule. Successful attempt-1 workflow
|
|
|
50
50
|
run `31531549256` published registry integrity
|
|
51
51
|
`sha512-I8mixCbSrLKyOAAyqEI/HZJ8cML2rz3r812Up8pr547OdAk9LxZevdCo7ojG42ZwrUmS5u7iKQPg7Vk1XvtX1g==`.
|
|
52
52
|
Never rerun or move that tag.
|
|
53
|
+
|
|
54
|
+
`borgmcp-shared@0.12.3` is published and immutable. Annotated tag object
|
|
55
|
+
`e410256092974dcef9619415ebdf74acde3e4484` peels to protected-main commit
|
|
56
|
+
`9bfa43ced2af772d7a60f7be7828baaa146b4fd4`; successful tag workflow run
|
|
57
|
+
`31779837930` staged the exact candidate. Subsequent authorized npm stage
|
|
58
|
+
approval made the release live with registry integrity
|
|
59
|
+
`sha512-3GPQ1U7tBxg8Jp1Uac31CKKXQjMv4UNPhs5P6N83CyDXGJvkI88yowVJZGlLuo30eEk6jhERZ9AQWOjHst/sFA==`.
|
|
60
|
+
The tagged commit omitted `docs/releases/0.12.3.md`, so the normal GitHub Release
|
|
61
|
+
operator correctly refused to claim tagged notes. The GitHub Release at
|
|
62
|
+
https://github.com/Byte-Ventures/borg-mcp-shared/releases/tag/v0.12.3 uses an
|
|
63
|
+
explicit post-publication reconstruction from issue #138, merged PR #139, and
|
|
64
|
+
the exact `v0.12.2..v0.12.3` diff: the software-development template gained
|
|
65
|
+
proportionate Coordinator review gates, focused Builder verification and status
|
|
66
|
+
rules, and the tag-restricted npm staging boundary while preserving its seven
|
|
67
|
+
roles and routing contract. No post-hoc file is represented as tagged notes.
|
|
68
|
+
Never rerun or move that tag.
|
|
@@ -163,6 +163,10 @@ export interface ConformanceOperations {
|
|
|
163
163
|
enroll(request: unknown): Promise<ConformanceHttpResponse>;
|
|
164
164
|
createCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
165
165
|
deleteCube(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
166
|
+
putDocument(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
167
|
+
getDocument(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
168
|
+
listDocuments(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
169
|
+
removeDocument(credential: string, cube: ConformanceCube, request: unknown): Promise<ConformanceHttpResponse>;
|
|
166
170
|
resolveRepositoryCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
167
171
|
associateRepositoryCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
|
|
168
172
|
attach(credential: string, request: unknown): Promise<ConformanceHttpResponse>;
|
|
@@ -202,6 +206,9 @@ export declare const ADAPTER_CONFORMANCE_FIXTURES: readonly [{
|
|
|
202
206
|
}, {
|
|
203
207
|
readonly id: "repository.explicit-association";
|
|
204
208
|
readonly area: "repository";
|
|
209
|
+
}, {
|
|
210
|
+
readonly id: "documents.lifecycle";
|
|
211
|
+
readonly area: "documents";
|
|
205
212
|
}, {
|
|
206
213
|
readonly id: "security.adapter-boundary-injection";
|
|
207
214
|
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,11 +1,12 @@
|
|
|
1
|
-
import { ErrorCode, compareLogCursor, createProtocolEnvelope, decodeAssociateRepositoryCubeResponseEnvelope, decodeCreateCubeResponseEnvelope, decodeDeleteCubeResponseEnvelope, decodeAppendLogResultEnvelope, decodeDecisionResultEnvelope, decodeDecisionsResultEnvelope, decodeEnrollmentExchangeResponseEnvelope, decodeInvitationArtifact, decodeAttachResponseEnvelope, decodeDroneRuntimeMetadataPatch, decodeEvictDroneResultEnvelope, decodeDeleteRoleResultEnvelope, decodeRoleRationaleResultEnvelope, decodeProtocolEnvelope, decodeProtocolErrorEnvelope, decodeProtocolTagPreflight, decodeReadLogResultEnvelope, decodeResolveRepositoryCubeResponseEnvelope, decodeReassignDroneResultEnvelope, decodeUpdateDroneRuntimeMetadataResponseEnvelope, decodeSseFrames, PROTOCOL_LIMIT_CEILINGS, PROTOCOL_HTTP_CONTRACT, PROTOCOL_VERSION, ROLE_IN_USE_DELETE_MESSAGE, utf8ByteLength, } from '../protocol/index.js';
|
|
2
|
-
import { APPEND_LOG_IDEMPOTENCY_CONFORMANCE, CREATE_CUBE_ASSOCIATION_CONFORMANCE, CREATE_CUBE_RETRY_CONFORMANCE, INVITATION_ARTIFACT_CONFORMANCE, ENROLLMENT_RETRY_CONFORMANCE, } from './index.js';
|
|
1
|
+
import { ErrorCode, compareLogCursor, createProtocolEnvelope, decodeAssociateRepositoryCubeResponseEnvelope, decodeCreateCubeResponseEnvelope, decodeDeleteCubeResponseEnvelope, decodeAppendLogResultEnvelope, decodeDecisionResultEnvelope, decodeDecisionsResultEnvelope, decodeEnrollmentExchangeResponseEnvelope, decodeInvitationArtifact, decodeAttachResponseEnvelope, decodeDroneRuntimeMetadataPatch, decodeEvictDroneResultEnvelope, decodeDeleteRoleResultEnvelope, decodeRoleRationaleResultEnvelope, decodeProtocolEnvelope, decodeProtocolErrorEnvelope, decodeProtocolTagPreflight, decodeReadLogResultEnvelope, decodeResolveRepositoryCubeResponseEnvelope, decodeReassignDroneResultEnvelope, decodeUpdateDroneRuntimeMetadataResponseEnvelope, decodeGetDocumentResultEnvelope, decodeListDocumentsResultEnvelope, decodePutDocumentResultEnvelope, decodeRemoveDocumentResultEnvelope, decodeSseFrames, PROTOCOL_LIMIT_CEILINGS, PROTOCOL_HTTP_CONTRACT, PROTOCOL_VERSION, ROLE_IN_USE_DELETE_MESSAGE, utf8ByteLength, } from '../protocol/index.js';
|
|
2
|
+
import { APPEND_LOG_IDEMPOTENCY_CONFORMANCE, CREATE_CUBE_ASSOCIATION_CONFORMANCE, CREATE_CUBE_RETRY_CONFORMANCE, INVITATION_ARTIFACT_CONFORMANCE, ENROLLMENT_RETRY_CONFORMANCE, DOCUMENT_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
6
|
{ id: 'enrollment.invitation-artifact', area: 'enrollment' },
|
|
7
7
|
{ id: 'enrollment.retry-authority', area: 'enrollment' },
|
|
8
8
|
{ id: 'repository.explicit-association', area: 'repository' },
|
|
9
|
+
{ id: 'documents.lifecycle', area: 'documents' },
|
|
9
10
|
{ id: 'security.adapter-boundary-injection', area: 'security' },
|
|
10
11
|
{ id: 'security.oversize-request', area: 'security' },
|
|
11
12
|
{ id: 'security.cross-cube-isolation', area: 'security' },
|
|
@@ -176,7 +177,7 @@ function expectSecretFreeError(response, status, code, operation, secrets) {
|
|
|
176
177
|
expectError(response, status, code, operation);
|
|
177
178
|
const diagnostic = JSON.stringify(response.body);
|
|
178
179
|
for (const secret of new Set(secrets)) {
|
|
179
|
-
invariant(!diagnostic.includes(secret), `${operation} exposed
|
|
180
|
+
invariant(!diagnostic.includes(secret), `${operation} exposed sensitive material.`);
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
export async function runAdapterConformance(environment, options = {}) {
|
|
@@ -599,6 +600,159 @@ export async function runAdapterConformance(environment, options = {}) {
|
|
|
599
600
|
cross_client_binding_hidden: true,
|
|
600
601
|
};
|
|
601
602
|
});
|
|
603
|
+
await record('documents.lifecycle', async () => {
|
|
604
|
+
const outcomes = new Map();
|
|
605
|
+
const documentPrincipal = await environment.admin.createPrincipal('document-lifecycle');
|
|
606
|
+
const documentCube = await environment.admin.createCube('document-lifecycle');
|
|
607
|
+
await environment.admin.grantCube(documentPrincipal, documentCube, 'write');
|
|
608
|
+
const documentCredential = await environment.admin.issueDroneSession(documentPrincipal);
|
|
609
|
+
const reader = await environment.admin.createPrincipal('document-reader');
|
|
610
|
+
const peer = await environment.admin.createPrincipal('document-peer');
|
|
611
|
+
const manager = await environment.admin.createPrincipal('document-manager');
|
|
612
|
+
await environment.admin.grantCube(reader, documentCube, 'read');
|
|
613
|
+
await environment.admin.grantCube(peer, documentCube, 'write');
|
|
614
|
+
await environment.admin.grantCube(manager, documentCube, 'manage');
|
|
615
|
+
const readerCredential = await environment.admin.issueDroneSession(reader);
|
|
616
|
+
const peerCredential = await environment.admin.issueDroneSession(peer);
|
|
617
|
+
const managerCredential = await environment.admin.issueDroneSession(manager);
|
|
618
|
+
const foreignPrincipal = await environment.admin.createPrincipal('document-foreign');
|
|
619
|
+
const foreignCube = await environment.admin.createCube('document-foreign');
|
|
620
|
+
await environment.admin.grantCube(foreignPrincipal, foreignCube, 'write');
|
|
621
|
+
const foreignCredential = await environment.admin.issueDroneSession(foreignPrincipal);
|
|
622
|
+
const foreignPut = await environment.operations.putDocument(foreignCredential, foreignCube, createProtocolEnvelope('document-foreign-put', {
|
|
623
|
+
title: 'Foreign evidence', content_type: 'text/plain', content: 'Foreign immutable content.',
|
|
624
|
+
}));
|
|
625
|
+
expectStatus(foreignPut, 201, 'Foreign document put');
|
|
626
|
+
const foreignDocument = decodePutDocumentResultEnvelope(foreignPut.body).payload.document;
|
|
627
|
+
expectError(await environment.operations.putDocument(readerCredential, documentCube, createProtocolEnvelope('document-read-only-put', { title: 'Denied', content_type: 'text/plain', content: 'denied' })), 403, ErrorCode.ACCESS_DENIED, 'Read-only document put');
|
|
628
|
+
expectError(await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-bad-type', { title: 'Binary', content_type: 'application/octet-stream', content: 'x' })), 400, ErrorCode.DOCUMENT_CONTENT_TYPE_UNSUPPORTED, 'Unsupported document type');
|
|
629
|
+
outcomes.set('unsupported-content-type', ErrorCode.DOCUMENT_CONTENT_TYPE_UNSUPPORTED);
|
|
630
|
+
expectError(await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-bad-title', { title: 'x'.repeat(121), content_type: 'text/plain', content: 'x' })), 400, ErrorCode.INVALID_INPUT, 'Oversized document title');
|
|
631
|
+
outcomes.set('oversize-title', ErrorCode.INVALID_INPUT);
|
|
632
|
+
expectError(await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-oversize', { title: 'Too large', content_type: 'text/plain', content: 'x'.repeat(65_537) })), 413, ErrorCode.DOCUMENT_BUDGET_EXCEEDED, 'Oversized document');
|
|
633
|
+
const budgetCube = await environment.admin.createCube('document-budget');
|
|
634
|
+
await environment.admin.grantCube(documentPrincipal, budgetCube, 'write');
|
|
635
|
+
let budgetPredecessor;
|
|
636
|
+
for (let index = 0; index < 8; index++) {
|
|
637
|
+
const response = await environment.operations.putDocument(documentCredential, budgetCube, createProtocolEnvelope(`document-budget-${index}`, {
|
|
638
|
+
title: `Budget ${index}`, content_type: 'text/plain', content: 'x'.repeat(65_536),
|
|
639
|
+
}));
|
|
640
|
+
expectStatus(response, 201, `Document budget fill ${index}`);
|
|
641
|
+
if (index === 0)
|
|
642
|
+
budgetPredecessor = decodePutDocumentResultEnvelope(response.body).payload.document;
|
|
643
|
+
}
|
|
644
|
+
invariant(budgetPredecessor !== undefined, 'Budget predecessor was not created.');
|
|
645
|
+
expectError(await environment.operations.putDocument(documentCredential, budgetCube, createProtocolEnvelope('document-budget-overflow', {
|
|
646
|
+
title: 'Overflow', content_type: 'text/plain', content: 'x', supersedes: budgetPredecessor.id,
|
|
647
|
+
})), 413, ErrorCode.DOCUMENT_BUDGET_EXCEEDED, 'Active document budget');
|
|
648
|
+
const budgetList = decodeListDocumentsResultEnvelope((await environment.operations.listDocuments(documentCredential, budgetCube, createProtocolEnvelope('document-budget-list', {}))).body).payload.documents;
|
|
649
|
+
invariant(budgetList.length === 8, 'Budget refusal partially mutated the document set.');
|
|
650
|
+
const budgetRetained = decodeGetDocumentResultEnvelope((await environment.operations.getDocument(documentCredential, budgetCube, createProtocolEnvelope('document-budget-get-predecessor', { id: budgetPredecessor.id }))).body).payload.document;
|
|
651
|
+
invariant(budgetRetained.state === 'active' && budgetRetained.superseded_by === null &&
|
|
652
|
+
budgetRetained.content === budgetPredecessor.content, 'Budget refusal partially mutated its predecessor.');
|
|
653
|
+
expectError(await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-unknown-supersedes', { title: 'Unknown', content_type: 'text/plain', content: 'x', supersedes: 'unknown_full_id' })), 409, ErrorCode.DOCUMENT_SUPERSESSION_INVALID, 'Unknown document supersession');
|
|
654
|
+
outcomes.set('foreign-supersedes', ErrorCode.DOCUMENT_SUPERSESSION_INVALID);
|
|
655
|
+
const put = await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-put', {
|
|
656
|
+
title: 'Review evidence',
|
|
657
|
+
content_type: 'text/markdown',
|
|
658
|
+
content: '# Evidence\n\nPortable UTF-8: €\n',
|
|
659
|
+
}));
|
|
660
|
+
expectStatus(put, 201, 'Document put');
|
|
661
|
+
const created = decodePutDocumentResultEnvelope(put.body).payload.document;
|
|
662
|
+
outcomes.set('markdown', 'created');
|
|
663
|
+
invariant(created.size_bytes === utf8ByteLength(created.content), 'Document size is not its UTF-8 byte size.');
|
|
664
|
+
expectSecretFreeError(await environment.operations.getDocument(documentCredential, documentCube, createProtocolEnvelope('document-foreign-get', { id: foreignDocument.id })), 404, ErrorCode.DOCUMENT_NOT_FOUND, 'Foreign document get', [foreignDocument.title, foreignDocument.content]);
|
|
665
|
+
expectSecretFreeError(await environment.operations.listDocuments(documentCredential, foreignCube, createProtocolEnvelope('document-foreign-list', {})), 404, ErrorCode.NOT_FOUND, 'Foreign document list', [foreignDocument.title, foreignDocument.content]);
|
|
666
|
+
expectSecretFreeError(await environment.operations.removeDocument(documentCredential, documentCube, createProtocolEnvelope('document-foreign-remove', { id: foreignDocument.id })), 404, ErrorCode.DOCUMENT_NOT_FOUND, 'Foreign document remove', [foreignDocument.title, foreignDocument.content]);
|
|
667
|
+
const entriesBeforeForeignCitation = decodeReadLogResultEnvelope((await environment.operations.read(documentCredential, documentCube, createProtocolEnvelope('document-read-before-foreign-cite', { cursor: null, limit: 100 }))).body).payload.entries.length;
|
|
668
|
+
expectSecretFreeError(await environment.operations.append(documentCredential, documentCube, createProtocolEnvelope('document-foreign-citation', {
|
|
669
|
+
post_id: '00000000-0000-4000-8000-00000000031f',
|
|
670
|
+
message: 'Foreign evidence must remain hidden.',
|
|
671
|
+
documents: [foreignDocument.id],
|
|
672
|
+
})), 404, ErrorCode.DOCUMENT_NOT_FOUND, 'Foreign document citation', [foreignDocument.title, foreignDocument.content]);
|
|
673
|
+
const entriesAfterForeignCitation = decodeReadLogResultEnvelope((await environment.operations.read(documentCredential, documentCube, createProtocolEnvelope('document-read-after-foreign-cite', { cursor: null, limit: 100 }))).body).payload.entries.length;
|
|
674
|
+
invariant(entriesAfterForeignCitation === entriesBeforeForeignCitation, 'Denied foreign citation mutated the log.');
|
|
675
|
+
const successorResponse = await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-put-successor', {
|
|
676
|
+
title: 'Review evidence revision 2',
|
|
677
|
+
content_type: 'text/markdown',
|
|
678
|
+
content: '# Revised evidence\n',
|
|
679
|
+
supersedes: created.id,
|
|
680
|
+
}));
|
|
681
|
+
expectStatus(successorResponse, 201, 'Document successor put');
|
|
682
|
+
const successor = decodePutDocumentResultEnvelope(successorResponse.body).payload.document;
|
|
683
|
+
invariant(successor.supersedes === created.id, 'Document successor did not bind the full prior id.');
|
|
684
|
+
expectError(await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-branch', { title: 'Branch', content_type: 'text/plain', content: 'x', supersedes: created.id })), 409, ErrorCode.DOCUMENT_SUPERSESSION_INVALID, 'Branched document supersession');
|
|
685
|
+
outcomes.set('branched-supersedes', ErrorCode.DOCUMENT_SUPERSESSION_INVALID);
|
|
686
|
+
expectError(await environment.operations.putDocument(credentialA, cubeA, createProtocolEnvelope('document-cross-cube', { title: 'Cross cube', content_type: 'text/plain', content: 'x', supersedes: created.id })), 409, ErrorCode.DOCUMENT_SUPERSESSION_INVALID, 'Cross-cube document supersession');
|
|
687
|
+
const citationResponse = await environment.operations.append(documentCredential, documentCube, createProtocolEnvelope('document-citation', {
|
|
688
|
+
post_id: '00000000-0000-4000-8000-000000000320',
|
|
689
|
+
message: 'See the document evidence.',
|
|
690
|
+
documents: [created.id, successor.id],
|
|
691
|
+
}));
|
|
692
|
+
expectStatus(citationResponse, 201, 'Document citation append');
|
|
693
|
+
const cited = decodeAppendLogResultEnvelope(citationResponse.body).payload.entry.documents;
|
|
694
|
+
invariant(cited?.length === 2 && cited[0].state === 'superseded', 'Log citations did not render document metadata and state.');
|
|
695
|
+
expectError(await environment.operations.append(documentCredential, documentCube, createProtocolEnvelope('document-citation-conflict', {
|
|
696
|
+
post_id: '00000000-0000-4000-8000-000000000320',
|
|
697
|
+
message: 'See the document evidence.',
|
|
698
|
+
documents: [successor.id],
|
|
699
|
+
})), 409, ErrorCode.POST_ID_CONFLICT, 'Document citation post-id conflict');
|
|
700
|
+
const listed = await environment.operations.listDocuments(readerCredential, documentCube, createProtocolEnvelope('document-list', {}));
|
|
701
|
+
expectStatus(listed, 200, 'Document list');
|
|
702
|
+
const list = decodeListDocumentsResultEnvelope(listed.body).payload.documents;
|
|
703
|
+
invariant(list.length === 2 && list.some(({ id, state, superseded_by }) => id === created.id && state === 'superseded' && superseded_by === successor.id), 'Linear document supersession was not listed by full ids.');
|
|
704
|
+
invariant(!('content' in list[0]), 'Document list exposed immutable content.');
|
|
705
|
+
invariant(!list.some(({ id }) => id === foreignDocument.id), 'Document list leaked a foreign document.');
|
|
706
|
+
expectStatus(await environment.operations.getDocument(readerCredential, documentCube, createProtocolEnvelope('document-reader-get', { id: created.id })), 200, 'Read-only document get');
|
|
707
|
+
expectError(await environment.operations.removeDocument(readerCredential, documentCube, createProtocolEnvelope('document-reader-remove', { id: created.id })), 403, ErrorCode.DOCUMENT_REMOVE_DENIED, 'Read-only document remove');
|
|
708
|
+
expectError(await environment.operations.removeDocument(peerCredential, documentCube, createProtocolEnvelope('document-peer-remove', { id: created.id })), 403, ErrorCode.DOCUMENT_REMOVE_DENIED, 'Peer document remove');
|
|
709
|
+
outcomes.set('peer-remove', ErrorCode.DOCUMENT_REMOVE_DENIED);
|
|
710
|
+
const authorDocumentResponse = await environment.operations.putDocument(documentCredential, documentCube, createProtocolEnvelope('document-author-put', {
|
|
711
|
+
title: 'Author removable', content_type: 'text/plain', content: 'Author content.',
|
|
712
|
+
}));
|
|
713
|
+
expectStatus(authorDocumentResponse, 201, 'Author document put');
|
|
714
|
+
outcomes.set('plain', 'created');
|
|
715
|
+
const authorDocument = decodePutDocumentResultEnvelope(authorDocumentResponse.body).payload.document;
|
|
716
|
+
expectStatus(await environment.operations.removeDocument(documentCredential, documentCube, createProtocolEnvelope('document-author-remove', { id: authorDocument.id })), 200, 'Author document remove');
|
|
717
|
+
const got = await environment.operations.getDocument(documentCredential, documentCube, createProtocolEnvelope('document-get', { id: created.id }));
|
|
718
|
+
expectStatus(got, 200, 'Document get');
|
|
719
|
+
invariant(decodeGetDocumentResultEnvelope(got.body).payload.document.content === created.content, 'Document get did not preserve immutable content and metadata.');
|
|
720
|
+
const removed = await environment.operations.removeDocument(managerCredential, documentCube, createProtocolEnvelope('document-remove', { id: created.id }));
|
|
721
|
+
expectStatus(removed, 200, 'Document remove');
|
|
722
|
+
invariant(decodeRemoveDocumentResultEnvelope(removed.body).payload.document.state === 'removed', 'Document removal did not produce audit-retained removed state.');
|
|
723
|
+
const afterResponse = await environment.operations.listDocuments(readerCredential, documentCube, createProtocolEnvelope('document-list-after', {}));
|
|
724
|
+
expectStatus(afterResponse, 200, 'Read-only document list after removal');
|
|
725
|
+
const after = decodeListDocumentsResultEnvelope(afterResponse.body).payload.documents;
|
|
726
|
+
invariant(after.length === 1 && after[0].id === successor.id, 'Removed document remained in active listing.');
|
|
727
|
+
const retainedResponse = await environment.operations.getDocument(readerCredential, documentCube, createProtocolEnvelope('document-get-removed', { id: created.id }));
|
|
728
|
+
expectStatus(retainedResponse, 200, 'Read-only audit-retained document get');
|
|
729
|
+
const retained = decodeGetDocumentResultEnvelope(retainedResponse.body).payload.document;
|
|
730
|
+
invariant(retained.state === 'removed' && retained.content === created.content, 'Removed content was not forensically resolvable.');
|
|
731
|
+
const foreignRetained = decodeGetDocumentResultEnvelope((await environment.operations.getDocument(foreignCredential, foreignCube, createProtocolEnvelope('document-foreign-retained', { id: foreignDocument.id }))).body).payload.document;
|
|
732
|
+
invariant(foreignRetained.state === 'active' && foreignRetained.content === foreignDocument.content, 'Denied foreign operations leaked mutation into the foreign document.');
|
|
733
|
+
outcomes.set('removed', 'audit-resolvable');
|
|
734
|
+
const entriesBeforeUnknownCitation = (await environment.operations.read(documentCredential, documentCube, createProtocolEnvelope('document-read-before-unknown-cite', { cursor: null, limit: 100 })));
|
|
735
|
+
const beforeCount = decodeReadLogResultEnvelope(entriesBeforeUnknownCitation.body).payload.entries.length;
|
|
736
|
+
expectError(await environment.operations.append(documentCredential, documentCube, createProtocolEnvelope('document-unknown-citation', {
|
|
737
|
+
post_id: '00000000-0000-4000-8000-000000000321', message: 'Unknown.', documents: ['unknown_full_id'],
|
|
738
|
+
})), 404, ErrorCode.DOCUMENT_NOT_FOUND, 'Unknown document citation');
|
|
739
|
+
const longAccepted = await environment.operations.append(documentCredential, documentCube, createProtocolEnvelope('document-advisory', {
|
|
740
|
+
post_id: '00000000-0000-4000-8000-000000000322', message: 'x'.repeat(1025),
|
|
741
|
+
}));
|
|
742
|
+
expectStatus(longAccepted, 201, 'Advisory log append');
|
|
743
|
+
invariant(decodeAppendLogResultEnvelope(longAccepted.body).payload.advisory?.threshold_bytes === 1024, 'Configured advisory was not returned.');
|
|
744
|
+
expectError(await environment.operations.append(documentCredential, documentCube, createProtocolEnvelope('document-hard-cap', {
|
|
745
|
+
post_id: '00000000-0000-4000-8000-000000000323', message: 'x'.repeat(4097),
|
|
746
|
+
})), 413, ErrorCode.CONTENT_TOO_LARGE, 'Hard-cap log append');
|
|
747
|
+
const citedRead = decodeReadLogResultEnvelope((await environment.operations.read(documentCredential, documentCube, createProtocolEnvelope('document-citation-read', { cursor: null, limit: 10 }))).body).payload.entries[0].documents;
|
|
748
|
+
invariant(citedRead?.[0].state === 'removed', 'Log citation did not resolve the audit-retained removed state.');
|
|
749
|
+
const finalCount = decodeReadLogResultEnvelope((await environment.operations.read(documentCredential, documentCube, createProtocolEnvelope('document-final-read', { cursor: null, limit: 100 }))).body).payload.entries.length;
|
|
750
|
+
invariant(finalCount === beforeCount + 1, 'Rejected document or log operations partially mutated state.');
|
|
751
|
+
for (const vector of DOCUMENT_CONFORMANCE) {
|
|
752
|
+
invariant(outcomes.get(vector.fixture) === vector.expected, `${vector.name} produced ${outcomes.get(vector.fixture) ?? 'no outcome'}; expected ${vector.expected}.`);
|
|
753
|
+
}
|
|
754
|
+
return { refusals: true, put: 201, cited: true, active_listed: true, removed_hidden: true, audit_resolvable: true };
|
|
755
|
+
});
|
|
602
756
|
await record('security.adapter-boundary-injection', async () => {
|
|
603
757
|
const injectedMessage = "'); DROP TABLE log_entries; --\r\ndata: forged-sse-frame";
|
|
604
758
|
const injectedBody = JSON.stringify(createProtocolEnvelope('inject-b1', {
|