Skip to content

Release v0.15.0 - xchain-documentation - #26

Open
jdogresorg wants to merge 53 commits into
masterfrom
release/v0.15.0
Open

Release v0.15.0 - xchain-documentation#26
jdogresorg wants to merge 53 commits into
masterfrom
release/v0.15.0

Conversation

@jdogresorg

Copy link
Copy Markdown
Contributor

Cuts xchain-documentation for the v0.15.0 release train. 50 commits from master.

Added

  • The ATTEST response-mirror activation height, its two hub overrides, the attestation batch publisher settings, the round cadence knobs and the indexer's three hub-mirror grace windows are documented, with the attestation_responses mirror table.
  • A two-chain regtest venue can opt in to roll-call activation, and the roll-call, frozen-tip and config-oracle variables are documented for the hub and indexer.
  • Flag-day gates parked on the unarmed testnet sentinel are reported.
  • The v0.14.0 release train is recorded on the releases page.
  • ROLLCALL, a validator liveness action published on Dogecoin, is documented: wire format, EQUIV canonical, the rules each chain judges, the accept window and its cut, and the eviction rule.
  • Eight frozen ROLLCALL consensus constants are declared in protocol/constants.js, with mainnet shipping inert at null.
  • XROLLCALL joins ENGINE_TAGS with header vectors; it is namespacing only and deliberately not a SLASH family.
  • rollcall_canonical.json freezes the canonical and wire bytes with real Ed25519 signatures, including negative cases for a wrong ledger hash, network or epoch.
  • The indexer configuration page states that the existing DOGE wiring becomes required a second time from ROLLCALL_ACTIVATION, and that a BTC indexer without it defers every block.
  • ATTEST's hub-mirror response path is documented: the mirror-era signed canonical, deterministic callback timing with no broadcast transaction, the v5/v6 batch format, and the escrow-split change once broadcasting stops.

Changed

  • The white paper is bumped to version 1.4, dated 2026-08-31, for the announcement.
  • White paper §6 reconciles its action counts with the corpus: ROLLCALL joins the non-user-submittable list and thirty-six of the 37 actions are wire-decoded.
  • The white paper describes ROLLCALL in §6.6 and replaces §11.1's governance-adjudicated non-participation claim with roll-call eviction.
  • The white paper's stale tenses are corrected: the BATCH mainnet activation is in the past, the source repositories are public, and the per-version Node failure modes defer to the docs.
  • The four texts promising a non-participation slash are corrected: an absent validator is counted in N and nothing on-chain penalises it; where ROLLCALL is active it is evicted by deactivation, never burned.
  • The hub decentralization table separates on-chain slashing, which is equivocation only, from hub-local suspension, which leaves stake untouched.
  • ACTION counts move to 37 named and 36 wire-decoded across the docs; user-submittable stays 31.
  • The ANCHOR version set restarts at v0 (checkpoint bundle), v1 (archive head, publisher attestation always appended) and v2 (continuation), gated by a new per-network ANCHOR_ACTIVATION height below which any version is invalid; the checkpoint bundle carries one anchor per network per cycle with every chain as its own section, and the pre-restart version numbers are retired.
  • The anchor publish reward is now one anchor_bundle row per bundle instead of one anchor_<chain> row per chain, across the COLLECT, hub and indexer reference pages.
  • getanchorstatus documents anchorsPublished as a count of bundles, and adds the sectionsAnchored and bundlesOversize fields.
  • The anchor_actions table is documented as keyed on (action_index, section_index), one row per bundle section.
  • The frozen ANCHOR canonical vectors carry the restarted v0 case, and the pre-restart version cases are dropped.
  • The light-client guide documents parseAnchorV0 and anchorBundleSection in place of parseAnchorV3, and explains that a bundle omitting a chain is normal.
  • The hub configuration reference documents the anchor startup flush, the confirmed-inputs rule and the two confirmation-watchdog settings.
  • The explorer REST endpoint counts in the component map are re-derived after the token holders route was added to both namespaces.
  • The ATTEST v3 rules document the relay-identity exactly-once gate the reference indexer enforces.
  • The utxo-tracker configuration reference documents the derived bulk-sync RAM budget alongside the other memory-budget figures.
  • The decoder configuration reference documents the mempool response cache duration.
  • The sync configuration reference documents the replication-connection setting for multi-source replicas, and names the MariaDB privilege that replica-status reads actually require.
  • The open-mint terms now carry a 1,000 XCHAIN per-address cap in the white paper, overview and genesis runbook, enforced by the distribution-figures test.
  • The regtest guide now documents creating XCHAIN and minting gas on a fresh chain, replacing the incorrect claim that a pre-funded GAS address exists, and explains seeding oracle prices for native-coin fees.
  • Intro pages, FAQ, use cases, concepts and component pages now lead with the chain-agnostic claim and time-stamp Bitcoin, Litecoin, and Dogecoin as the live-today roster, enforced by a new chain-positioning test.
  • White paper §13.3 now publishes the genesis figures (100,000,000 cap, the seven-leg distribution, snapshot pins and open-mint terms) instead of deferring them, carrying the pre-launch "not yet final" qualifier.
  • Overview, GAS, COLLECT and the genesis runbook no longer claim supply is created only by public mints, which held only while the distribution was unpublished.

