borgmcp-shared 0.2.2 → 0.4.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.
@@ -1,38 +1,57 @@
1
1
  # Protocol Compatibility
2
2
 
3
- `borgmcp-shared` versions the contracts shared by Borg MCP clients and server
4
- implementations. Package versions and protocol generations are related but are
5
- not interchangeable: a package release can add helpers without changing the
6
- wire protocol.
7
-
8
- | Package range | Protocol generation | Status |
9
- | --- | --- | --- |
10
- | `>=0.2.0 <0.3.0` | `1` | Versioned envelope, runtime codecs, and adapter conformance |
3
+ `borgmcp-shared` carries the wire contract shared by the Borg MCP client and
4
+ server. The client and server are a single clean-slate product: they ship and
5
+ update together, so the contract has no negotiation surface and no
6
+ backwards-compatibility machinery.
7
+
8
+ ## Exact Protocol Tag
9
+
10
+ `PROTOCOL_VERSION` is the sole acceptance authority. The credential-free,
11
+ mutation-free `GET /api/protocol` preflight returns only the exact tag, and
12
+ `decodeProtocolTagPreflight` fails closed on any other tag, an extra field, or a
13
+ non-object body with a clear static mismatch diagnostic — so a client verifies
14
+ pinned TLS and the tag before it creates or sends any credential. Every
15
+ subsequent envelope also carries the tag and decodes it before any payload as
16
+ defense in depth. There is no capability negotiation, no supported-version list,
17
+ no compatibility matrix, and no package-range or version-range fallback. A peer
18
+ that presents a different tag is rejected — it is never adapted to.
11
19
 
12
20
  ## Change Policy
13
21
 
14
- Consumers must pin `^0.2.0`, which npm interprets as `>=0.2.0 <0.3.0`. During
15
- the pre-1.0 series, a breaking envelope or wire-contract change requires a
16
- minor release (`0.3.0`); additive compatible contracts and corrections use a
17
- patch release (`0.2.x`). Consumers widen the minor range only after deliberate
18
- compatibility review. A broad `<1.0.0` range is unsafe because pre-1.0 minor
19
- versions may break the wire contract.
22
+ A wire-shape change is simply made: the protocol tag increments and both the
23
+ client and server adopt the new tag together in a coordinated release. There is
24
+ no mixed-version window in which an old client and a new server (or the reverse)
25
+ interoperate; incompatible peers fail closed rather than degrade.
20
26
 
21
- A wire-shape change must include all of the following:
27
+ A wire-shape change must include:
22
28
 
23
29
  1. A new or updated implementation-neutral conformance vector.
24
30
  2. A documented client and server rollout order.
25
- 3. A compatibility entry that identifies the affected package and protocol
26
- versions.
27
- 4. A migration window when older deployed clients or servers can still be in
28
- use.
31
+ 3. A package version bump so the new protocol tag never ships under a version
32
+ whose already-published artifact carries a different tag.
29
33
 
30
34
  Removing or reinterpreting an existing field is a protocol-breaking change even
31
35
  when TypeScript permits it. Implementations must not infer compatibility from a
32
- successful build alone; they should check the declared protocol generation and
33
- run the shared conformance suite.
36
+ successful build alone they must run the shared conformance suite against the
37
+ exact tag they implement.
38
+
39
+ ## Coordinated Rollout
40
+
41
+ The rollout order is fail-closed:
42
+
43
+ 1. Publish the reviewed `borgmcp-shared` registry artifact only after its
44
+ separate tag and publication gates pass, under a version that has never been
45
+ published with a different protocol tag.
46
+ 2. Update the client and server releases to the reviewed registry range; Git
47
+ dependencies are not an authorized release input.
48
+ 3. Run the shared adapter conformance suite in both consumers before release.
49
+ 4. Deploy client and server support together. A peer on the prior protocol tag
50
+ and a peer on the new tag are incompatible; neither side falls back.
51
+
52
+ ## Model/Provider Selection
34
53
 
