borgmcp-shared
v2.2.0
Published
Shared protocol, domain, templates, and conformance contracts for Borg MCP implementations.
Maintainers
Readme
borgmcp-shared
borgmcp-shared is the implementation-neutral contract package for Borg MCP.
It contains protocol types, domain helpers, built-in cube templates, and portable
conformance vectors that Borg MCP client and server implementations can run.
Its pure ESM helpers and generated TypeScript declarations run consistently
across host environments with zero runtime dependencies.
Installation
npm install borgmcp-sharedThe package is ESM-only and supports Node.js 22.12.0 or newer.
Public API
The root entry point exports the complete supported API:
import {
PROTOCOL_VERSION,
TEMPLATES,
compareBroadcastHwm,
parseRoleSections,
} from 'borgmcp-shared';Focused subpath exports let consumers keep their dependency boundary explicit:
import { TEMPLATES } from 'borgmcp-shared/templates';
import { patchRoleSectionText } from 'borgmcp-shared/role-section';
import { compareBroadcastHwm } from 'borgmcp-shared/log-stream-hwm';
import { formatDroneAddressToken } from 'borgmcp-shared/drone-address';
import { canonicalizeRepositoryIdentity } from 'borgmcp-shared/runtime-metadata';
import type { AppendLogRequest } from 'borgmcp-shared/protocol';
import { ADAPTER_CONFORMANCE_FIXTURES } from 'borgmcp-shared/conformance';The supported subpaths are:
borgmcp-shared/protocol: wire entities, requests, responses, errors, and the exact protocol version tag.borgmcp-shared/domain: pure role-section, address, and high-water-mark helpers plus shared domain types.borgmcp-shared/conformance: test-runner-independent behavior vectors and the adapter conformance runner.borgmcp-shared/templates: built-in cube templates, template schemas, and helpers.borgmcp-shared/role-section: lossless role-text parsing and patching.borgmcp-shared/log-stream-hwm: broadcast cursor ordering.borgmcp-shared/drone-address: stable short drone-address rendering.borgmcp-shared/runtime-metadata: pure validation and canonical repository identity helpers for advisory local seat metadata.borgmcp-shared/package.json: package metadata.
Generated declaration files are included in the published package.
Handshake
Key paths in the repository-first HTTP contract include:
GET /healthzis the only unauthenticated liveness probe. Success is204with no body or identifying metadata.POST /api/enrollment/exchangeaccepts a single-use invitation, canonical retry key, and client-generated 256-bit bearer in a bounded JSON body over verified TLS. It returns only stable non-secret client identity and server capabilities; owner enrollment grantscreate_cubebut creates no cube. An exact credential-proven retry is non-mutating, and a mismatched replay fails uniformly. Secrets never belong in a response, URL, query string, command-line argument, diagnostic, or serializable domain entity.GET /api/protocolis credential-free and mutation-free. It returns ONLY the exact protocol tag — no package version, limits, server identity, or other fingerprint surface — so a client verifies pinned TLS and the exact tag before it creates or sends any credential.POST /api/cubesrequires an active parent client withcreate_cube. Its strict, idempotent request selects a server-owned template; one atomic success creates a cube, two initial roles, and the creator's cube-scopedmanagegrant. Exact retries return the same non-secret identities without mutation.POST /api/repository-cubes/resolveis an authenticated, read-only lookup by canonical repository identity. It returns explicit none or the stored authoritative cube, template, role IDs, and repository display without inferring identity from a cube name.PUT /api/repository-cubes/associationatomically binds an explicitly confirmed cube ID and canonical repository identity for a caller with cubemanageauthority. The same binding is idempotent, multiple repositories may bind to one cube, and a repository bound to a different cube returns a non-enumerating conflict. A conflict, validation failure, or permission failure performs no mutation.DELETE /api/cubes/:cubeIdrequires cubemanageauthority and atomically deletes the cube and its scoped state. Formerly authorized callers receive410 CUBE_DELETED, while callers that were never authorized still receive404 NOT_FOUND.PUT /api/cubes/:cubeId/documentsstores immutable UTF-8text/markdownortext/plaincontent under one opaque full id. Titles are required and bounded to 120 characters. Optionalsupersedeslinks form a same-cube linear chain.GETby full id remains audit-resolvable after author-or-manager removal, while active listings delist removed documents. Log posts cite documents with the structureddocumentsid array; reads render id, title, UTF-8 size, and superseded or removed state.
decodeProtocolTagPreflight fails closed on any tag other than the exact
expected version, on any extra field, or on a non-object body — before any
credential is created or sent. The exact protocol tag is the sole acceptance
authority: there is no capability negotiation, and client and server ship and
update together as one clean-slate product. The attach request envelope still
decodes its version before any payload as defense in depth.
Versioned JSON coordination requests and successful responses are carried inside
ProtocolEnvelope<T>. Failures use ProtocolErrorEnvelope. The credential-free
protocol preflight uses its exact tag-only shape; 204 liveness and
acknowledgement responses are bodyless. Payload codecs are exported separately
so adapters can validate the envelope first and then validate the
operation-specific payload without accepting ambiguous fields. Append-log
requests require a canonical UUID post_id and an explicit to audience of
scalar "broadcast" or a non-empty recipient-selector array. Identity is scoped
by authenticated author: an exact retry must preserve the message and fully
resolved visibility and recipient set, then returns the same entry with
deduplicated: true. Reusing an author's post_id with a changed tuple returns
POST_ID_CONFLICT; another author may independently use the same UUID. Attach responses include the nullable
initial_log_cursor that anchors subsequent log replay.
At the configurable defaults, log messages up to 1 KiB are accepted silently,
messages from 1 KiB through 4 KiB return a store-as-document advisory, and
larger messages are rejected.
Cube managers reassign a seat with PATCH /api/cubes/:cubeId/drones/:droneId
and evict one with DELETE on the same path. Both operations use strict
versioned request and success envelopes. An evicted seat's former bearer receives
the terminal 410 DRONE_EVICTED signal; revoked sessions return
401 SESSION_REVOKED.
An authenticated drone session updates only its own advisory identity with
PATCH /api/cubes/:cubeId/drones/self/metadata. The strict patch carries no
target seat ID. Metadata never grants authority or changes role, wake, liveness,
timestamp, log, routing, or model-execution state.
Identity responses include runtime_metadata_reported, keeping an omitted
attach report distinct from a reported all-null or explicitly cleared state.
See docs/enrollment.md for purpose-bound owner enrollment,
ordinary ungranted enrollment, cube creation, pending enrollment, and retry contracts.
See docs/cube-documents.md for document budgets,
supersession, removal, citation, and log-length contracts.
Conformance
Server and client implementations should run the vectors exported from
borgmcp-shared/conformance against their adapters. The vectors are readonly
data, and the runner is test-runner-independent code, so they work with any test
runner supported by the implementation. Cases cover HTTP and canonical errors,
credential misuse, isolation and revocation, SSE framing/replay/cursor ordering,
executable enrollment authority/retry/mismatch/redaction and cube-create
idempotency, explicit repository resolution/adoption and conflict outcomes,
acks, claims, decisions, cube-scoped drone reassignment, role-class
and single-seat invariants, eviction exclusion, and terminal bearer signaling.
The same runner covers complete attach reports, own-seat metadata self-healing,
canonical repository identity, invalid-patch atomicity, cross-cube isolation,
secret non-echo, and protocol-visible metadata non-interference.
The document lifecycle fixture covers immutable put/get, metadata-only listing,
removal delisting, and retained exact-id forensic resolution.
Manage-scoped cube, role, taxonomy, decision, and drone operations also share an
authority matrix: managing parents may mutate; known same-cube read/write
parents receive 403 ACCESS_DENIED; drone sessions remain non-managing; and
no-grant, foreign, or unknown cubes remain hidden behind 404 NOT_FOUND.
Denied role, decision, reassignment, and eviction operations have portable
protocol readback controls. Cube-directive and taxonomy writes currently have
no shared read route, so mutation-negative coverage for those two operations
remains implementation-owned.
The credential-free protocol preflight likewise has no portable before/after
state-enumeration route; shared conformance pins its exact response while
implementations retain mutation-negative coverage for their local state.
Decision write requests cap each decision and optional rationale field at
512 UTF-8 bytes independently. Response decoders continue to read longer
historical values so existing registry entries remain compatible.
Implement ConformanceEnvironment with raw responses from the target adapter,
then call runAdapterConformance. The runner creates and decodes envelopes,
drives state transitions, and decides pass/fail; adapters do not submit expected
results. Adapters provide fixture creation plus narrowly scoped deterministic
cursor, replay-transition, and entry-ID controls. Shared fixtures assert only
protocol responses, streams, and subsequent protocol queries; persistence
layout, internal counters, and authority restart are implementation-owned test
concerns. A failed setup prerequisite is reported as skipped with its cause;
assertion failures do not change prerequisite availability, and unrelated
fixtures continue. A server or client adapter can run the portable suite against
its local/self-hosted implementation. The package does not define a second
authority, migration target, or fallback implementation.
The 1.1.0 suite was measured at 156 requests per credential within one reset
epoch. Adapters running the suite must provision a budget of at least 192
requests per credential per epoch so conformance does not consume the
implementation's limit.
ConformanceAdmin classifies its complete boundary as follows. Fixture setup is
reset, createPrincipal, createCube, grantCube, revokeCubeGrant,
createRole, createDrone, issueManagedDroneSession,
revokeManagedDroneSession, grantCreateCubeCapability, issueDroneSession,
issueSingleUseInvitation, prepareRepositoryCube, and revokePrincipal.
seedEntryQueryIds and expireCursor are deterministic synthetic controls for
states that random protocol IDs and wall-clock retention cannot reliably create.
armReplayTransition is the sole synchronization control. The shared boundary
contains no persistence inspection, internal counters, or process restart.
The package's own suite covers built-in templates, role-section patching, broadcast high-water-mark ordering, drone-address formatting, runtime metadata canonicalization, and current public response shapes.
Compatibility
PROTOCOL_VERSION is the sole acceptance authority: every envelope carries the
protocol tag, and each decoder fails closed on any value other than the exact
expected tag. There is no capability negotiation, supported-version list,
compatibility matrix, or version-range fallback. The client and server are one
clean-slate product — a wire change increments the tag and both adopt it
together in a coordinated release, with no mixed-version window.
See docs/compatibility.md for the exact-tag policy and the coordinated rollout order for introducing protocol changes.
See RELEASES.md for release history.
Security Posture
The package publishes portable wire contracts, deterministic helpers, canonical templates, and conformance data as dependency-free ESM.
Protocol types describe untrusted wire data; they do not replace runtime input validation at a server boundary. Implementations remain responsible for authenticating callers, authorizing every operation, enforcing resource limits, and validating request bodies before using them.
The exported codecs reject unknown security fields, unsafe identifiers, oversized payloads, and terminal control characters. Network adapters must also enforce the published SSE and request byte limits while reading from the socket; calling a decoder only after an unbounded response has been buffered is unsafe.
Please report vulnerabilities through the private process in SECURITY.md, not through a public issue.
See CONTRIBUTING.md before proposing a change.