Added

  • A test that keeps the genesis allocation table arithmetically closed and in agreement with the genesis runbook.
  • operations/release-process.md: how a release train is cut, signed and published, the hotfix path, and the classification rule that decides whether a fix needs an activation point.
  • The operations index now lists Release Signing, which existed as a page but was reachable only by direct link.

Fixed

  • The smart-contract development guide counted 13 shipped xchain-contracts templates and omitted counterpartyBridge; it now counts 14 and lists it, matching what npx xchain-contracts list reports.

Removed

  • The operator-dashboard pages and every listing of it (components index, README table, platform map, test counts); it is internal operator tooling, not part of the public platform.

jdogresorg and others added 30 commits September 3, 2026 06:43
The tree listing claimed a file tally taken from a working-tree scan, which runs ahead of the committed count whenever a lane holds unlanded suites. Register the committed number and drop the two count exceptions the ACTION set now covers on its own.
…rom the tree

The published figures counted two slash suites that are not in the repo, so the e2e-test repo's own recount test refused them. The tree carries 30 ACTION test suites across 76 files with 48 helper modules; SLASH leaves the enumerated list with them.
…ents

Covers the encoder/node maintenance-window sentinel pair, three hub oracle
consensus/publishing knobs plus two operator drill-script RPC targets, and
the four HUB_SYNC_* mirror-drain knobs on both the indexer and its explorer
twin, documented from each component's own copy of hub_db_sync.js. Notes
that the explorer never calls the resync path HUB_SYNC_BARRIER_HOLD_CEILING_S
gates, unlike the indexer's block loop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133x7wrwS7q1num9bQ2PT3R
…blish ledger override

Sixteenth variable added to this pass after the coordinator found it landed
on origin (visible to the coverage checker's origin-pointed ref run but not
the local checkout this pass started from).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133x7wrwS7q1num9bQ2PT3R
The teardown ledger adds one module under test/helpers, so the published figure moves from 48 to 49.
…age adds

The capability slash suite adds one file under test/actions, so the published figure moves from 76 to 77.
…re on the tree

The capability and contract slash suites bring SLASH back into the counted set, so the figure returns to 31 and the two count exceptions are no longer needed.
…le variables

The hub's RollcallRound and StateCheckpointEngine and the indexer's hub
client landed reads of nine variables on origin with no configuration rows,
and the cross-repo coverage gate has been refusing every platform push since.
Each gets a row (name, default, meaning) on its component's configuration
page, plus the three ROLLCALL_*_BLOCKS tunables that the scanner cannot see
because they are read by computed name.

The computed-read baselines move to match the committed trees: hub 31 to 33
(the roll-call tunable resolver and the attest response forward override),
node 17 to 20 (ConfigService passes the roll-call rail env through by name).
… through