35
- Model/provider selection is intentionally absent from the `0.2.x` coordination
36
- contract. Agent CLIs own model configuration; Borg servers may expose the
37
- separate advisory `reported_model` field for session observability, but clients
38
- must not use it for routing, launch configuration, or authorization.
54
+ Model/provider selection is intentionally absent from the coordination contract.
55
+ Agent CLIs own model configuration; Borg servers may expose the separate
56
+ advisory `reported_model` field for session observability, but clients must not
57
+ use it for routing, launch configuration, or authorization.
@@ -0,0 +1,167 @@
1
+ # Retry-Safe Enrollment and Cube Creation Contract
2
+
3
+ This document defines the data-only enrollment and cube-creation boundaries
4
+ shared by clients and servers. It does not define offline operator commands,
5
+ database transactions, or keychain APIs, but implementations must preserve the
6
+ security properties below.
7
+
8
+ This contract replaces the server-generated bearer response used by the
9
+ published `borgmcp-shared@0.3.0` v1 baseline. It is a breaking clean-slate v2
10
+ wire change; this source now identifies the still-unpublished `0.4.0` candidate
11
+ and requires a coordinated client/server release. The version bump grants no tag
12
+ or publication authority — creating the `v0.4.0` tag and the registry
13
+ publication remain separate, independently gated steps. There is no
14
+ compatibility path that returns a bearer from the server.
15
+
16
+ A client verifies the credential-free `GET /api/protocol` tag preflight before
17
+ it creates or sends any enrollment secret; a peer that presents a different
18
+ protocol tag is rejected outright. The exact protocol tag is the sole acceptance
19
+ authority, so there is no capability a server advertises or a client negotiates
20
+ for this contract. The enrollment
21
+ response still carries the enrolled principal's `server_capabilities` (owner
22
+ enrollment grants `['create_cube']`; ordinary enrollment grants none) — that is
23
+ an authorization fact about the principal, not a negotiated protocol capability.
24
+
25
+ ## Client Preconditions
26
+
27
+ Before network I/O, the client generates:
28
+
29
+ - a cryptographically random 256-bit client bearer, encoded as canonical
30
+ unpadded base64url; and
31
+ - a canonical UUID retry key.
32
+
33
+ The client persists both values as a pending enrollment in the operating-system
34
+ keychain before sending the request. It retains and reuses the exact pending
35
+ tuple after an ambiguous timeout or connection loss. The wire contract never
36
+ permits a file fallback, URL/argv/environment transport, or diagnostic output
37
+ for these secrets.
38
+
39
+ ## Exchange Request
40
+
41
+ `POST /api/enrollment/exchange` receives one strict payload inside the protocol
42
+ envelope:
43
+
44
+ ```json
45
+ {
46
+ "invitation": "<opaque invitation>",
47
+ "retry_key": "00000000-0000-4000-8000-000000000101",
48
+ "client_credential": "<43-character canonical base64url>",
49
+ "client_name": "optional presentation label"
50
+ }
51
+ ```
52
+
53
+ `client_name` is bounded presentation data. All other fields are required.
54
+ Unknown fields, weak credentials, noncanonical UUIDs, controls, and ambiguous
55
+ aliases fail closed. Invitation purpose is server-owned persisted state; the
56
+ request has no caller-controlled owner flag, plan, role, grant, or cube.
57
+
58
+ ## Secret-Free Responses
59
+
60
+ Ordinary enrollment returns only:
61
+
62
+ ```json
63
+ {
64
+ "purpose": "client",
65
+ "client_id": "<canonical UUID>",
66
+ "server_capabilities": []
67
+ }
68
+ ```
69
+
70
+ An ordinary enrollment creates no cube grant. It cannot infer access from
71
+ enrollment order, owner metadata, or a role label.
72
+
73
+ A successful purpose-bound owner enrollment returns only the narrow server
74
+ capability persisted for that client:
75
+
76
+ ```json
77
+ {
78
+ "purpose": "owner",
79
+ "client_id": "<canonical UUID>",
80
+ "server_capabilities": ["create_cube"]
81
+ }
82
+ ```
83
+
84
+ The response never contains, echoes, rotates, or recovers the client bearer,
85
+ invitation, retry key, recovery credential, or a session credential. Owner
86
+ enrollment creates zero cubes, roles, and cube grants. `create_cube` authorizes
87
+ only authenticated cube creation; it does not reveal or grant access to any
88
+ existing cube and is never inferred from enrollment order, owner metadata, or a
89
+ role label.
90
+
91
+ ## Retry Semantics
92
+
93
+ The server binds the consumed invitation to the canonical retry tuple and stores
94
+ only a keyed client credential digest. A retry is successful only when the retry key,
95
+ submitted credential proof, optional client name, invitation purpose, and
96
+ server-owned owner-claim epoch exactly match the original enrollment. It
97
+ returns the same non-secret response and performs no mutation.
98
+
99
+ A reused invitation with a different retry key, credential, client name,
100
+ purpose, epoch, or plan receives the same non-enumerating `AUTH_INVALID` class as
101
+ an absent, expired, revoked, malformed, or already-consumed invitation. It never
102
+ rotates credentials or creates another client, capability, cube, role, or grant.
103
+
104
+ ## Server Transaction Boundary
105
+
106
+ For an owner invitation, the server capacity-preflights and atomically binds the
107
+ invitation/retry tuple, inserts the keyed client credential digest, persists the
108
+ `create_cube` capability, and marks the invitation/owner epoch claimed. It
109
+ creates no cube, role, or cube grant. Any failure rolls back every mutation,
110
+ including invitation consumption.
111
+
112
+ Ordinary invitation exchange atomically creates only the client and credential
113
+ digest. Explicit grants are a separate offline administrative operation. Network
114
+ enrollment and attach routes never create or widen grants.
115
+
116
+ ## Cube Creation
117
+
118
+ `POST /api/cubes` requires an active parent-client credential with the persisted
119
+ `create_cube` server capability. Ordinary clients, revoked clients, and drone
120
+ sessions cannot use it. Its strict payload is:
121
+
122
+ ```json
123
+ {
124
+ "retry_key": "00000000-0000-4000-8000-000000000201",
125
+ "name": "repository presentation name",
126
+ "template": "default"
127
+ }
128
+ ```
129
+
130
+ The client persists the pending retry tuple before network I/O and reuses it
131
+ after ambiguous transport failure. `name` is bounded presentation data and
132
+ `template` selects allowlisted, server-owned inert data. The request cannot
133
+ supply cube or role IDs, an owner, access, grant target, capability, arbitrary
134
+ template data, paths, URLs, modules, commands, or repository credentials.
135
+
136
+ The server binds `(authenticated client, retry_key)` to the exact canonical name
137
+ and template. A successful transaction atomically creates one cube, exactly one
138
+ human-seat role, exactly one default worker role, and exactly one creator
139
+ `manage` grant. It returns:
140
+
141
+ ```json
142
+ {
143
+ "cube_id": "<canonical UUID>",
144
+ "human_seat_role_id": "<canonical UUID>",
145
+ "default_worker_role_id": "<canonical UUID>",
146
+ "access": "manage"
147
+ }
148
+ ```
149
+
150
+ An exact retry returns that stable response without mutation. Reusing the same
151
+ retry key with a different name or template returns non-enumerating HTTP `409`
152
+ `INVALID_INPUT` and creates nothing. A fresh retry key may create another cube,
153
+ subject to implementation quotas. `owner_id` and role labels remain metadata;
154
+ cube access derives only from the explicit cube-scoped grant.
155
+
156
+ ## Conformance
157
+
158
+ `ENROLLMENT_RETRY_CONFORMANCE` covers exact retry stability plus retry-key,
159
+ credential, and client-name mismatches. `ENROLLMENT_REDACTION_CONFORMANCE` pins
160
+ diagnostic redaction for invitation, bearer, and contextual retry-key values
161
+ while preserving unrelated public UUIDs. `ENROLLMENT_AUTHORITY_CONFORMANCE`
162
+ distinguishes ordinary zero-authority enrollment from owner `create_cube`
163
+ authority with zero initial cube state. The executable adapter runner drives
164
+ every retry vector, observes client/capability/cube/role/grant counts, verifies
165
+ secret-free errors, and proves authorized idempotent cube creation. Hostile
166
+ reference adapters demonstrate that each retry, authority, and idempotency
167
+ violation fails conformance.
package/docs/releasing.md CHANGED
@@ -122,6 +122,38 @@ Under the ratified `borgmcp-shared-recovery-version` decision, `0.2.2` is the
122
122
  selected recovery version. The source bump does not itself authorize a tag,
