Skip to content

Repository files navigation

tapes-crates

The client-side Rust crates for Tapes: what a coding-agent harness needs in order to run under capture, what capture puts on the wire, and how a client reads the results back.

Three published crates and one internal test-support crate, in one workspace. They are consumed by tapesctl and by closed-source clients alike, which is the point: parity between two clients' start commands is structural, not policed, because the same code runs in both. A behaviour that lives here cannot differ between clients; a behaviour that lives in a client can, and that is the test for whether something belongs in this repository at all.

Two documentation surfaces, and they answer different questions. What these crates are for, how to add a harness, and the contracts they seal are at tapes.dev/docs/tapes-crates. The API of each published crate is on docs.rs: tapes-harnesses, tapes-capture, tapes-client.

The public API boundary

All three published crates are supported public API. None of them is a private helper that happens to be published: tapes-capture is the capture protocol, tapes-harnesses is the harness knowledge, tapes-client is the read client. Each is meant to be depended on directly, on its own version, and each is versioned against the promise below.

Each crate owns one question. The boundaries below are the contract this repository publishes — a change that moves a responsibility across one of these lines is a breaking change even when every signature still compiles.

crate owns does not own
tapes-harnesses (crates/tapes-harnesses/) Harness launch and attribution knowledge. The registry, launch recipes, config patch grammars, plugin artifacts, per-harness attribution lanes, transcript discovery and packaging. Anything true of every harness — that is tapes-capture.
tapes-capture (crates/tapes-capture/) The capture protocol. The X-Tapes-* envelope producer and the harness-id vocabulary it stamps, the capture-gateway environment contract, the launch-nonce protocol, peer-PID lookup, and the peer-trust ancestry walk. Any harness's name, and any knowledge that arrives because a harness was added.
tapes-client (crates/tapes-client/) The read surface. The sealed core contract (vendored from a published release asset) and a deployment's discovered cassettes, driven over one transport seam — with the error taxonomy, decode policy, pagination convention, and path join written once beneath both. Authentication, tenancy, transport, and rendering. Each is a consumer's, and each consumer's answer differs.

The membership test for the first two is: would adding one more harness change this? If yes it is tapes-harnesses; if no it is tapes-capture. The dependency edge runs one way and Cargo enforces it rather than review — tapes-harnesses depends on tapes-capture, never the reverse. The moment a capture primitive knows a harness's name it has stopped being the thing every harness shares. Where capture needs something from a harness — the envelope needs a session's fields — it declares a trait and the harness crate implements it.

The test for the third is different, because tapes-client is not split by subject matter but by when the operation table is known: the core contract is sealed at build time, a deployment's cassettes are discovered at runtime. Both halves are thin method tables over one shared floor. That is the whole design — see crates/tapes-client/README.md.

tapes-client was formed by merging two earlier crates, tapes-read-contract and tapes-cassette-client. A consumer still pinning either name will find the item-by-item mapping — including the one variant that was renamed — in that same README, under Migrating from tapes-read-contract / tapes-cassette-client.

Nothing specific to one consumer belongs in any of the three: no auth headers, no endpoints, no branding in behaviour. Delivery, auth, and retry live in each consumer.

The fourth crate

The repository contains a fourth crate, tapes-mock-upstream. It is internal test support — a streaming mock provider upstream, a mock ingest server, and the scripted recipes behind the harness regression matrix — and it is never released: the release workflow accepts tags for the three published crates only, and the crate itself sets publish = false. It is a crate rather than a test module because the matrix launches real harness binaries through tapes-harnesses rather than inside it, and an integration test in a sibling crate can only reach items the tested crate exports — so a feature gate would have made a mock HTTP server part of tapes-harnesses' public surface, and feature unification would then link it into consumers' production builds. Consumers who want it for their own tests take it as a git dependency under [dev-dependencies]; it makes no stability promise. The reasoning is in crates/tapes-mock-upstream/README.md.

Publishing

All three crates are live on crates.io — each first published at 0.1.0 — so cargo add tapes-capture, cargo add tapes-harnesses, and cargo add tapes-client resolve against the real index, and every PR proves the crates have stayed publishable.

Each crate is released on its own version and its own <crate>-v<version> tag, cut from main by the Cut Release dispatch workflow. They version independently: a fix to the read client is not a reason to renumber the capture protocol.

Semantics, pre-1.0. While these crates are 0.x, versions carry the usual Cargo meaning — a breaking change bumps the minor (0.2.0), anything compatible bumps the patch (0.1.1). The boundary table above is what "breaking" is measured against, which is why it is written as a contract rather than as a description of where files currently sit.

Only one dependency edge exists — tapes-harnesses requires tapes-capture — so tapes-capture publishes first whenever both move, and tapes-client is free of the ordering entirely. docs/releasing.md has the order, the tag scheme, the gates that keep publishability from rotting between releases, and the two-lock hold on the upload step: the publish runs only while the repository variable PUBLISH_ENABLED is true, and it is.

Adding a harness

Teaching this repository about a new coding agent starts with one const in crates/tapes-harnesses/src/harness.rs. docs/adding-a-harness.md walks the whole path: the registry declaration, when a launch recipe is needed, which attribution strategy applies, and what the tapes deriver needs on its side.

The envelope contract

The X-Tapes-* envelope is a cross-language contract: tapes-capture produces it in Rust, and the Go parsers in tapes' ingest and gateway capture read it back. The shared fixture corpus is vendored under crates/tapes-capture/vendor/tapes-envelope-fixtures/ (authored in the tapes repository at fixtures/envelope/) and the producer-side oracle in crates/tapes-capture/src/envelope_fixtures.rs runs against it — the same corpus the Go parsers test against. scripts/sync-envelope-fixtures.sh refreshes the copy and detects drift.

The corpus ships a DIGEST sealing its case set, and make corpus-seal recomputes it over the vendored files. Conformance to a copy only proves parity if every copy is the same corpus — without the seal, a hand-edit here would leave the Rust producer and the Go parsers testing against different bytes with both suites green. The seal needs no network and no tapes checkout, so unlike the read contract below it has nothing to configure: it either matches or CI is red.

The read contract

tapes-client vendors the published tapes read contract at crates/tapes-client/contracts/tapes-api.yaml, pinned by fingerprint in the PROVENANCE.md beside it. make contracts-check verifies the vendored bytes against both that fingerprint and the published release asset, and CI runs it with TAPES_CONTRACT_STRICT=1 so a gate that cannot reach its input fails rather than reporting a comparison it never made.

Developing

The repository is one workspace whose members are crates/*, so a bare cargo invocation at the root covers every crate — including crates added after a command was written. The Nix flake dev shell pins the toolchain via rust-toolchain.toml:

nix develop
make check   # build + fmt-check + clippy + test

Every crate denies unwrap, expect, and panic through the workspace lint table; return Result and surface errors through the crate error types instead.

License

Dual-licensed under either of

at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Shared, open-source client-side harness knowledge for Tapes capture — launch recipes, session attribution, transcript discovery, and the X-Tapes-* envelope. Dual MIT/Apache-2.0.

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages