Release v0.17.0 - xchain-node - #43
Merged
Merged
Conversation
The explorer's batch balance and coinpay-obligation routes share one limiter with its own knob; it rides the same by-name passthrough as the other eight explorer rate-limit variables so a deployment can size it from the host env.
…de is reset A regtest chain reset is a re-genesis, and the hub's cross_chain_matches, cross_chain_calls and capability_snapshots rows are scoped by network alone, so every fresh indexer re-mirrored the dead chain's finalized matches and refused them at every block (spec cross-settle-relic-matches.md, leg 3). `reset node`/`reset all` on regtest now purges them beside the price-fence delete, named in the confirmation, never fatal (prints the statements and the hub restart on failure). A Bitcoin reset clears all three tables for the network; a non-Bitcoin reset clears only matches and calls with a leg on that chain. capability_snapshots has no network column, so its unscoped delete runs only when the co-located hub is proven to be the regtest stack's.
…ugh with the other three, so an armed regtest venue no longer holds every block 60s behind a frozen match mirror
… rides the regtest pass-through, since each mirrored table's barrier holds blocks on its own
Every install points its coin indexers at the hub container on the same host, and that hub judges the on-chain PRICE batches they read. A non-validator install gave it neither the network the batch rules are keyed on nor a BTC indexer to read capability snapshots from, so it failed closed on every batch with insufficient signer stake and the price mirror never filled. HUB_NETWORK is now derived from the single network this deployment's stacks run on, or from the network the running install names when no stack is registered yet; a deployment spanning two networks stays unset and says so. BTC_INDEXER_API_URL composes the co-located bitcoin indexer for that network when one exists, and warns naming the consequence when none can be composed. Host env wins in both cases, and the composition is gated on the indexer actually existing so it cannot shadow a working configs-table push. Measured on a stock 0.16.2 install in an isolated docker-in-docker sandbox: before, the hub container carried no HUB_NETWORK at all and an empty BTC_INDEXER_API_URL; after, HUB_NETWORK=testnet and the bitcoin case composes.
…eanup, node first under all Four operator findings from one mainnet Pi install, each fixed where it lives: - XCHAIN_NODE_STOP_TIMEOUT_SECONDS overrides the 600 s flush budget a coin node gets before docker kills it, on update and as the container's own stop timeout. The stop now reads the container's exit code, prints how long the daemon took, and warns when it ran out of budget and was killed, because a killed mainnet bitcoind came back 17000 blocks lower and re-validated for four hours while the update said nothing. - ps marks a decoder or tracker whose coin node is not answering as NODE UNREACHABLE with the duration and a note saying it is not the IBD wait, from the node_last_ok_at and node_unreachable fields the services publish. - A bootstrap restore removes latest.tgz and its .sig after success or a node-behind refusal (the download is unconditional, so a kept copy buys nothing), keeps it after a failure and says so with the size, and the install summary carries what came back. A 151 GiB tracker archive had sat in a bootstrap volume for a week. - The coin node leads the all expansion, so install creates it before the services that poll it: a decoder was created four and a half hours before its node existed and logged ENOTFOUND the whole time.
…t activation positions It parses addChange() mainnet arguments, mainnet map entries and *MAINNET* constants for 9999999999 or 999999999 instead of substring-matching 1798761600, which is CROSS_CHAIN_ROYALTY's ARMED instant (ruled 2026-09-09), not a placeholder.
# Conflicts: # CHANGELOG.md
…rom where
getValidatorEnv() answers {} for three situations a hub deploy could not tell
apart: a genuine standalone node, a validator deliberately disabled, and a
validator whose files are intact but not under the configDir this process
resolved. VALIDATOR_DIR is path.join(configDir, 'validator') and configDir
defaults to <xchain-node>/config, so a config directory holding only the .local
sidecars makes isInitialized() false and silently drops P2P_VALIDATOR_ADDR,
SIGNING_PRIVKEY_HEX, HUB_CAPABILITY_CONFIG and the /validator mount.
The failure was invisible in every direction: no error, no warning, a healthy
container, and getanchorstatus reporting active:true from hub-local effector
flags while the pubkey was in no capability set at all and the anchor publisher
never ran. A reset only restarts containers so it never fired; an update
recreates them so it did.
validatorModeReport() separates the three, and the hub config now states the
mode and the directory it came from every time. A HALF-present state (one of
validator.json / signing.key) is called incomplete and warned about, because that
is never a standalone node, it is a broken install.
Deliberately a statement and a warning, never a throw. Some hubs are
legitimately standalone, and this repo's own config dir already carries a
sidecar-without-base for bitcoin-testnet, bitcoin-mainnet and dogecoin-testnet,
so a fatal guard would have wedged those deploys rather than informing them.
Falsified by deleting the incomplete branch: the half-present cases then report
standalone, which is the original defect, and two cases go red. Restored
byte-exact, sha256 equal either side. Driven against real directories rather than
only fixtures: this machine's config dir reports validator, an empty dir reports
standalone with both files missing, a half-populated one reports incomplete
naming the one missing file, and enabled:false reports disabled.
The three ConfigService ValidatorService stubs gain the new function. A stub that
omits it is not a standalone machine, it is an incomplete module, and leaving
production code to guard around that would have hidden the same class of gap
this change exists to expose.
…work A reset cleared the hub's price-ingest fence by chain, so resetting one network could drop the fence row another network was relying on. The obvious fix, scoping the delete by network, is not available and would be worse than the bug: the fence table is keyed on the source chain alone and has no network column, so the clause would fail the statement outright. The caller catches and warns, so the fence would then never clear at all, which is the silent price-rail outage this function exists to prevent. So the guard is where the information actually is. When the co-located hub declares a network and it is not the one being reset, the clear is refused and the statement to run by hand is printed. A hub that declares no network contradicts nothing and behaves exactly as before. This mirrors the guard the sibling capability-snapshot clear in this file already uses. Separating the fences properly needs a network column on the hub table and its accessors, which is a fleet migration and is tracked as its own item.
…repairs it When the hub container was restart-looping, the pre-check pushed configuration to it, failed after nine connection refusals and aborted every command, including the update that would have rebuilt it. The only way out was removing the container by hand. The pre-check now asks once whether the hub answers, with a single request and no retries or restart attempts, because only a request distinguishes a crash-looping container from a healthy one: a looping container is still registered, still has an id and still reports a status. When the hub is down and the command about to run would put it back, the configuration push is skipped with a warning and the command proceeds. When the hub is down and the command is anything else, it still aborts, and the message now names the command that repairs it and points at the container logs. A healthy hub keeps the previous behaviour byte for byte, including the existing error text. A command counts as repairing only when the verb replaces or restarts container state and the target actually resolves to the hub, using the same argument resolver the actions use. That includes the update that carries the shared services hub-first, the documented teardown that removes the hub, and the autoheal sweep whose whole job is restarting unhealthy containers. It excludes stop, everything that works through a hub it needs running, a repairing verb aimed at another service, and the bulk forms whose expansion never includes the hub. The hub update takes a flag that keeps the shared-container network attaches, which work on a container that is not serving, and drops only the push. The key sidecar load stays ahead of everything that talks to the hub, and a regression test pins that on the new unhealthy path, where an ordering mistake would hide.
- fix(validator): probe what a host resolves as its capability set against what the indexer answers - fix(node): forward LEVELDB_CACHE_BYTES and LEVELDB_WRITE_BUFFER_BYTES to the utxo-tracker container - fix(node): read the LevelDB passthrough variables by name so the doc-coverage gate can see them - fix(node): bitcoin testnet chain subdir is testnet4, dogecoin stays testnet3 - test(node): seal e2e-env call-time requires so proxyquire stubs are not bypassed Squashed from the local landing commits: 2407a6a9 4662d7fc ab19c0c4 00ca21e5 d671a9eb
The lines those commits added were pushed with their leading whitespace collapsed to one space by a message-scrubbing step; content is unchanged.
… docker kill on update, recreate, uninstall and stop Every CLI path that took a service container down used `docker kill` (update, recreate, uninstall) or a bare `docker stop` with docker's ten seconds (stop), so no service ever received a bounded drain and the indexer's SIGTERM handler was dead code. An operator's `docker stop -t 180` on a BTC mainnet stack read encoder 143, indexer 0, tracker 1, decoder 137. StopBudgetService gives each service a budget (decoder and utxo-tracker 120 s, others 30 s, the coin node its own 600 s resolver), overridable through XCHAIN_NODE_MODULE_STOP_TIMEOUT_SECONDS_<SERVICE>, stamps it on the container as --stop-timeout so a plain docker stop honours it, and prints a clean stop with its duration or a warning naming the override when the process was killed.
R-LIF-003 and R-LIF-008 stubbed stopContainer only, so once stopModules stops with a budget the call fell through proxyquire to the real docker on the CI venue and nothing was recorded.
Bumps the package and lockfile to 0.17.0 and dates the 0.17.0 changelog entry. The manifest, README badge and release notes are written separately from the tagged sibling SHAs.
jdogresorg
force-pushed
the
release/v0.17.0
branch
from
September 11, 2026 01:01
21c295d to
5282d3d
Compare
Pins the twelve component repos at their signed v0.17.0 master merge commits, resolved against origin by the platform manifest generator rather than by hand. Also sets the README version badge to 0.17.0, which the stats refresher cannot do before the carrier's own tag exists.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps the package and lockfile to 0.17.0 and dates the 0.17.0 changelog entry. The release manifest, README badge and platform release notes are written on this branch once the sibling tags exist, so this PR is the last of the train to merge. Platform notes ride this repo's Release.