123
123
  environment approval, or publication.
124
124
 
125
+ Run `29360398007` published `borgmcp-shared@0.2.2` from the exact approved
126
+ artifact, and the registry integrity and maintainer matched that artifact. The
127
+ workflow then failed during immediate postpublish verification when the package
128
+ ownership read returned HTTP 404 before registry propagation completed. The run
129
+ and `v0.2.2` tag are immutable and MUST NOT be rerun, moved, or reused. Consumer
130
+ migration was blocked until provenance and signatures were independently verified
131
+ and Security approved the recovery evidence.
132
+
133
+ Recover out of band without rerunning the workflow:
134
+
135
+ 1. Download artifact `8321897865` from run `29360398007` and recheck its recorded
136
+ ZIP digest, `RUN_EVIDENCE`, tarball SHA-512, and artifact verifier report.
137
+ 2. With `GITHUB_SHA=508f2dc88658d8e00ff036b7ce6913fcfbef239b` and
138
+ `NPM_EXPECTED_OWNER=byteventures`, run
139
+ `node scripts/verify-registry-release.mjs postpublish <audited-tarball>`. The
140
+ verifier retries only transient HTTP 404 propagation responses. Its production
141
+ envelope performs at most 18 reads over approximately three and a half minutes
142
+ (1, 2, 4, and 8 second waits, then a 15 second cap). Non-404 failures and
143
+ integrity, owner, or provenance mismatches remain immediate terminal failures.
144
+ 3. Install exact `borgmcp-shared@0.2.2` from the registry into a clean temporary
145
+ prefix with scripts disabled and run `npm audit signatures` against that
146
+ prefix.
147
+ 4. Record the independent integrity, ownership, signed-provenance, and signature
148
+ evidence for a fresh Security verdict before changing any consumer dependency.
149
+
150
+ That recovery completed successfully: the registry tarball is byte-identical to
151
+ the audited artifact, its sole maintainer is `byteventures`, the signed in-toto
152
+ SLSA provenance binds the protected workflow/tag/commit/run, and npm verified one
153
+ registry signature plus one publish attestation. Security approved the recovery
154
+ evidence. Complete the bootstrap credential and Trusted Publisher cleanup below
155
+ before migrating consumers.
156
+
125
157
  The eventual first publication will use one temporary credential while still
