fix(xmr): S07 - enforce deployment signer contracts

Series context

S07 of the focused replacement MRs for the superseded XMR MR !4983 (closed).

  • Scope: XMR deployment authentication, per-node signer identity, local signer exposure, immutable image enforcement, and rendered Compose contract coverage
  • Base while stacked: zly/s06-xmr-signer-contract
  • THORNode dependency: S06 / !5052
  • External dependency: thorchain/devops/serai!70 provides the required signer identity and capability contract
  • Merge: S06(!5052) into develop -> rebase/retarget & merge this MR.
  • Follow-up: S08 — scanner birthday, restart, and reorg recovery
  • Full roadmap: !4983

While S06 / !5052 is open, this MR should target its source branch so the review contains only S07. After S06 / !5052 merges, retarget this MR to develop.

Problem

S06 (!5052) makes XMR client construction fail closed when the signer or Monero daemon identity is missing or incompatible. The deployment layer must preserve that contract across every Compose profile and every node.

Several deployment-specific gaps remained:

  • YAML service merges could replace the complete signer environment on secondary mocknet signers, silently dropping shared network, authentication, and rate-limit settings.
  • The chainnet signer seed phrases were still passed directly through container environment variables instead of the same per-node file interface used by THORNode and Bifrost.
  • The scanner-reset helper derived authentication from the node mnemonic instead of using the explicitly configured deployment bearer.
  • Plaintext signer and simulation-helper APIs were published on every host interface.
  • No automated test rendered all Compose profiles and checked the resulting per-service identity, authentication, image, dependency, and seed assignments.

A Compose file can remain syntactically valid while violating any of these runtime contracts.

Changes

Enforce explicit deployment authentication

  • Document that chainnet and stagenet require a deployment-specific random XMR_FROST_AUTH_BEARER.
  • Require the same bearer to be provided to Bifrost and its local signer.
  • Preserve fixed bearer defaults only for the local mocknet fixture.
  • Keep signer health checks authenticated without expanding the bearer during Compose rendering.
  • Document that node mnemonics and keystore passwords must not be reused as signer API credentials.

Stop deriving authentication from the mnemonic

Update xmr-reset-scanner.sh to:

  • read the configured bearer from the running signer without printing it,
  • reject a conflicting bearer already present in the host environment,
  • export the verified bearer for Compose recreation,
  • authenticate the reset request with the signer’s configured bearer,
  • remove the previous seed-based token derivation.

Preserve per-node signer identity

  • Mount the dog, cat, and fox chainnet seed configs at /run/thornode/seed_phrase.
  • Point each signer at that file through SIGNER_SEED_PHRASE_FILE.
  • Ensure each node’s THORNode, Bifrost, and FROST signer use the same seed source.
  • Ensure different nodes cannot accidentally reuse the same seed fixture.
  • Reapply the complete shared signer environment before overriding cat, fox, and pig identities in mocknet, because service-level YAML merges do not merge nested environment maps.

The committed chainnet seed files are well-known development fixtures. Real deployment seeds must come from protected, non-committed storage such as a tmpfs mount.

Limit local signer exposure

  • Bind the primary signer API to host loopback in chainnet and stagenet.
  • Bind mocknet signer readiness ports and the simulation-helper API to host loopback.
  • Keep secondary chainnet and stagenet signer APIs unpublished.
  • Preserve container-to-container communication through the private Compose network.

Monero RPC and eBifrost host publication remain unchanged by this MR.

Add a rendered Compose contract

Add make test-xmr-compose-contract and a dedicated GitLab CI job that renders the mocknet, chainnet, and stagenet profiles and checks:

  • required versus deliberate mocknet bearer behavior,
  • signer and Monero network identities,
  • Bifrost-to-signer endpoint and authentication wiring,
  • removal of obsolete authentication and rate-limit variables,
  • per-node seed consistency and isolation,
  • authenticated signer health checks,
  • healthy-service dependencies,
  • private Compose network membership,
  • loopback-only signer and helper publication,
  • a uniform immutable tag@sha256 signer image across profiles,
  • separation of the faucet-capable simulation-helper image,
  • mainnet Monero daemon mode for the chainnet and stagenet fixtures.

The contract reports explicitly when the signer remains pinned to the temporary integration-only image.

Compatibility and rollout

This MR changes deployment wiring only. It does not change consensus state, ceremony messages, signer API formats, persisted key shares, or scanner state.

For each validator deployment:

  1. Generate one independent random bearer.
  2. Give that value to the validator’s Bifrost and local signer.
  3. Store it securely and reuse it across restarts.
  4. Do not share it between validator deployments.
  5. Do not derive it from the node mnemonic or keystore password.

Containers continue to reach the signer over the private Compose network. Loopback binding affects only access through host-published signer and helper ports.

The currently pinned Serai !70 (merged) image remains integration-only. It supports review and simulation but must be replaced with a reviewed published digest before merge or production XMR activation.

Out of scope

This MR intentionally does not include:

  • signer or Monero daemon identity protocol changes covered by S06,
  • scanner birthday or reorg recovery changes planned for S08,
  • XMR keygen or keysign coordination changes,
  • consensus migrations or application-version selection,
  • production XMR activation,
  • changes to Monero RPC or eBifrost host publication,
  • a production secret-management implementation,
  • runtime signer hot-swapping or mixed-version compatibility.

The rendered Compose contract passes and identifies the current signer image as an explicit integration-only pin.

Edited by ZlyDevMaya

Merge request reports

Loading
Loading