Development
Run scripts/check.sh before every commit. It is the offline gate: formatting, vet, module and
vendor consistency, the dependency allowlist, build, tests and the capability baseline (scripts/capslock.sh),
all without network access. Run scripts/tools.sh once first: it installs the pinned capslock.
(The vendor-consistency step needs the module cache; set GOPROXY_VENDOR=https://proxy.golang.org on a
cold machine.)
Supported platforms are Linux and macOS (the key-file loader uses O_NONBLOCK; its tests use Mkfifo).
The other scripts:
| Script | What it does | Network |
|---|---|---|
scripts/tools.sh |
installs the pinned govulncheck and capslock into .tools/bin |
yes |
scripts/vulncheck.sh |
runs govulncheck ./...; fails on any finding |
yes (vulnerability database) |
scripts/capslock.sh [update] |
fails if a capability was added versus docs/capslock.json; update rewrites the baseline (offline once tools.sh has installed capslock); check.sh runs it |
no |
scripts/fuzz.sh [seconds] |
runs every Fuzz* target in turn (default 30 s each) |
no |
scripts/image-check.sh <image> <allowed file>... |
lists every entry in every layer of a built release image and fails on any file not named | no |
scripts/image-run-test.sh <satbooth image> <relay image> <tools image> |
runs each release image as it ships (read-only root, no capabilities, user 65532) with a missing configuration (the tools image: /satbooth-setup with no arguments), and checks the refusal, the user and the environment |
no |
scripts/release-* |
the release pipeline: release-build.sh builds the three images reproducibly, release-pins.sh reads and rewrites their pins in the bundles, release-push.sh, release-notes.sh and release-smoke.sh publish, describe and check a release (docs/releasing.md); check.sh runs release-pins_test.sh, release-pins_prop_test.sh (seeded), release-notes_test.sh, release-push_test.sh (a throwaway registry in Docker) and setup-image_test.sh |
the build and the push: yes |
scripts/volume-init-test.sh |
runs each bundle’s volume-init twice on directories laid out like an operator’s and checks the owners and modes (Docker; on Linux, where a bind mount keeps the host’s owner) |
to pull the pinned images |
CI (.github/workflows/check.yml) runs the first four; .github/workflows/image.yml builds all three release images
(deploy/README.md, “Build and check”) and runs scripts/image-check.sh, scripts/image-run-test.sh and
scripts/volume-init-test.sh. scripts/check.sh also checks both Compose bundles with docker compose config when
Compose is installed (CI has it). check.sh also runs the release tests (release-pins_test.sh,
release-pins_prop_test.sh, release-notes_test.sh and release-push_test.sh) and setup-image_test.sh, and a pull
request that touches the build inputs gets a release run, whose pin comparison is advisory unless the pull request
changes the pins.
The rate providers. The offline tests parse recorded answers of Kraken (BTC and XMR), Coinbase, Yadio and
Bitfinex in rates/testdata/ (Kraken’s trimmed to its seven BTC pairs and its two XMR pairs, Coinbase’s to those seven
codes plus its longest fraction when that is longer, Yadio’s and Bitfinex’s whole, with their response headers in
*.meta.json; custom.json is written by hand in SatBooth’s format),
and TestFixturesAgree checks that the three recordings agree with each other, within 2%, in each of Kraken’s seven
currencies.
The service’s tests reach a fake rate server (rates/ratestest) through service.WithRateTransport, never the
network. Two tests, with the build tag live, call the real providers and are run by hand only:
go test -tags live -run '^TestLiveSmoke$' -v ./rates/runs the live smoke test,TestLiveSmoke: one request to each built-in provider, then the median, the spread and whether the default rules would price an order, all logged. It fails on a changed answer format or a 4xx other than 429 (a request the provider no longer accepts), not on a provider that is down or limiting. It does not rewrite anything.SATBOOTH_RECORD_FIXTURES=1 go test -tags live -run '^TestLiveRecord$' -v ./rates/re-records the fixtures: one request per provider, the bodies (trimmed as above;TestFixturesAreTrimmedchecks the committed ones are) and headers written torates/testdata/, then the checks the vendors’ docs do not cover (Kraken’s key for each of its seven currencies, Coinbase’s longest fraction, Yadio’s timestamp lag, the USD spread). Without the variable it skips. Review the new files before you commit them (public market data, a few hundred bytes to a few KB each); the parser tests andTestFixturesAgreethen run against them.
Qualification against real hubs is separate and dev-only: scripts/qualify.sh [--keep] builds a regtest
environment from dev/regtest/compose.yml (Bitcoin Core, two Alby Hub containers, a SatBooth relay beside each
hub, monerod, two monero-wallet-rpc (wallet-customer, which holds the test’s customer and merchant wallets, and
wallet-satbooth, SatBooth’s view-only wallet behind a Digest login) and a Go test runner, all on an internal network
with no internet) and runs the tests in qualify/, which carry the build tag qualify, so scripts/check.sh never
compiles them. It needs Docker with Compose and, on first use, the network to pull the pinned images, build bitcoind
from the Bitcoin Core release tarball and the Monero image from the Monero v0.18.5.4 release tarball (dev/regtest/monero/,
its SHA-256 pinned from the PGP-signed hashes.txt). The Lightning phases wait for an invoice to expire in real time, so they take about 20 minutes (1,230 s in plan 5’s last run, on Docker Desktop with the images already
built and cached); the on-chain phases follow, against the same bitcoind through SatBooth’s own whitelisted RPC user (dev/regtest/bitcoind/bitcoin.conf), then the Monero phases, against wallet-satbooth through SatBooth’s own client, then the refund phases (about 12 minutes, with the payout connection and the merchant’s hub as the claimant’s wallet), then the release images’ phases (a satbooth container of the release image driven over HTTP only; the relays always run from the release image) and the public host’s bundle (deploy/public/compose.yml with its compose.build.yml); the whole suite took 84 minutes in plan 10’s run; a first run also pulls and builds the images and takes longer (not measured). qualify/REPORT.md lists what the phases measure and which ran on the image. --keep leaves the
environment up. --only deploy runs the setup and the public bundle’s phases alone, --only image the setup,
TestOnchainSetup, TestMoneroSetup and the release image’s phases, --only refund the setup and the refund phases
(about 15 minutes, most of it the 11-minute void wait), --only hub the setup and TestQualify with the check on the
hub’s unknown-app lines (about 1.5 minutes), and --only onchain the setup and every on-chain phase with the bitcoind
kills between them (about 15 minutes), for a targeted check; none is the qualification. QUALIFY_CRASH_REPEATS=<n>
repeats the hub stop and kill phases; each round spends part of the payout connection’s
50,000 sat test budget, which the refund phases share (they pay about 5,100 sat), so about 12 rounds in all is the limit (an estimate from the budget arithmetic). .github/workflows/qualify.yml runs it by hand and weekly.
Documentation from SatBooth v0.1.0-rc.1, Apache-2.0