126
158
  generating provenance from GitHub Actions.
127
159
 
@@ -185,6 +217,45 @@ Immediately after a successful first publish:
185
217
 
186
218
  ## Later Releases
187
219
 
220
+ ### 0.4.0 Clean-Slate v2 Contract
221
+
222
+ The ratified `borgmcp-shared-enrollment-version` decision defined this breaking
223
+ retry-safe enrollment and idempotent multi-cube contract. `borgmcp-shared@0.3.0`
224
+ is now the published, immutable v1 baseline on the registry, so the clean-slate
225
+ v2 contract — credential-free tag preflight included — is versioned as the
226
+ still-unpublished `0.4.0` candidate (a breaking change on a `0.x` package bumps
227
+ the minor). This source now identifies `0.4.0`. That reviewed version bump
228
+ does not authorize creating `v0.4.0` or publishing the package, and the source
229
+ branch must not republish `0.3.0`.
230
+
231
+ Before a separately authorized `v0.4.0` tag is created:
232
+
233
+ 1. The exact `0.4.0` source commit must be merged to protected `main` after Code
234
+ Review, Security, and Release Quality approve the package identity, public
235
+ API, conformance behavior, generated output, and release documentation.
236
+ 2. The package and root lockfile, `SHARED_PACKAGE_VERSION`, the exact protocol
237
+ version tag, packed-artifact verifier, and version assertions all identify
238
+ `0.4.0` (done in this source bump).
239
+ 3. The protected workflow must build one exact tarball and pass its full source,
240
+ test, audit, public-export, source-map, install/import, dry-run, lock-derived
241
+ CycloneDX SBOM, integrity, and provenance gates. Security must approve that
242
+ exact workflow artifact.
243
+ 4. The `0.4.0` workflow must generate a CycloneDX SBOM, canonicalize npm's
244
+ checkout-derived root display name to the already-verified manifest name,
245
+ and validate every component's exact canonical registry tarball URL, single
246
+ matching CycloneDX distribution reference, purl, and lock SHA-512 plus the
247
+ exact installed dependency graph and zero package runtime dependencies. The
248
+ tarball, SBOM, and SBOM validation report must be
249
+ checksummed and uploaded together for Security audit.
250
+ 5. The tag and publication each require their own explicit authorization. A
251
+ source-version approval, merge, or successful verification run grants neither.
252
+
253
+ After registry publication and independent integrity/provenance verification,
254
+ the first borgmcp client and borgmcp-server releases may replace their temporary
255
+ development dependency with the reviewed registry range `^0.4.0`. They must not
256
+ ship a Git dependency or fall back to the incompatible published `0.3.0` v1
257
+ enrollment response.
258
+
188
259
  Later releases follow the same source gates, annotated protected tag, exact
