What this page covers
A cartridge is a small program that declares typed capabilities and speaks Bifaci, CapDAG’s binary frame protocol between host and cartridge. Generate a current starter in Python, Rust, Go, or Swift with capdag new. The Transmute menu can then discover the cartridge’s destinations, the planner can route through it, and other cartridges can call it.
What a cartridge is, how it speaks the wire protocol, how it registers handlers, and how peer invocation works between cartridges are all part of the cross-platform CapDAG cartridge runtime. It is specified once and is the same on every host:
- CapDAG cartridge runtime: entry point, handler registration, CLI/cartridge mode
- CapDAG cartridge anatomy: manifest shape, cap definitions, lifecycle
- CapDAG handler patterns:
Optrait, argument extraction, output emission - CapDAG model cartridges: three-phase architecture for ML cartridges
- CapDAG content cartridges: document-processing patterns
- CapDAG input/output: InputStream, OutputStream, stream lookup
- CapDAG peer invocation: calling other cartridges from a cartridge
- CapDAG progress and logging: LOG frames, progress mapping, keepalive
For the wire format itself (frame layout, handshake, multiplexed streams) see the Bifaci protocol documents.
This page covers the macOS-specific layer on top of that runtime: how FabricLoom finds cartridges on disk, how the operating system isolates them, what you must sign and notarize to ship one, and how to package it into a .pkg installer that users can double-click.
For an end-to-end tutorial, use Build your first cartridge. It goes from the maintained Python scaffold to an installed dev cartridge and a hosted peer call.
Where cartridges live on disk
On macOS, FabricLoom scans two cartridge roots at startup and watches them for changes:
FabricLoom.app/Contents/Resources/cartridgesrv-signed/cartridges/: bundled cartridges that ship with the application./Library/Application Support/FabricLoom/Cartridges/: user-installed cartridges..pkginstallers write here, and you populate it by hand during development.
Inside either root, every cartridge follows the layout the CapDAG spec mandates:
{cartridges-root}/{slug}/v{registry_version}/{channel}/{name}/{version}/
cartridge.json
{entry-binary}
{slug} is dev for unpublished cartridges. For cartridges published through a registry, it is a deterministic, human-readable transform of the registry’s domain (its host, plus :port if present): lowercased, with any character outside [a-z0-9.-] replaced by -. v{registry_version} is the registry regime version the cartridge was installed under (v1 today) and applies to every install including dev (for example dev/v1/release/...). {channel} is nightly or release. {name} and {version} come from the cartridge’s manifest and must match the directory names exactly. The host verifies this at discovery time and rejects mismatches as bad_installation.
The published registry manifest declares two fields alongside its entries: registryVersion, an integer giving the regime version that determines the v{registry_version} layout level, and fabricRegistryUrl, the fabric registry the cartridges were built against. If you add extra or custom cartridge registries through the app UI or the capdag CLI, every registry must declare the same fabricRegistryUrl so that their cartridges stay comparable. A registry built against a different fabric is rejected.
The cartridge.json install record is the host’s pre-spawn validation contract. Required fields are name, version, channel, registry_url (present but possibly null), entry, and installed_at. Discovery can report manifest_invalid, bad_installation, entry_point_missing, or handshake_failed when the record, layout, entry, or protocol startup is invalid.
Sandboxing and process isolation
Cartridges run in a sandboxed XPC service, separate from both the engine and the user-facing app. FabricLoom never loads cartridge code into its own process.
At runtime:
- The XPC service holds the manifests of every discovered cartridge in memory.
- When a cap is dispatched to a cartridge, the service spawns the cartridge’s entry binary as a child process.
- The cartridge speaks the Bifaci protocol on stdin/stdout: a handshake, then frame-multiplexed requests and responses.
- The XPC sandbox restricts what the cartridge can do. By default it has no network access and no access to user files except those explicitly handed to it.
- Heartbeats keep the connection alive, and a missed heartbeat tears the cartridge down.
The host runtime side of this (process spawn, frame routing, health monitoring) is CapDAG host runtime. The relay layer that bridges multiple cartridges through a single connection is relay switch and relay topology.
The XPC envelope and the sandbox profile are the macOS-specific parts. A cartridge that needs entitlements beyond the default, such as network access for a cloud-proxy cartridge, declares them in the .pkg and must be reviewed before it is accepted into the canonical manifest.
Code signing and notarization
Cartridges distributed through MachineFabric, meaning anything that ends up in the canonical cartridge manifest, must be:
- Code-signed with a Developer ID certificate.
- Notarized by Apple.
- Packaged as a signed
.pkginstaller.
For local development, use capdag dev-install .. The CLI validates the manifest and stages it in the per-user development registry. The Build your first cartridge tutorial demonstrates that workflow.
For distribution, the build pipeline looks like:
# 1. Build a universal binary (Rust example)
cargo build --release --target aarch64-apple-darwin
cargo build --release --target x86_64-apple-darwin
lipo -create -output mycartridge \
target/aarch64-apple-darwin/release/mycartridge \
target/x86_64-apple-darwin/release/mycartridge
# 2. Sign the binary with hardened runtime
codesign --sign "Developer ID Application: Your Name (TEAMID)" \
--options runtime \
--timestamp \
mycartridge
# 3. Stage the install layout
mkdir -p pkg-root/{slug}/v{registry_version}/{channel}/mycartridge/1.0.0
cp mycartridge cartridge.json pkg-root/{slug}/v{registry_version}/{channel}/mycartridge/1.0.0/
# 4. Build the installer package
pkgbuild --root ./pkg-root \
--identifier com.yourcompany.mycartridge \
--version 1.0.0 \
--install-location "/Library/Application Support/FabricLoom/Cartridges" \
mycartridge.pkg
# 5. Sign the package
productsign --sign "Developer ID Installer: Your Name (TEAMID)" \
mycartridge.pkg \
mycartridge-signed.pkg
# 6. Notarize and staple
xcrun notarytool submit mycartridge-signed.pkg \
--apple-id [email protected] \
--team-id TEAMID \
--password "@keychain:AC_PASSWORD" \
--wait
xcrun stapler staple mycartridge-signed.pkg
The {slug}/v{registry_version}/{channel}/ portion of the install path follows the CapDAG layout rules. For a registry-published cartridge, {slug} is the domain transform of the registry URL (its host, plus :port if present, lowercased with any character outside [a-z0-9.-] replaced by -).
Languages and SDKs
The cartridge protocol is language-agnostic. Any executable that implements the manifest and Bifaci contracts can be a cartridge. The maintained runtime paths are:
| Language | Package | Notes |
|---|---|---|
| Rust | capdag and capdag-cartridge-sdk |
Reference runtime plus product contracts |
| Go | capdag-go and capdag-cartridge-sdk-go |
Runtime mirror plus mirrored LLM contracts |
| Swift / Objective-C | capdag-objc and capdag-cartridge-sdk-objc |
Apple runtime and product helpers |
| Python | capdag on PyPI |
Runtime used in Build your first cartridge |
The CapDAG packages implement URN, planning, Bifaci, and runtime contracts. MachineFabric’s product SDKs add shared LLM payloads and selected product policies, and their exact surface differs by language. See the Cartridge SDK reference and the CapDAG handler patterns.
The SDK is not the protocol contract. Another implementation conforms by producing a valid manifest and implementing Bifaci, but the maintained scaffolds are the supported starting point and encode details that are easy to get wrong.
Implementation differences between hosts
Rust is the CapDAG behavioral reference. FabricLoom uses a macOS XPC host and a Rust daemon host on Linux and Windows. CapDAG also maintains Go and Swift host/relay implementations for their documented roles. These implementations share the wire contract but do not have identical module boundaries. See CapDAG Rust vs Swift for that pair’s API and host differences.
For the public failure contract used by cartridge runtimes and hosts, see error handling. Task persistence and operating-system pressure handling are host implementation concerns, not part of the public cartridge protocol.
Publishing
To make your cartridge available through FabricLoom’s cartridge browser, propose it on cartridge-shelf, the public submissions inbox. Every submission is reviewed by hand and merged into the canonical cartridges.machinefabric.com manifest that ships with every install.
The full submission process and inclusion criteria (what the repository must contain, what cap URN validity entails, and what happens between submission and acceptance) are in Contributing.