Four host-environment variables the node hands to the explorer: the overall
request budget, the fee-quote and pre-flight budgets, and the tip-age freshness
gate. Each note says what makes the public default wrong on a private venue -
one tunnelled host is a single IP to the limiter, and a regtest chain that
nobody is mining crosses the six-hour staleness gate while its lag is zero.
An attestation costs two on-chain transactions today. The request rides
inside the EXECUTE the user already paid for, but the response is a whole
ATTEST v1 transaction that a validator broadcasts and pays a fee for, and
the contract callback fires only when it mines. A contract executed a
thousand times is a thousand validator-paid transactions, each waiting on
Bitcoin block time.

This is the height at which that stops. At or above it the responsible
set's finalized artifact reaches indexers through the hub mirror the way
PRICE rounds already do, and the full history still lands on chain in
periodic batches so a node replaying the chain re-derives every callback.

Evaluated on the request's own block, like the other attestation gates, so
the rule for a request is fixed the moment it is admitted. Mainnet and
testnet both ship unratified.
Both are regtest-only test seams introduced with the response mirror, and
both were invisible to the coverage gate: the hub's was read through a
constant holding its own name, and the indexer's is resolved by a shared
helper that indexes process.env by argument. Operator-settable variables
with no row in any configuration table.

The hub knob shifts when a mirrored response becomes applicable, and it
exists because regtest blocks are stamped at roughly now, so without it no
response could bind for two real minutes. The indexer knob is the barrier's
grace, which only has to cover stream lag because the real forward margin
travels inside the signed row. Both refuse to take effect off regtest,
because two nodes resolving either differently settle blocks differently.
Two schema tables landed locally with no row naming them, on both sides of
the mirror. The hub authors the table when a round reaches quorum; the
indexer carries a local copy that hub_db_sync populates.

The indexer's row says the two things a reader needs that the hub's does
not: that the mirror is transport and never authority, since the applier
re-verifies every row's signatures against the responsible set it resolves
from its own local request row, and that the applied state lives in attests
rather than here.
…ivation

Regtest stays inert by default, because arming a network commits every BTC indexer on it to a wired DOGE peer and a single-coin venue would defer at its first epoch close. A venue that runs both chains sets XC_ROLLCALL_REGTEST_ACTIVATION on every hub and indexer to arm at height 0, or at a height it names.
A consensus change registered after the public testnet launch cannot be genesis-active there without re-deciding history outside nodes have committed, so it parks on the sentinel until an operator names an instant. The page now says which gates those are, rather than letting the genesis-active line stand for every gate.
Both actions charge a base plus a per-item or per-recipient amount from the unified fee gate, in place of the flat charge per database hit. The base is what keeps the smallest sweep or callback able to buy a native-coin fee output above the chain's dust threshold.
…ponse

Names the database the quoted oracle prices were read from, so a tool that seeds prices writes to the one the node reads instead of inferring it from its own environment.
A consensus train that moves the node, hub and indexer, and points the install example at it. There is no v0.13.0: the number was skipped deliberately.
The tree carries 79 action test files and 51 helper modules.
The explorer serves 174 api patterns and 117 page routes, and the end-to-end
tree carries 79 action test files.
Above the response-mirror activation height a response reaches every
indexer through the hub mirror and fires its callback at the first block
whose protocol time reaches the signed effective time, with no validator
transaction; the batch head and continuation formats that put the window's
responses on chain, the mirror-era signing canonical, the invalid status
for an on-chain v1 above the height, and the escrow split without a
broadcast reimbursement are all written down beside the existing lifecycle.
…mirror venue has one

The tree carries 52 helper modules; both component pages published 51.
The batch publisher and its window seam read three variables that no
configuration table described, so the coverage gate refused every push
across the tree.

The kill switch and the buffer path join the attestation publishing
table. The window override goes beside the forward-margin override it
shares a module and a rule set with, including the one way they differ:
a batch window of zero seconds is a division by zero in the alignment
arithmetic, so regtest demands a positive integer rather than merely a
whole number.
… node read baseline

xchain-node forwards the price, oracle and attestation-response grace
windows to the indexer by name in one loop, so each is a computed read the
coverage gate cannot see. All three now carry rows, and the baseline moves
to match the committed tree.
Both reached the p2p config only recently, so an operator setting them saw
no effect and neither carried a row.
…en window

