The README, reference and as-built documents are refreshed to the 0.6.8 cut.
19 KiB
kind, title, status, topics, domains, applies-when, siblings, prerequisite-reads, last-verified-against-source, last-updated
| kind | title | status | topics | domains | applies-when | siblings | prerequisite-reads | last-verified-against-source | last-updated | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| as-built | Packaging, Bootstrap, Bundles, Legacy Install Engine | active |
|
|
Locate npm package assembly, topology-bundle creation and validation, source routing, bootstrap plan/apply, or the retained package install engine. |
|
|
e8f0ab340d |
2026-10-08 |
Packaging, bootstrap and bundles
This module describes source at main commit
e8f0ab340db773392ec8be75b072d1c0f3068a50. Source paths below are repository-relative.
An npm CLI artifact and a .rigbundle have different builders and consumers; neither
source verification nor archive integrity establishes that a daemon has adopted an artifact.
npm CLI assembly
scripts/build-package.sh builds the daemon, web UI, TUI and CLI, then assembles their
outputs under packages/cli. The publishable file list and binary declarations live in
packages/cli/package.json.
The assembler writes generated BUILD_INFO modules into the daemon and CLI outputs with
the package version, Git commit, dirty flag and build time. The dirty check covers tracked
and untracked inputs under packages/ and scripts/; it is not a whole-worktree status
claim.
The same script:
- Generates shipped context packs through
scripts/generate-context-packs.mjs. - Copies daemon output, assets, specs, context packs, policies and
docs/reference/. - Checks the specs staging input with
scripts/check-internal-leak-guard.mjsand writes the staged substance-root inventory into the package. - Copies the web UI and TUI outputs.
- Runs
scripts/rewrite-daemon-imports.mjsso staged CLI/TUI JavaScript resolves the shipped daemon output rather than an unpublished workspace package.
This is the build layout. Native dependency installation and consumer runtime behavior need their own checks; this document does not infer them from a successful assembly.
Topology-bundle creation
packages/daemon/src/routes/bundles.ts mounts create, inspect, install and history handlers
under /api/bundles (the mount is in packages/daemon/src/server.ts).
The create handler validates the source spec before comparing it with recorded live topology. A divergent spec is refused unless the request explicitly allows drift; an allowed divergence is recorded in the bundle's provenance notes. The exported spec is not silently rewritten from live state.
| Source shape | Builder and output |
|---|---|
| Pod-aware rig spec | packages/daemon/src/domain/pod-bundle-assembler.ts: PodBundleAssembler.assemble() produces schema-version-2 metadata, vendors resolved agent trees, and rewrites agent references to local paths. |
| Legacy rig spec | packages/daemon/src/domain/bundle-assembler.ts: LegacyBundleAssembler (imported as BundleAssembler by the route) collects the referenced legacy packages and produces the legacy manifest shape. |
The pod builder collects culture, docs and startup material, preserves file bytes, and skips vendoring the terminal sentinel as an agent. A copied file keeps its source mode with owner read and write added. Required document collection failures stop assembly; missing declared skills can instead produce warnings, which are also written into the provenance notes. Inspect the builder's collection helpers when changing these distinctions.
Both route branches can consume a source-root bundle.yaml to carry declared skills,
plugins, workflow specs, context packs, agent images and preconditions into staging before
computing integrity. Manifest types, validation and serialization are in
packages/daemon/src/domain/bundle-types.ts. Provenance and compatibility are metadata;
provenance is not an author signature.
Preconditions are carried as data. Commands containing shell operators produce a warning, inspect reports the block, and the behaviour view lists it under needs. The install and bootstrap code do not reference preconditions.
Two more inputs are accepted only for a pod-aware spec; the legacy branch refuses them with a 400:
--context-pack <dir>(vendorContextPackDir()inpackages/daemon/src/domain/bundle-carried-context-pack.ts) carries a pack'smanifest.yamland its declared files tocontext-packs/<manifest name>/. The pack may sit outside the rig folder.--project-dir <dir>(vendorProjectDir()inpackages/daemon/src/domain/bundle-carried-project.ts) carries a folder with aproject.yamltoproject/.
Create also refuses content it must not ship. computeIntegrity() in
packages/daemon/src/domain/bundle-integrity.ts throws on sensitive paths (.env files,
*.pem, *.key, *.p12, credentials.*, tokens.*, .git/ and node_modules/).
assertShippableSubstance() in packages/daemon/src/domain/agent-resolver.ts runs on
vendored content and on the final staging tree, and refuses text containing
substrate/shared-docs/ and files declaring taxonomy: lore.
Configurations and package identity
A bundle folder may declare alternate team configurations in configurations.yaml beside
rig.yaml (packages/cli/src/lib/bundle-configuration.ts). rig bundle configurations <spec> lists them. rig bundle create --preset <name> or --seat <member=runtime> resolves
one and stages a copy of the rig folder in a temporary directory with the chosen runtimes and
profiles, so the author's folder is never changed. With a GitHub folder link, rig up,
rig bundle create, inspect and install take the same two options and stage a copy of the
whole checkout (importGitHubBundle() in packages/cli/src/lib/bundle-source.ts).
packages/daemon/src/domain/bundle-identity.ts defines the two identity values:
configurationId(): every member'spod.member=runtime, sorted and joined with,. Create computes it from the normalized spec and returns a 400 if the client sent a different one.packageDigest(): SHA-256 over the sortedintegrity.filesentries (coverage labelopenrig.package-digest/v1). It covers every packaged file's bytes; it does not coverbundle.yaml, the junk files the integrity walk skips, or file modes.
Create and inspect responses carry the source, configuration ID, package digest, archive hash and assembler version.
Archive checks
packages/daemon/src/domain/bundle-archive.ts owns pack() and unpack();
packages/daemon/src/domain/bundle-integrity.ts owns the per-file integrity map.
Packing requires a .rigbundle output path, sorts entries, uses portable tar metadata, a
fixed tar timestamp and gzip level 9, and writes a sibling SHA-256 file. Unpacking checks that
digest, rejects archive links and unsafe entry paths before extraction, requires
bundle.yaml and its integrity section, and verifies the extracted file inventory. Hashes
establish consistency with the supplied manifest and digest; they do not authenticate
whoever supplied both.
The inspect handler does not call unpack(), because it must report a broken bundle rather
than refuse it. It reports a missing or mismatched digest as digestValid: false, applies the
same unsafe-entry pre-scan (collectUnsafeArchiveEntries()) and extraction filter, and runs
verifyIntegrity() to report an integrityResult. A syntactically valid manifest alone is
not an inspected archive.
Inspect also returns a behaviour view, describeBundleBehaviour() in
packages/daemon/src/domain/bundle-behaviour.ts (schema openrig.bundle-behaviour/v1). It
reads the extracted archive only, through bundle-behaviour-inspect.ts (regular UTF-8 files,
at most 1 MiB each and 8 MiB in total, opened without following links). It lists the team,
each seat's permission posture, the files seats are told to read, what else runs, where
content is written, literal outside addresses, needs, and what can't be known before launch.
A schema-1 archive gets state: not_generated. Posture comes from the member's or rig's
permission_policy: builtin:yolo is full bypass and builtin:auto is auto, both by launch
flag; builtin:locked, builtin:standard and builtin:open are configuration; a policy file
is read from the archive or marked unresolved; no policy means the product default. A rig-level
non_interruptive boolean is reported on each full-bypass Claude Code or Codex seat as
nonInterruptiveDefault, and true drops the Claude bypass first-run warning. The CLI prints
the view before rig bundle install and rig up as diagnostics only; it never changes the
request or the exit code.
Source routing and bootstrap
packages/daemon/src/domain/up-command-router.ts classifies a source as
rig_spec, rig_bundle, rig_name or topology. It recognizes named rigs,
spec paths, .rigbundle, .rigtopology, and YAML with a top-level rigs list;
extensionless paths also have content detection.
packages/daemon/src/routes/up.ts then selects the execution path:
| Input | Consumer |
|---|---|
| Existing rig name | Existing-rig restore path; unarchived rigs of that name are preferred. |
| Single rig spec or bundle | BootstrapOrchestrator.bootstrap() in packages/daemon/src/domain/bootstrap-orchestrator.ts. |
| Topology manifest | MultiRigLauncher, with a single-rig bootstrap or remote-up leaf per entry. |
Topology plan mode is rejected. Placement belongs on the individual topology entries; a
top-level host flag is rejected. The selected topology parser/route also rejects nested
topologies, existing-rig-name entries and .rigbundle entries: topology entries are spec
paths only. This is narrower than the single-rig up surface.
Bootstrap distinguishes pod-aware and legacy specs, and inspects a bundle manifest to choose
PodBundleSourceResolver or LegacyBundleSourceResolver from
packages/daemon/src/domain/bundle-source-resolver.ts. Thus the router's
rig_bundle kind does not imply a legacy manifest.
For pod-aware specs, plan mode validates and runs preflight probes; apply delegates to
PodRigInstantiator. Plan mode still records a bootstrap run/result: it is not a pure
file read. Apply reports partial completion when nodes fail or require attention, preserving
the created rig identity rather than claiming every member launched. Apply's non-interruptive
choice is the request's, else the spec's top-level non_interruptive, else the
launch.non_interruptive setting.
Durable install target
Bundle apply requires an explicit target root at the HTTP boundary. The CLI supplies the
current directory as the default for rig up <file>.rigbundle and for a GitHub link through
rig up or rig bundle install. rig bundle install with a local .rigbundle sends no
default, so apply without --target gets a 400. For a pod bundle,
materializePodBundle() copies the verified extraction into that durable root before
instantiation, so local agent references and relative working directories survive removal
of the extraction directory. It checks destination conflicts before copying, preserves
identical files, and refuses differing files or incompatible destination types.
When an unarchived or running team already has the bundle's rig name (bundleInstallContext()
in packages/daemon/src/domain/bundle-install-context.ts), pod-bundle apply refuses before
writing if any of them is running. If the target is that team's own install folder (its
bundle.yaml names the offered schema-2 bundle, or my-bundle or github-bundle, the default
names before 0.6.7, and it is the folder recorded in rigs.install_root when any of those teams
recorded one; isExistingBundleTarget()), bootstrap first
confirms the old sessions are stopped; materialization then copies each conflicting path to a
bundle-backups/reinstall-* folder under the OpenRig home, with a RESTORE.json, before
replacing it, and leaves unrelated files in place. Any other target keeps the conflict refusal.
The instantiator archives the stopped earlier generation. Pod-bundle apply records the
target's real path as the new rig's install_root (migration 096).
Pod bundles containing service definitions are refused by bootstrap; their service path requires a stable spec directory. Direct spec bootstrap has separate service prelaunch handling. See agent-spec-and-startup.md for instantiation.
GitHub folder links and the bundle check
packages/cli/src/lib/bundle-source.ts lets rig up, rig bundle create, inspect and
install take a GitHub folder link (isGitHubBundleLink()). Only a credential-free
https://github.com/owner/repo[/tree/<ref>/<folder>] link is accepted. The ref is resolved to
the longest matching branch or tag, or taken as a full commit hash. The import:
- needs a healthy local daemon whose
selfHostIdmatches the local origin; - runs Git with credential helpers, hooks and non-HTTPS protocols disabled;
- shallow-fetches the exact commit into
bundle-imports/under the OpenRig home, requires arig.yamlin the folder, and refuses symlinks or agent and package references that resolve outside the checkout; - posts the folder to
/api/bundles/createwith the source recorded in provenance, refuses a response without a package digest or with a different source commit, and writessource.jsonandbuild.jsonin the import folder.
rig bundle check <folder> (checkBundleFolder() in packages/cli/src/lib/bundle-check.ts)
is an advisory, file-read-only check against openrig.bundle-standard/v1; it makes no daemon,
launch or provider call. Its rules are pod_aware_rig, readme_in_docs, referenced_files,
portable_agents, minimum_versions, configurations and credential_paths (a filename scan
bounded at 10,000 entries that skips .git, node_modules and symlinks). readme_coverage
and embedded_secrets are always reported as not checked, and host-resolved resources are
reported as checked at launch (host_resources). Any finding sets exit code 1.
Install checks and content routing
The install handler in packages/daemon/src/routes/bundles.ts obtains a source-path
lock, reads validated metadata, checks compatibility, and checks a declared rig name
against same-name rigs with running sessions before bootstrap; a stopped team of that name
doesn't block it. A conflict returns a 400 with status: "not_attempted", the installed team
and the offered bundle, and three choices: use the existing team, stop it and retry to replace
it, or cancel. The compatibility override skipVersionCheck and name-conflict override force
are explicit request fields; neither skips the metadata pass, which always validates the
manifest.
packages/daemon/src/domain/bundle-conflict-detector.ts implements the rig-name check.
It is not a complete agent, port or filesystem collision audit; a missing rig name supplies
no name comparison. Target-file conflicts are handled separately by materialization.
Skipping these prechecks does not skip archive validation in bootstrap.
Declared content is routed by routeBundleContents() in
packages/daemon/src/domain/bundle-content-routing.ts, which unpacks the bundle once and
routes each kind independently. When it runs depends on the bundle schema:
- Pod-aware (schema 2): bootstrap runs it as
PodRigInstantiator's prelaunch hook, after the rig record exists and before any seat launches, so every seat's first turn can see the content. - Legacy (schema 1): there is no hook, so the install route routes after bootstrap, and
only when the status is
completed.
| Manifest content | Destination |
|---|---|
skills |
The OpenRig packages cache, stripping the legacy packages/ prefix; this does not import a complete harness skill into the managed skill catalog. |
plugins |
The OpenRig plugins root, through local plugin references. |
workflow_specs |
workflows/ beneath the resolved workspaceSpecsRoot. If that setting is empty, this kind is recorded as a routing failure. |
context_packs |
The configured context.root, matching context-library discovery. |
agent_images |
The OpenRig agent-images root; declarations address image directories containing a manifest. |
project |
Registered in the workspace catalog (workspace.catalog_path) with the rig associated, and its files placed under workspace.projects_root/<id> (registerBundleProject() in packages/daemon/src/domain/workspace/project-registration.ts). A conflict changes nothing and is a routing failure. |
Context packs are never merged into an installed pack of the same name: an identical one is
reported as already_installed, a different one is kept unchanged as kept_existing. After
routing, the live context library rescans, and a routed pack it can't load becomes a routing
failure.
The per-kind implementations are the bundle-skills-router.ts, bundle-plugins-router.ts,
bundle-workflow-specs-router.ts, bundle-context-packs-router.ts and
bundle-agent-images-router.ts files under packages/daemon/src/domain/. Routing is best
effort in both cases: the hook always lets the launch continue, and a failure never rolls back
or fails the install. Failures appear in the result's routingFailures and warnings, in a
route_bundle_contents stage for the hook, and in the install audit record.
Install audit records are appended best effort to bundle-audit.jsonl under the OpenRig
home; the history endpoint reads that audit. Bootstrap run state and action journals are
separate, repository-backed records.
Legacy package install path
The legacy path remains executable, not just an archive parser.
packages/daemon/src/routes/packages.ts is mounted at /api/packages, and legacy
bootstrap calls PackageInstallService in
packages/daemon/src/domain/package-install-service.ts.
The source chain is package resolution/manifest validation, planning and conflict policy, then apply/verification with repository records:
resolvePackage()inpackages/daemon/src/domain/package-resolve-helper.ts, which reads and validates throughpackage-manifest.ts.package-resolver.tssupplies the types; itsPackageResolverclass has no production caller.packages/daemon/src/domain/install-planner.ts,conflict-detector.tsandinstall-policy.ts. The planner refuses any planned destination outside the install target.packages/daemon/src/domain/install-engine.ts,install-verifier.ts,install-repository.tsandpackage-repository.ts.
Legacy bootstrap also probes requirements and records staged outcomes before rig instantiation. A failed package installation prevents that path from importing the rig. This compatibility implementation does not imply that pod-aware bundles use the same package-install stages.
Related maps
- plugin-agent-image-context-pack.md: content library consumers and their state boundaries.
- daemon-core.md: bootstrap construction and HTTP wiring.
- agent-spec-and-startup.md: spec resolution, projection and launch.