A rig bundle can be shared, and anyone with it can read what's inside, so `rig bundle create` no longer packs a harness login file by accident. It refuses `.claude/.credentials.json`, `.codex/auth.json` and `.pi/agent/auth.json`, at the top of an agent folder or carried project or nested anywhere inside one, and the message names the file, says anyone with the bundle could read it, and says how to fix it: remove it and build again, or, only if recipients should have this login (for example, your own internal test bundle) and its owner agrees, recreate with `--include-login-files`.
That opt-in is off by default. It records the exact archive paths of the login files actually included (`included_login_files` in the manifest), and verify, unpack, inspect, the install plan and bootstrap accept only those recorded paths, each with a visible notice. An unrecorded login file, an extra file, a malformed, directory, wildcard or traversal record, or a hash mismatch still fails. The record is the creator's unsigned declaration, not proof of consent, trusted origin or safe credentials. There's no machine setting, force or wildcard.
The check extends the existing shared sensitive-file predicate and integrity verifier, so ordinary `auth.json` files elsewhere and other harness files are unaffected. One fixed-time ordinary v2 bundle and one v1 bundle were checked byte-identical to main's.
Changed results:
1. A bundle that contains one of the three login files, created today without the flag, is now refused, and no archive is written.
2. Existing archives that hold one of those files without a record now fail verify, unpack and install (install returns 400, even with --force or --skip-version-check). Inspection stays a 200 diagnostic with integrity false, and the CLI exits 2.
3. The CLI author check now reports login-file findings, and the CLI and web bundle inspector now show integrity errors.
4. Inspecting a v1 bundle whose manifest can't be parsed or normalized now returns the 200 diagnostic ("Invalid v1 manifest: …"), the same as v2 already did, where it used to fail with 500. Install, verify and unpack are unchanged for those.
Notices and errors name paths, never file contents.
42 KiB
RigBundle Reference
Version: 2 (pod-aware)
Last validated against code: 2026-10-07, at the 0.6.6 cut 2620dea8 (whole document at a350c59b; changes to its sources since then checked)
Source of truth: packages/daemon/src/domain/bundle-types.ts, packages/daemon/src/domain/bundle-archive.ts, packages/daemon/src/domain/pod-bundle-assembler.ts, packages/daemon/src/routes/bundles.ts, packages/cli/src/commands/bundle.ts, packages/daemon/src/domain/bundle-install-context.ts, packages/daemon/src/domain/bundle-source-resolver.ts, packages/daemon/src/domain/bootstrap-orchestrator.ts
A .rigbundle is a self-contained distributable archive that packages a rig spec, all referenced agent specs, their resources (skills, guidance, startup files), culture file, documentation, and an integrity manifest into a single file. The recipient can install and launch the rig without needing the original source tree.
GitHub folder links
rig bundle create, inspect, install, and rig up accept a public HTTPS GitHub folder link:
rig bundle create https://github.com/example/teams/tree/main/rigs/team -o team.rigbundle
rig bundle inspect https://github.com/example/teams/tree/v1/rigs/team --json
rig up https://github.com/example/teams/tree/COMMIT/rigs/team --target ./project
Replace COMMIT with a full commit ID. Branches and tags are also accepted, including names containing /: they
resolve once to a full commit before fetching. Each result names the resolved source, configuration ID, package digest
and assembler. Save the printed commit-pinned URL to repeat the same source selection. Only a full 40-character commit
ID counts as a commit; a shorter one is looked up as a branch or tag name. --preset and repeatable
--seat pod.member=runtime select only configurations the folder declares. A link build takes the rig's name from
its rig.yaml unless --name says otherwise, and is named github-bundle when rig.yaml has no readable name.
Link import needs a running, verified local daemon and a credential-free, publicly readable GitHub URL. It does not
upload files to remote daemons. For a remote target, run the link command on that host, or create an archive, transfer
it with its sibling digest, and use the existing path command there. Existing local path/name commands keep their
dispatch. Importing does not install dependencies or execute repository scripts. Before packaging a link, it checks
the checkout's symlinks (excluding Git metadata), member agent refs, agent imports and legacy package refs (including
additional --include-packages entries): every target must resolve inside the fetched checkout. Shared agents and
legacy packages outside the selected folder are allowed within that repository. Preset staging
preserves that directory layout. Unresolved or escaping references stop the import and remove its temporary source.
The selected source is packaged through the existing bundler once. Link up/install then use the existing bundle
install path, including compatibility and target-conflict checks. For a link, the default install target is the current
directory; --cwd separately overrides seat working directories. Imported archives and source/build receipts remain
under the selected OpenRig home's bundle-imports/ directory for install history. If a response is lost, inputs stay
available and the outcome is reported as unknown; inspect rig ps and rig bundle history before retrying.
An author's root bundle.yaml can declare compatibility.min_cli_version and compatibility.min_daemon_version.
Link import passes these into create; explicit create minimum-version flags override them. A local-path create doesn't
read them: it packages a minimum only from --min-cli-version or --min-daemon-version.
The package digest covers the packaged integrity.files entries, excluding bundle.yaml, ignored junk basenames,
and file modes. It does not cover resources resolved on the installing host or prove what ran. The archive hash is
separate. See bundle formats for the exact coverage and identity definitions. Older archives can
have unknown source, configuration or assembler; inspection does not fill those gaps with the inspecting daemon's
identity.
Advisory author check
rig bundle check ./my-team --json
This local, read-only check reports openrig.bundle-standard/v1 with pass, finding or not_checked per rule. It
checks pod-aware rig validity, README inclusion in docs, readable declared files, in-folder agent refs/imports,
minimum-version shape, declared preset consistency and known sensitive filenames. It does not contact a daemon,
build an archive, run preflight or launch anything. Findings give exit status 1; they are never an install gate. It
reads the folder on disk, not a link or a commit, so files that aren't committed are checked too. Human output gives
each rule's status, ID and reason, then the file it's about in parentheses when there is one; --json carries the
same as a path field.
README completeness and arbitrary embedded secrets need human review and are reported as not_checked. A bounded
or unreadable scan is also not_checked, never a clean scan. The check is advice for authors, not a security audit or
listing approval.
Archive Format
A .rigbundle file is a gzip-compressed tar archive (.tar.gz) with a fixed structure.
File extension
The archive MUST have the .rigbundle extension. The packer rejects output paths that don't end with .rigbundle.
Sibling digest file
Every .rigbundle has a sibling .rigbundle.sha256 file containing the SHA-256 hex digest of the archive. This detects corruption during transfer. The unpacker verifies this digest before extraction.
Example:
my-rig.rigbundle — the archive
my-rig.rigbundle.sha256 — "a1b2c3d4..." (64-char hex SHA-256)
Determinism
The packer produces deterministic output:
- Files are sorted alphabetically
- A fixed mtime is used (
2026-01-01T00:00:00Z) - Portable mode normalizes uid/gid/mode
- Maximum gzip compression (level 9)
Identical staged bytes (including manifest timestamps and provenance) produce the same archive hash. Use the package digest to compare packaged file content independently of manifest metadata.
Archive Layout
bundle.yaml — manifest (required)
rig.yaml — the RigSpec (required, may be rewritten)
CULTURE.md — culture file (if declared in rig spec)
SETUP.md — documentation (if declared in rig spec docs field)
agents/
agent-name/
agent.yaml — AgentSpec
guidance/
role.md — guidance files
skills/
skill-name/
SKILL.md — skill files
startup/
context.md — startup files
context-packs/<name>/ — a pack carried with --context-pack (its manifest.yaml and declared files)
project/ — the project carried with --project-dir (project.yaml and files beside it)
skills/<name>/ — a shared skill the author's bundle.yaml declares; other declared
plugins, workflow specs, context packs and agent images keep their paths
Key rules
bundle.yamlis the manifest — always present, always at the rootrig.yamlis the rig spec — rewritten during assembly with vendoredagent_refpaths- Agent directories are vendored copies of the original agent specs with all their resources
- Import refs are rewritten from the original
local:orpath:paths to bundle-relativelocal:paths - All file paths within the archive are safe relative paths (no
.., no absolute, no symlinks)
Manifest (bundle.yaml)
The manifest is a YAML file at the archive root that describes the bundle contents.
Schema Version 2 (pod-aware, current)
schema_version: 2
name: my-bundle
version: "0.1.0"
created_at: "2026-04-11T22:32:48.570Z"
rig_spec: rig.yaml
agents:
- name: pm-lead
version: "1.0"
path: agents/pm-lead
original_ref: "local:agents/pm-lead"
hash: "ed4cff20..."
import_entries: []
- name: researcher
version: "1.0"
path: agents/researcher
original_ref: "local:agents/researcher"
hash: "11f8a077..."
import_entries:
- name: shared
version: "1.0"
path: agents/shared
original_ref: "local:../../shared"
hash: "abc123..."
culture_file: CULTURE.md
provenance:
created_at: "2026-04-11T22:32:48.570Z"
daemon_version: "0.6.6"
cli_version: "0.6.6"
# ... and, for a GitHub link, the source it was built from
assembler:
openrigVersion: "0.6.6"
configuration:
id: "dev.lead=claude-code,dev.researcher=codex"
integrity:
algorithm: sha256
files:
rig.yaml: "b80c0674..."
CULTURE.md: "9354361b..."
agents/pm-lead/agent.yaml: "ed4cff20..."
# ... every file in the archive
Manifest Fields
| Field | Type | Required | Description |
|---|---|---|---|
schema_version |
number | yes | Must be 2 for pod-aware bundles. |
name |
string | yes | Bundle name. |
version |
string | yes | Bundle version. |
created_at |
string | yes | ISO-8601 timestamp of creation. |
rig_spec |
string | yes | Relative path to the rig spec within the archive. Safe relative path. |
agents |
AgentEntry[] | yes | Array of vendored agent entries. |
culture_file |
string | no | Relative path to the culture file if present. |
included_login_files |
string[] | no | Exact archive-relative harness login files deliberately included by the producer. Only these paths are exempt from the login-file refusal; this unsigned declaration is not authentication or receiver approval. |
integrity |
Integrity | no | Per-file SHA-256 checksums for content verification. Validation doesn't require it, but install refuses an archive without it and inspect reports integrity FAIL. |
provenance |
object | no | Stated, not verified: created_at, daemon_version, cli_version, source_host, author_session, source_rig_id, source_rig_name, notes, and source for a GitHub link build. |
assembler |
object | no | openrigVersion of the OpenRig that built the archive. |
configuration |
object | no | The configuration ID the archive was built in, and preset when it matches a declared preset. Outside the package digest. |
compatibility |
object | no | min_cli_version and min_daemon_version, checked on install. |
preconditions |
list | no | The author's setup declarations, shown before install and never run (see bundle formats). |
skills, plugins, workflow_specs, context_packs, agent_images |
lists | no | Contents from the author's bundle.yaml and --context-pack, routed into your libraries on install. |
project |
object | no | id and path of the project carried with --project-dir. |
Agent Entry Fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Agent name (from agent.yaml). |
version |
string | no | Agent version. |
path |
string | yes | Relative path to the vendored agent directory. Safe relative path. |
original_ref |
string | written by create | The original agent_ref before rewriting. Validation doesn't require it. |
hash |
string | yes | SHA-256 hash of the agent.yaml content. |
import_entries |
ImportEntry[] | written by create | Vendored imports for this agent (may be empty). Validation doesn't require it. |
Import Entry Fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Imported agent name. |
version |
string | yes | Imported agent version. |
path |
string | yes | Relative path to the vendored import within the archive. |
original_ref |
string | yes | The original import ref before rewriting. |
hash |
string | yes | SHA-256 hash of the imported agent.yaml. |
Harness login files
By default, create refuses .claude/.credentials.json, .codex/auth.json and .pi/agent/auth.json
at the root or nested anywhere in selected bundle content, including agents and an explicitly carried project.
The author check reports them too. Messages name paths, never contents, and explain both remedies:
remove the file from the agent folder or carried project and build again; or, only if recipients should have this
login (for example, your own internal test bundle) and its owner agrees, recreate with
rig bundle create <spec> -o <archive.rigbundle> --include-login-files.
Anyone with that archive could read the included login files.
The flag applies to this create only. The generated included_login_files manifest list names only the actual,
exact archive-relative login files included. Inspect, unpack, install and bootstrap honour those paths without
another flag and show a credential-bearing notice. An additional unrecorded login file still fails, as do malformed
records, directory or wildcard exemptions, and unrelated sensitive files. There is no machine setting or --force
bypass. The unsigned record states the producer's choice; it does not authenticate the producer, establish trust,
or constitute a fresh recipient approval. Review the source and files before using a bundle.
Compatibility change: previously accepted bundles containing these files now fail default creation and downstream
verification/install unless deliberately rebuilt with the record. Inspection remains diagnostic: it reports failed
integrity. Ordinary auth.json elsewhere and non-login harness files are unchanged. Filename checks do not scan
arbitrary contents for secrets.
Integrity Section
| Field | Type | Required | Description |
|---|---|---|---|
algorithm |
string | yes | Must be sha256. |
files |
map<string, string> | yes | Map of archive-relative file path → SHA-256 hex hash. Every file in the archive except bundle.yaml, .DS_Store, Thumbs.db and .gitkeep is listed; an extra file that isn't listed fails verification. |
Security Model
Bundle integrity provides self-consistency verification, not authenticity.
- The sibling
.sha256file detects corruption during transfer - The per-file integrity hashes detect tampering of individual files within the archive
- Neither mechanism authenticates the bundle author
An attacker who can rewrite the full bundle + digest can bypass verification. Users must trust the source they obtained the bundle from. This is the same trust model as unsigned npm packages and Docker images.
Future enhancement: cryptographic signing (Ed25519) for author authentication.
Safety Guarantees
The unpacker enforces these safety rules before extraction:
- No symlinks or hardlinks —
SymbolicLinkandLinkentries are rejected - No absolute paths — entries starting with
/, or carrying a Windows drive letter such asC:\, are rejected - No path traversal — entries containing
..segments are rejected, on either slash direction (..or..\) - Digest verification — archive SHA-256 must match the sibling
.sha256file - Content integrity — after extraction, per-file hashes are verified against the manifest
If any check fails, extraction is aborted and an error is thrown.
rig bundle inspect applies the same unsafe-entry scan (rules 1-3) before it extracts into its temporary directory, so an archive the installer refuses for an unsafe entry is never reported as inspectable. Digest and content-integrity failures are a different case: inspect reports those in its result (digestValid, integrityResult) instead of refusing, which is what lets it describe a broken bundle rather than throw.
CLI Surface
Paths
rig bundle create, inspect and install resolve every path you give them (<spec-path>, -o, --rig-root,
<bundle-path>, --target) to an absolute path against your current directory before sending the request. The
daemon then reads and writes those paths on its own host: nothing is uploaded, so the files must already exist
where the daemon runs.
Create a bundle
rig bundle create <spec-path-or-github-link> -o <output.rigbundle> [--rig-root <dir>] [--preset <name>] [--seat <pod.member=runtime>]... [--context-pack <dir>]... [--project-dir <dir>] [--name <name>] [--bundle-version <ver>] [--min-cli-version <ver>] [--min-daemon-version <ver>] [--notes <text>] [--include-packages <refs...>] [--allow-drift] [--include-login-files] [--json]
| Flag | Required | Default | Description |
|---|---|---|---|
<spec-path-or-github-link> |
yes | — | Path to the rig spec YAML file, or a GitHub folder link (see "GitHub folder links" above). |
-o, --output |
yes | — | Output path. Must end with .rigbundle. |
--rig-root |
no | spec directory | Root directory for resolving agent_ref and other relative paths. |
--preset <name> |
no | — | Build one of the configurations the bundle declares in configurations.yaml beside rig.yaml (see bundle-formats.md). The chosen runtimes and profiles are applied to an owned copy of the rig folder, never to yours. |
--seat <pod.member=runtime> |
no | — | Use this runtime for one seat, within what configurations.yaml allows. Repeatable; applied after --preset. An undeclared choice is refused with the allowed set, and nothing is built. |
--context-pack <dir> |
no | — | Carry the context pack in <dir>. Repeatable. The directory may be outside the rig folder, for example a world pack whose manifest.yaml is at its repository root. Only manifest.yaml and the files it declares are carried, the same set rig context add --git installs, and the pack lands in the bundle at context-packs/<manifest name>/. Needs a pod-aware spec. |
--project-dir <dir> |
no | — | Carry the project the rig works in: the folder holding its project.yaml (which must declare an id) and files beside it, such as SPEC.md. On install the project is registered in the workspace catalog and the rig is associated with it, before any seat launches (see project-workspace.md). Needs a pod-aware spec. |
--name |
no | the rig's name in rig.yaml; my-bundle (github-bundle for a link) when it has no readable name |
Bundle name in the manifest. |
--bundle-version |
no | 0.1.0 |
Bundle version in the manifest. |
--min-cli-version, --min-daemon-version |
no | — | The oldest OpenRig CLI or daemon that may install the bundle, checked on install. For a link they override the author's bundle.yaml minimums. |
--notes <text> |
no | — | Notes stored in the manifest's provenance. |
--include-packages <refs...> |
no | all from the spec | Legacy (schema 1) specs only: the package refs to include. |
--include-login-files |
no | off | Only if recipients should have these logins and their owner agrees (for example, an internal test bundle), include them and record their exact archive-relative paths. Create and consumers show a credential-bearing notice. Does not exempt other sensitive files. |
--allow-drift |
no | off | Build a spec that differs from the running rig of the same name. Without it, create refuses; with it, the difference is written into the manifest's provenance notes. |
--json |
no | off | Machine-readable output. |
--context-pack and --project-dir exist only on create. rig up and rig bundle install have neither, so a bundle
installed straight from a GitHub link carries no project, and carries a context pack only when the folder's own
bundle.yaml declares one inside the folder.
The create command:
- Validates the rig spec, and refuses a spec that differs from the running rig of the same name (see
--allow-drift) - Collects the culture file, docs files, and rig-, pod- and member-level startup files
- Resolves all
agent_refpaths and their imports, vendors the agent specs and their resources, and rewrites the refs to bundle-relativelocal:paths - Adds the author's
bundle.yamlcontents (preconditions, skills, plugins, workflow specs, context packs, agent images), the carried packs and project, and the configuration and assembler stamps - Computes per-file integrity hashes, refusing sensitive paths (such as
.env,*.pem,*.key,*.p12,credentials.*andtokens.*), and writes the manifest (bundle.yaml) - Refuses staged content that can't be shipped publicly
- Packs into a deterministic
.tar.gzand writes the sibling.sha256digest
List a bundle's configurations
rig bundle configurations <spec-path> [--json]
Lists the presets that configurations.yaml declares, each with its configuration ID, which one is recommended, and which one matches rig.yaml as written.
Inspect a bundle
rig bundle inspect <bundle-path-or-github-link> [--preset <name>] [--seat <pod.member=runtime>]... [--json]
Shows the manifest, the archive's identity (source and commit for a link, configuration, package SHA-256 and its
coverage, assembler, archive SHA-256), digest validity, the integrity result, any carried context packs and project,
and then the behaviour view. Inspect extracts the archive into a temporary directory for safe validation, then cleans
that directory up. It does not install or launch anything. It exits 2 when the digest or integrity check fails.
--preset and --seat choose a declared configuration of a GitHub link.
The behaviour view describes the selected archive: its team and configured models, declared permission posture, files agents receive, startup actions and hooks, managed writes, literal outside domains, prerequisites and what remains unknown until launch. It reads packaged declarations; it does not run scripts, contact providers, probe runtimes or create a bootstrap run. Source and assembler identity come from the archive, separately from the view generator. Configured models are not observations of a running model, and literal domains are not predicted traffic.
Under "Needs (not checked)" the view lists the author's setup preconditions, with their commands marked "not run; one
shell, in order". For a declared full-bypass Claude seat it notes that Claude Code asks once to accept its bypass
warning, and when any Claude or Codex seat is full bypass it says that --non-interruptive is available (see
non-interruptive mode). It doesn't select the mode or check whether you've already
accepted the warning. When the bundle's rig.yaml declares non_interruptive: true, the view instead says that the
bundle declares non-interruptive launches for those seats, with broad file, command and network access, and that
--no-non-interruptive overrides it; the asks-once note is left out.
rig bundle install and bundle-form rig up print this same view to stderr before the existing action, including
--plan. With --json, stdout remains one result; its optional behaviour field carries the structured view.
GitHub links reuse the single prepared archive. The header is informational: missing views and inspection errors
print a diagnostic, then the original installation checks and result determine success. Legacy schema-1 archives
report not_generated, rather than an empty team. A local archive can change between inspection and installation;
the header does not bind a later action atomically to those bytes.
Agents can run shell commands as the launching user, subject to runtime and host policy. Explicit permission bypass
is distinguished from conditional launch floors; Pi resource trust is separate from native permission policy.
Host settings, accounts, plugins, runtime support and effective permissions remain unknown. Text scanning is bounded
and reports unread members; dynamically computed commands and addresses cannot be predicted. Installation targets,
--cwd and configured library roots resolve separately from the archive's declarations. See
bundle formats for the structured view and digest coverage.
Integrity means the archive is self-consistent, not who made it. Provenance is stated by the bundle and not verified.
Install a bundle
rig bundle install <bundle-path-or-github-link> [--plan] [--yes] [--target <root>] [--cwd <dir>] [--preset <name>] [--seat <pod.member=runtime>]... [--non-interruptive | --no-non-interruptive] [--skip-version-check] [--force] [--json]
| Flag | Required | Default | Description |
|---|---|---|---|
<bundle-path-or-github-link> |
yes | — | Path to the .rigbundle file, or a GitHub folder link. |
--plan |
no | false |
Preview without installing or launching. Not side-effect free: it records a bootstrap run and runs the preflight, which for a pod-aware bundle runs pi --version or omp --version for Pi or OMP seats and codex -p <profile> mcp list for a Codex seat that names a codex_config_profile. It writes nothing to the target. |
--yes |
no | false |
Auto-approve trusted actions during apply mode. |
--target <root> |
yes in apply mode, for an archive path | the current directory for a link | Directory the bundle is installed into and launched from. Required for an archive path unless --plan is used. |
--cwd <dir> |
no | — | Working directory for every launched member, for this install only (for example, the repository the rig works on). Does not change the install target. |
--preset <name>, --seat <pod.member=runtime> |
no | — | For a GitHub link, the declared configuration to build and install. |
--non-interruptive |
no | the bundle rig.yaml's non_interruptive, else the operator setting launch.non_interruptive, else off |
Accept the harnesses' first-launch warnings for this rig's full-bypass Claude and Codex seats with launch flags, writing nothing to your settings. The choice is saved on the rig. --no-non-interruptive turns a saved choice off; stop a running rig with rig down first. See non-interruptive mode. |
--skip-version-check |
no | off | Skip the compatibility check below. Not for routine use. |
--force |
no | off | Skip the running-team check below. Not for routine use. A pod-aware bundle is still refused before target files are written when a team with the same name is running; a legacy (schema 1) bundle can be left partly installed. |
--json |
no | false |
Emit machine-readable JSON. |
Install launches the rig; it does not just unpack it. It extracts the bundle to a temporary directory, validates integrity, and bootstraps the rig. In apply mode, the daemon requires targetRoot, so rig bundle install must be given --target <root> for an archive path unless you are running with --plan.
Before it plans or applies, install runs two checks on the archive, both also under --plan:
- compatibility: the manifest's
min_daemon_versionandmin_cli_versionagainst this daemon and CLI. A failure reports "Bundle compatibility check failed" with the versions involved. - running team: a running team with the bundle's rig name refuses the install with "Bundle install conflict check
failed". The error names the installed and the offered team and lists three choices: use the existing team, stop it
with
rig downand retry to replace it, or cancel. This check does not refuse a stopped team with that name.
Each error lists its fixes; the compatibility error also names --skip-version-check. --skip-version-check and
--force skip only these two checks: the manifest safety check of its rig_spec and project entries always runs.
For a pod-aware (schema version 2) bundle, installing a team whose rig name matches a stopped team replaces it: the
earlier team is archived when the new one is created, and the result gives the rig unarchive <id> command that
restores it. If OpenRig cannot confirm the earlier team is stopped, install refuses with generation_unconfirmed and
creates no replacement team. For an install into that team's own install folder, OpenRig makes this check before
writing any files; for any other target, bundle files already copied there stay.
A legacy (schema version 1) bundle does not replace a stopped team. Its approved external and package install steps run first, and then its rig-name check fails preflight with "Rig name '' already exists".
For a pod-aware (schema version 2) bundle, apply copies every extracted file (bundle.yaml, rig.yaml, agents/, culture and docs files, and any carried project/, context-packs/ and author-declared contents) into the target and launches from there, then removes the temporary extraction. So:
- the target becomes the rig root:
agent_refpaths resolve inside it, and a member withcwd: "."starts in the target (every pod-aware member must declare acwd); - an absolute member
cwdstays as authored, and--cwd <dir>(onrig bundle installorrig up) still overrides every member's cwd; - if the target already has a file with different content at any bundle path (for example its own
rig.yaml), install refuses withtarget_conflictand writes nothing. Identical target files don't cause a target-content conflict; the other installation checks above, including the running-team check, still apply. Use an empty or dedicated directory as the target. - exception: reinstalling over a stopped team into that team's own install folder (the target's
bundle.yamlis schema version 2 with the same bundle name, or withmy-bundleorgithub-bundle, the default names before 0.6.7) replaces its files there. Each differing path is first copied under<OpenRig home>/bundle-backups/reinstall-*/files/, with aRESTORE.jsonbeside it; other files in the folder are kept. The files stay installed even if a later launch fails.
The bundle's declared skills, plugins, workflow specs, context packs and agent images are routed into your libraries before any member launches, and the context-pack library is rescanned, so a member's first turn can already read a pack the bundle carried. A routing failure does not stop the install: it is printed as a warning, returned in routingFailures, and recorded in the install audit. The human output lists what each declared kind routed and any entry that was not routed.
A carried pack is routed into context.root under its folder name in the archive: for a pack carried with --context-pack, that is its manifest name (context-packs/<name>/); for a pack the author's bundle.yaml declares, it is the basename of the declared folder, whatever the manifest says. If a pack of that name is already installed, install never merges into it: an identical pack is reported as already_installed, and a different one (for example a rig context add --git install at another revision) is kept unchanged and reported as kept_existing, with the rig context rm <name> command to use the bundle's copy instead.
A carried project is registered as registered (a new catalog entry), associated (the rig added to an existing entry), already_registered or conflict. A conflict writes nothing to the catalog and is reported as a routing failure; the install continues. A project folder that already exists with different files is kept unchanged.
Skills shared by bundle profiles
For skills shared by several agents, place each complete skill directory at
skills/<name>/ beside the rig spec and declare its entry point in the source
bundle.yaml:
skills:
- skills/review-work/SKILL.md
rig bundle create includes that directory's helper scripts and references as
well as SKILL.md. The skill needs valid name and description frontmatter;
the name is the ID a profile selects with uses.skills: [review-work].
Undeclared sibling directories are not bundled. Other declared skill file paths
retain their single-file behavior.
Claude, Codex and Pi profiles can select this installed bundle pool even when
their working directory is elsewhere. Explicit AgentSpec resources keep priority
over a discovered skill with the same ID. Pi projects selected skills into its
seat's agent directory; it does not scan Claude or Codex home/workspace pools.
Installing the bundle does not add these skills to the operator's managed
skills.root catalog. Profiles that already get a skill from a plugin should not
also select its bundle copy.
When a profile selects a skill from inside its agent spec's folder (for example the
installed bundle) and the managed catalog holds a different copy with the same ID,
the bundle's copy is used. Launch warns skill_bundle_precedence and leaves the
catalog unchanged. The skill's SKILL.md name must equal the selected ID. A
differing copy from anywhere else fails launch with skill_identity_conflict.
Legacy (schema version 1) bundles keep their old behavior: --target is only where packages are installed, and their declared contents are routed after a completed install.
Launch directly
rig up <bundle-path-or-github-link> [--target <root>] [--cwd <dir>] [--plan] [--yes] [--preset <name>] [--seat <pod.member=runtime>]... [--non-interruptive | --no-non-interruptive] [--host <id>] [--json]
rig up auto-detects .rigbundle files and GitHub folder links and prints the behaviour view before it acts. The two
take different routes:
- A GitHub link builds the archive and installs it through the
rig bundle installpath, with its compatibility and rig-name checks and the install audit. - A local
.rigbundlepath goes through the bundle bootstrap path directly. It skips the compatibility check, isn't recorded in the install audit (sorig bundle historydoesn't list it), and doesn't print the routing summary. For a pod-aware archive, a running team with the same name is still refused before target files are written, and a stopped one is replaced as described under "Install a bundle". A legacy (schema 1) archive runs its approved install steps before its rig-name check, which then refuses a running team and fails preflight for a stopped one; it also gets no post-install routing this way. To get the checks and the audit for a local archive, userig bundle installorrig bootstrap.
rig bootstrap <bundle-path> also accepts archives and uses the ordinary bundle
install path, including compatibility checks and the install audit. Use --plan
to preview, or --target <root> to choose the persistent install directory. Its
default target is the caller's current directory; --cwd only changes the agents'
working directory. YAML files and library spec names keep the spec bootstrap path.
It prints no behaviour view and has no --non-interruptive option; a bundle's declared non_interruptive still applies.
--target <root>is the install target described above (for a schema-version-2 bundle, the directory the bundle is copied into and launched from)- if
--targetis omitted for a.rigbundleor a link, the CLI defaults the install target to the current working directory, so the bundle's files are written there rig upresolves a relative--targetagainst your current directory before sending it, likerig bundle install; with--host,--targetis sent as given and must be a path that exists on that host, and an omitted--targetis refused there ("targetRoot is required for bundle apply mode"). GitHub links don't work with--host.--cwd <dir>does not change the install target; it only overrides the launched members' working directory for that run--non-interruptiveworks as onrig bundle install- a schema-version-2 bundle's declared contents are routed before any member launches, as with
rig bundle install
Install history
rig bundle history [--rig <name>] [--since <iso>] [--json]
Lists the install audit records in bundle-audit.jsonl in the OpenRig home (default ~/.openrig): when, the outcome,
the rig and the bundle path.
--rig filters by target rig name and --since by install time. Installs through rig bundle install,
rig bootstrap <archive> and rig up <link> are recorded; rig up <local archive> isn't.
Assembly Process
When rig bundle create runs, the PodBundleAssembler stages the files (steps 1-4). The create route then adds the
author's bundle.yaml contents, the carried packs and project, and the configuration and assembler stamps, and does
steps 5-7:
- Parse and validate the rig spec
- Collect rig-level files:
- Culture file (if
culture_fileis set) - Docs files (if
docsarray is set) — required: missing docs fail assembly - Rig-level startup files
- Pod-level and member-level startup files
- Culture file (if
- For each member's
agent_ref:- Resolve the ref to an agent spec directory
- Copy the agent spec and all its resources (skills, guidance, hooks, startup, runtime resources)
- Recursively resolve and copy imports
- Record the agent entry in the manifest with its hash
- Rewrite the ref to a bundle-relative
local:path
- Write the rewritten rig spec to the staging directory
- Compute integrity — SHA-256 of every file in the staging directory
- Write the manifest (
bundle.yaml) with all entries and integrity - Pack the staging directory into a deterministic tar.gz
Terminal nodes
Members with agent_ref: "builtin:terminal" are bundle-native sentinels. They are not vendored — the runtime handles them directly.
Deduplication
If multiple members reference the same agent spec (same resolved path), the spec is vendored once and all members' refs are rewritten to the same bundle-relative path.
Import resolution
When an agent spec has imports, each import is resolved, vendored into the bundle, and the import refs in the vendored agent.yaml are rewritten to bundle-relative local: paths. Import entries are recorded in the manifest's agent entry.
Validation Rules Summary
Manifest validation (schema version 2)
schema_versionmust be2nameis required non-empty stringversionis required non-empty stringcreated_atis required non-empty stringrig_specis required and must be a safe relative pathagentsmust be an array- Each agent must have
name,path(safe relative), andhash - Optional blocks, when present, must be well formed:
provenance,compatibility,skills,plugins,workflow_specs,context_packs,agent_images, andproject(anidmade of letters, digits,.,_and-, and a safe relativepath), plusincluded_login_files(unique safe relative paths)
The manifest validator doesn't check integrity; unpack does (below).
Archive safety (enforced on unpack)
- No symlinks or hardlinks
- No absolute paths
- No
..path traversal - Archive digest must match sibling
.sha256 - The manifest must have an integrity section, and per-file content hashes must match it
Assembly validation
- Rig spec must validate
- All
agent_refpaths must resolve to valid agent specs - All declared
docsfiles must exist on disk (missing docs fail assembly) - Culture file and startup files are collected best-effort (missing = skipped)
- A declared skill that's missing or unreadable is a warning, written into the manifest's provenance notes; it doesn't stop the build
- Sensitive paths ("Sensitive paths detected in bundle") and content that can't be shipped publicly ("Public artifact substance refusal") stop the build
Legacy Bundles (Schema Version 1)
Schema version 1 bundles are the pre-reboot format using flat-node rig specs and package-based bundling. They are still supported for backward compatibility but should not be created for new rigs.
Key differences from v2:
schema_version: 1in the manifestpackagesarray instead ofagentsarray- Package entries have
original_sourceinstead oforiginal_ref - No
import_entriesin package entries - Legacy rig spec format (flat nodes, not pods)
The v1 validator checks a project block the same way as v2, so a v1 bundle can't write outside the projects root or
read outside the bundle.
Example: Creating and Using a Bundle
Create
# From the rig directory
rig bundle create rig.yaml -o my-team.rigbundle --rig-root . --name my-team --bundle-version 1.0.0
Output:
Bundle created: my-team.rigbundle
Name: my-team v1.0.0
Hash: a1b2c3d4e5f6...
Inspect
rig bundle inspect my-team.rigbundle
The manifest summary and identity lines are followed by the behaviour view:
Bundle: my-team v1.0.0
Configuration: dev.lead=claude-code,dev.researcher=codex
Package SHA-256: 3f9a...
Coverage: openrig.package-digest/v1; packaged files only; excludes bundle.yaml, ignored junk and file modes; not host-resolved resources or execution.
Assembler: OpenRig 0.6.6
Archive SHA-256: a1b2c3d4...
Digest valid: true
Integrity: PASS
What this bundle declares before installation:
...
Install and launch
cd ~/projects/my-project
rig up /path/to/my-team.rigbundle
Equivalent explicit form:
rig up /path/to/my-team.rigbundle --target ~/projects/my-project
The bundle is extracted to a temporary directory and its integrity is verified. A schema-version-2 bundle is then copied into the target root and the rig is launched from there, with all agents and resources from the bundle. If you also want agents to launch with a different working directory for that run, pass --cwd <dir> separately.
A running team must be stopped before reinstalling. Reinstalling into a stopped team's recorded install folder preserves conflicting files in the instance's bundle-backups directory; the result names the backup. The folder must still contain that bundle's installed manifest. A copied same-name manifest in another folder does not authorize replacing its conflicting files. Other targets retain first-install behavior: different files at the same paths are refused before writing. New installations record the canonical target folder for each generation, independently of --cwd. Older generations and new generations created directly from rig.yaml have no recorded folder and retain the manifest-only check until their next successful bundle installation, so a copied same-name manifest can still qualify for those generations.