An operator retuning it cannot see from the name that both engines read the
one value, or that raising it alone re-opens a pending request and pays for
a duplicate provider fetch.
It is the same at-most-once guard the single-response marker beside it
provides, one level up, covering a whole window rather than one response.
The row records why it lives in the database and not in the publisher's
buffer file: the batch spends real coin, and the buffer sits on the disk
whose exhaustion is what makes a rewrite after the broadcast fail.

The detail carries the four states, because only one of them is safe to
act on automatically. A window left at intent is quarantined for a person,
since the transaction may be waiting in a mempool this hub cannot see.
The two are different units on purpose, and an operator reading only the
hour cannot see that tying them would move a chain-only node's coverage
proof whenever price staleness is retuned.
The six wallet pages described a passphrase that was never stored and had
to be typed at every unlock. It is now captured once at create or import
and stored encrypted under the wallet password, so the pages say that, and
say plainly what it does and does not protect: it covers a recovery phrase
that leaks on its own, it does not cover someone holding the device and the
password, and it is not a decoy mechanism. Import now warns that a phrase
carries no record of whether a passphrase was used, so importing without
one opens a different, empty wallet with nothing to signal it.
One wave of the review round on the xchain-platform board.
Every change was re-derived from the code rather than applied from the
finding recommended option, and each carries a control that reproduces
the original failure.

Review findings: 6393 6394 6397 6425 6426 6442 6447 6458 6459 6460 6461 6500 6502 6532 6558 6559 6560 6561 c234140a0bd9
…env passthroughs, and the origin identity change in the wallet's data disclosure

The explorer's app-wide, action-proof and checkpoint-verify defaults are
now 1080, 90 and 90 per minute; the node pages document the encoder
trust-proxy and rate-limit passthroughs and the explorer's five per-route
caps. The data disclosure states that a real-client-IP module is loaded
for the rate limiters only and that the access logs still record the edge
address, so no visitor IP is written to disk.
…r a count

The node page recommended a setup by hardware SKU, two pages referred to
a specific hub role, and the Apple privacy labels counted the wallet's
first-party API hosts. Each now states the requirement or the guarantee
instead.
The canonical config reference documented CORS_ORIGIN as a single origin rather
than the allowlist parseCorsOrigin implements, and betting.md omitted that
controller-bound tokens are rejected as a market's wager token.