189
260
  tarball audit, Queen authorization, and environment approval. The registry
190
261
  preflight requires the package to be owned by `NPM_EXPECTED_OWNER` and rejects
@@ -210,11 +281,11 @@ Stop without approving the environment when any of these conditions occurs:
210
281
  - final CR, Security, Release Quality, exact-tarball Security approval, or Queen
211
282
  authorization is missing.
212
283
 
213
- npm versions are immutable. Never retry by overwriting `0.2.0`, moving the tag,
214
- force-pushing a branch, or publishing from a local rebuild. A failed verification
215
- before publication requires fixing the source and beginning again with a new
216
- reviewed version/tag plan. If npm accepted the version but integrity or
217
- provenance verification failed, block every consumer migration, preserve the
218
- workflow logs and artifact, and escalate as a release incident. Do not unpublish,
284
+ npm versions are immutable. Never retry by overwriting any published version,
285
+ moving a release tag, force-pushing a branch, or publishing from a local rebuild.
286
+ A failed verification before publication requires fixing the source and
287
+ beginning again with a new reviewed version/tag plan. If npm accepted the version
288
+ but integrity or provenance verification failed, block every consumer migration,
289
+ preserve the workflow logs and artifact, and escalate as a release incident. Do not unpublish,
219
290
  deprecate, or publish a replacement version without a separately reviewed and
220
291
  authorized recovery decision.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp-shared",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Shared protocol, domain, templates, and conformance contracts for Borg MCP implementations.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -56,6 +56,7 @@
56
56
  "check": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
57
57
  "test": "vitest run",
58
58
  "verify:artifact": "node scripts/verify-packed-artifact.mjs",
59
+ "verify:sbom": "node scripts/verify-release-sbom.mjs",
59
60
  "prepack": "npm run check && npm test && npm run build"
60
61
  },
61
62
  "keywords": [