| name | dsr |
| description | Doodlestein Self-Releaser - fallback release infrastructure for when GitHub Actions is throttled. Local builds, cross-platform releases, supply chain security. Use when: GH Actions slow, local release, build hosts, dsr command. |
dsr - Doodlestein Self-Releaser
Fallback release infrastructure for when GitHub Actions is throttled (>10 min queue time).
When to Use
Use dsr when:
- GitHub Actions queue time exceeds 10 minutes
- You need to build/release a tool locally
- CI is down or unreliable
- You want to test builds across platforms before pushing
Commands Reference
Core Pipeline
dsr check <repo>
dsr check --all
dsr build <tool>
dsr release <tool> <version>
dsr fallback <tool> <version>
System Management
dsr doctor
dsr doctor --fix
dsr health all
dsr health check mmini
dsr status
dsr prune
Configuration
dsr config init
dsr config show
dsr config get <key>
dsr config set <key> <val>
dsr repos list
dsr repos add <repo>
dsr repos info <tool>
dsr repos validate
Supply Chain Security
dsr signing init
dsr signing sign <file>
dsr signing verify <file>
dsr sbom <project>
dsr slsa generate <artifact>
dsr slsa verify <artifact>
dsr quality <tool>
Testing & Verification
dsr canary run <tool>
dsr canary run --all
dsr verify upgrade <tool>
dsr release verify <tool> <v>
Build Hosts
Hosts are whatever you define in hosts.yaml; the ids below are the
conventional ones used throughout these docs.
| Host | Platform | Connection | Purpose |
|---|
| trj | Linux x64 | local | Primary build host, act runner |
| ts1 | Linux x64 | SSH | Secondary Linux builder |
| mmini | macOS arm64 | SSH | Native macOS builds |
| wlap | Windows x64 | SSH | Native Windows builds |
More than one host may share a platform — a second windows/amd64 or
linux/amd64 entry is picked up automatically by the selector below, giving
overflow capacity and failover when one host is unhealthy or asleep.
hosts.yaml is per-machine. Each machine that runs dsr reads its own
~/.config/dsr/hosts.yaml. If you drive releases from more than one
orchestrator, a host added on one is invisible to the other — edit both.
Where builds are staged. Strict release snapshots and isolated Rust builds
go under /var/tmp (Linux) or /private/tmp (macOS), never /tmp: on many
Linux boxes /tmp is a RAM-backed tmpfs, and a staged workspace plus its
Cargo target can wedge the host. A host can override the root with
build_root: /path/on/disk in hosts.yaml; DSR_STRICT_BUILD_ROOT beats it
for strict snapshots. dsr refuses to stage onto tmpfs/ramfs in either mode.
Two config files, one contract. repos.d/<tool>.yaml is the build
authority (build/release read only it, and load it by its tool_name-matching
filename); repos.yaml is the registry (repos list/info/check, quality
gates). Any key present in both must be identical — dsr repos validate
fails on divergence and flags repos.d files the registry does not know about.
Keep both files updated together.
Portable Linux Rust binaries by default. Ordinary (non-strict) builds for
*-linux-gnu targets route cargo build through
cargo zigbuild --target <triple>.2.28 via a staged cargo shim, and the
collected binary's glibc symbol versions are asserted against the floor, so
artifacts do not inherit the build host's glibc. Needs cargo-zigbuild >=
0.23.0 and zig on the build host. Tune with linux_glibc_floor: "X.Y" or
disable with linux_glibc_floor: native in repos.d/<tool>.yaml; platforms
with an operator cross toolchain or a zigbuild/xwin/cross build_cmd are left
alone. Rust builds also always get an explicit CARGO_BUILD_TARGET (derived
from target_triples or the platform), collected artifacts are validated
against the requested platform's executable format, and build commands can
branch on DSR_TARGET_OS/ARCH/PLATFORM/TRIPLE. Windows hosts require a
drive-qualified host_paths.<host> (C:/... or /c/...).
Host selection
act_get_native_host resolves a platform to a host in this order:
- Per-repo override:
cross_compile."<platform>".host in repos.d/<tool>.yaml
- Health/capacity selector — considers every host whose
platform OS
matches, drops those that are disabled or failing health checks (5-min cache),
scores the rest by free build slots, and prefers the host named in
platform_mapping
- Static
platform_mapping["<platform>"]
- Compiled-in default
So a second same-platform host absorbs overflow and covers the first being
asleep, while platform_mapping still decides who goes first. Set
DSR_DISABLE_HOST_SELECTOR=1 to bypass step 2 and use the static mapping only.
The first call after a cold health cache costs ~10-15s while hosts are probed;
subsequent calls are instant for 5 minutes.
Common Workflows
Release When GH Actions is Throttled
dsr check --all
dsr build ntm --version 1.5.2
dsr release ntm 1.5.2
dsr fallback ntm 1.5.2
Health Check Before Building
dsr doctor
dsr health all
dsr health check mmini
Configure a New Tool
dsr repos add dicklesworthstone/my-tool --local-path /data/projects/my-tool
dsr repos validate
dsr build my-tool --dry-run
Clean Up Old Artifacts
dsr prune --dry-run
dsr prune --age 30d
dsr prune --tool ntm
Exit Codes
| Code | Name | Meaning |
|---|
| 0 | SUCCESS | Operation completed |
| 1 | PARTIAL_FAILURE | Some targets/repos failed |
| 2 | CONFLICT | Blocked by lock |
| 3 | DEPENDENCY_ERROR | Missing gh auth, docker, ssh |
| 4 | INVALID_ARGS | Bad options or config |
| 5 | INTERRUPTED | User abort or timeout |
| 6 | BUILD_FAILED | Compilation error |
| 7 | RELEASE_FAILED | Upload/signing failed |
| 8 | NETWORK_ERROR | Connectivity issue |
JSON Output
All commands support --json for machine-readable output:
dsr check ntm --json | jq '.status'
dsr health all --json | jq '.hosts[] | select(.status == "unhealthy")'
dsr status --json | jq '.last_run'
Troubleshooting
SSH Connection Fails
tailscale status
ssh -o ConnectTimeout=5 mmini "echo ok"
dsr health clear-cache
dsr health check mmini
Builds land on the wrong host (or an unexpected one)
The selector can legitimately pick a host other than the one in
platform_mapping — that is the failover working. To see why:
dsr health all --json | jq '.hosts[] | {hostname, status, healthy}'
DSR_DISABLE_HOST_SELECTOR=1 dsr build <tool>
A host is dropped from consideration when it is enabled: false, unhealthy
(disk >95% used is an error; >90% is only a warning), or has no free slots.
hosts.yaml is per-machine — confirm you edited the one belonging to the
machine you are running dsr on.
Build Fails
dsr doctor --fix
dsr health check trj --json | jq '.toolchains'
dsr build ntm --verbose
Release Upload Fails
gh auth status
gh release view v1.5.2 --repo Dicklesworthstone/ntm
dsr release ntm 1.5.2 --resume
Docker/Act Issues
docker info
act --version
colima status
Configuration Files
~/.config/dsr/
├── config.yaml # Main configuration
└── repos.d/
└── *.yaml # Per-repo build configs
~/.local/state/dsr/
├── logs/ # Run logs
├── artifacts/ # Build artifacts
└── manifests/ # Build manifests
~/.cache/dsr/
├── act/ # Act Docker layer cache
└── builds/ # Cached build artifacts
Integration with Beads
When working on dsr-related beads:
br ready | grep dsr
br update bd-1jt.x.y --status in_progress