The published helper-module count finding (#6813) was REJECTED: the monitor's
own replacement numbers were wrong in the other direction, and applying them
would have reddened xchain-e2e-test's ci chain for every lane running it with
the docs sibling present.

Review round 7 findings #6835, #6837, #6853, and the #6813 reject. Also carries
review round 6's documentation work.
Review-round fixes, each checked against the handler that implements it.

Max supply was described as a lifetime issuance ceiling when the handlers
compare against OUTSTANDING supply, so a burn reopens headroom; LOCK_MINT was
described as disabling issuer supply creation; BATCH issuance limits were still
taught in their pre-activation form three weeks after the mainnet gate armed;
and SLEEP was described as pausing a TICK the address ISSUED when the handler
gates on the OWNER, which the glossary itself defines as sellable and separate
from issuance.

ORACLE_BATCH_SIGN_TIMEOUT_MS is one name over two rails with two defaults: the
price rail reads the validator config table and defaults to 60000, the
attestation batch rail reads the environment and defaults to 15000. Both rows
now say so and name each other. XCHAIN_COINBASE_MATURITY is per chain rather
than a universal 100: DOGE is 240 on mainnet and testnet, 60 on regtest.

Suite: 441 passing. The 2 failures are a pre-existing endpoint-count drift gate;
the explorer route count is identical before and after this work.
… read

The fleet-wide coverage gate reads every process.env access in each service and
requires a row on that component's own configuration page. Seven reads had none:

  explorer, indexer  HUB_DB_SYNC_HTTP_DEADLINE   (vendored in both, so both pages)
  hub                ANCHOR_RATELIMIT_MAX_WAIT_MS, ANCHOR_RATELIMIT_MAX_WAITS
  hub                LLM_SPEND_LOG_FALLBACK_PATH
  indexer            HUB_CALL_DEADLINE_MS
  sdk                ENCODER_API_KEY

Each row states the meaning and asserts the default the code actually applies,
so a later drift between the two is a gate failure rather than a silent lie. The
encoder key is marked a credential, with the note that an unpinned encoder URL
lets a hub overlay repoint the client and send the key to the host it named.

Rows only. No existing row, heading or table shape changed.
The row described what the setting does and that it is regtest-only, but not the
one thing an operator needs before they need it: on a chain whose blocks are
stamped at about wall clock, the default cannot be satisfied at all. The barrier
waits for the mirror watermark to reach the block's timestamp plus the grace,
and where the watermark also tracks wall clock a freshly mined block can never
be two minutes behind it. Every affected block then waits out the full timeout
and carries on regardless, so the venue does not fail, it crawls.

The numbers are in the row because they are the argument: 367 blocks in six
hours at the default against two thousand in under two once lowered, with a
hundred and sixty deferrals waiting on a condition that could not arrive. That
cost a release matrix a full cycle and read as a wedged run rather than a
misconfiguration, which is exactly the confusion a line of documentation
prevents.

Off regtest nothing changes: the value is a consensus input there and the frozen
constant wins whatever the environment says.
MAX_DIRECT_TIP_AGE_S bounds how long the hub keeps trusting a direct
getlatestblock height that has not advanced past the pushed tip it just
rejected. It is separate from MAX_TIP_AGE_S on purpose: that gate falls through
to one more HTTP call, while this one is terminal and reports no BTC tip at all,
so its bound is sized for an ordinary long block gap on a healthy chain.

A height that beats the pushed tip is accepted whatever the tip's age.
The encoder now refuses an envelope cancel whose commit outpoint a foreign
reservation already holds, and returns it as an operational error naming the
outpoint. Documented alongside the other reservation errors so a client can map
the code rather than discover it.
…eclared here

The decoder vendors this constant byte-equal, so the canonical declaration has to
exist for that copy to be verified against anything. Keyed on block time with >=
semantics and consensus-affecting.

Mainnet is null, which fails closed and awaits a ratified per-network instant.
Testnet and regtest run from genesis, matching the sibling expiry-realign gate
under the pre-launch rule that every feature is active on testnet: the gate closes
a defect that spends a payer's native coin and returns nothing, so a public testnet
reaches it, and testnet decoder state is rebuilt from the chain before launch.
The e2e action suites gained a helper that decides whether a case depending on
the legacy on-chain ATTEST response path can run at all, so the published counts
move from 52 to 53. Both figures are asserted against the tree by the action
suite-count guard, which is what caught the drift.
The indexer resolves XCHAIN_COINPAY_EXPIRATION_S through one function so the
regtest-only rule for a consensus input (ignore with a warning off regtest,
positive-integer check on it) lives in one place. That read is computed, so
the env-var coverage scanner cannot see the name and the computed-read
ratchet held the indexer at its old count. Add the row the variable needs,
in the barriers table beside the two grace windows that share its mechanism,
and raise the indexer baseline to match the committed tree.
@jdogresorg jdogresorg changed the title Release v0.15.0 Release v0.15.0 - xchain-documentation Sep 7, 2026
Operator ruling 2026-09-07: the point of this train is to exercise the response
mirror and roll call on testnet, and a train that ships them dark there is not
worth cutting. The mirror is armed at block 151324, the chain tip when the
ruling was made, so it is active the moment a node updates rather than waiting
on a future height. Roll call needed no change: it was already armed at 151200,
which the chain passed some time ago.

Mainnet stays unratified for both, so its behaviour is byte for byte unchanged.
On testnet this changes state derived from existing bytes, so the changelogs now
carry an Activation section saying so, and every hub and the indexers following
it must update together rather than one at a time.

The activation map is mirrored in five places and all five move together: both
service copies, the documented canonical, the vendored copy the test helper
reads, and the assertion that used testnet as its example of an unratified
network, which it no longer is.
…rmed there

The activation page still described the response mirror as the one height-keyed
rule unarmed on testnet, which stopped being true when the v0.15.0 train
ratified it at block 151324. Mainnet is untouched and still carries the null
that encodes never.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant