Apr 24, 5-6 PM (38)
Apr 24, 6-7 PM (27)
Apr 24, 7-8 PM (12)
Apr 24, 8-9 PM (42)
Apr 24, 9-10 PM (17)
Apr 24, 10-11 PM (30)
Apr 24, 11-12 AM (16)
Apr 25, 12-1 AM (8)
Apr 25, 1-2 AM (1)
Apr 25, 2-3 AM (10)
Apr 25, 3-4 AM (5)
Apr 25, 4-5 AM (3)
Apr 25, 5-6 AM (13)
Apr 25, 6-7 AM (1)
Apr 25, 7-8 AM (4)
Apr 25, 8-9 AM (24)
Apr 25, 9-10 AM (17)
Apr 25, 10-11 AM (4)
Apr 25, 11-12 PM (4)
Apr 25, 12-1 PM (13)
Apr 25, 1-2 PM (3)
Apr 25, 2-3 PM (10)
Apr 25, 3-4 PM (6)
Apr 25, 4-5 PM (10)
Apr 25, 5-6 PM (16)
Apr 25, 6-7 PM (13)
Apr 25, 7-8 PM (30)
Apr 25, 8-9 PM (55)
Apr 25, 9-10 PM (13)
Apr 25, 10-11 PM (21)
Apr 25, 11-12 AM (22)
Apr 26, 12-1 AM (5)
Apr 26, 1-2 AM (0)
Apr 26, 2-3 AM (2)
Apr 26, 3-4 AM (5)
Apr 26, 4-5 AM (2)
Apr 26, 5-6 AM (2)
Apr 26, 6-7 AM (3)
Apr 26, 7-8 AM (8)
Apr 26, 8-9 AM (3)
Apr 26, 9-10 AM (0)
Apr 26, 10-11 AM (2)
Apr 26, 11-12 PM (1)
Apr 26, 12-1 PM (6)
Apr 26, 1-2 PM (4)
Apr 26, 2-3 PM (14)
Apr 26, 3-4 PM (14)
Apr 26, 4-5 PM (0)
Apr 26, 5-6 PM (13)
Apr 26, 6-7 PM (13)
Apr 26, 7-8 PM (7)
Apr 26, 8-9 PM (7)
Apr 26, 9-10 PM (5)
Apr 26, 10-11 PM (27)
Apr 26, 11-12 AM (21)
Apr 27, 12-1 AM (7)
Apr 27, 1-2 AM (7)
Apr 27, 2-3 AM (9)
Apr 27, 3-4 AM (9)
Apr 27, 4-5 AM (5)
Apr 27, 5-6 AM (13)
Apr 27, 6-7 AM (7)
Apr 27, 7-8 AM (82)
Apr 27, 8-9 AM (47)
Apr 27, 9-10 AM (33)
Apr 27, 10-11 AM (62)
Apr 27, 11-12 PM (80)
Apr 27, 12-1 PM (66)
Apr 27, 1-2 PM (44)
Apr 27, 2-3 PM (52)
Apr 27, 3-4 PM (42)
Apr 27, 4-5 PM (36)
Apr 27, 5-6 PM (26)
Apr 27, 6-7 PM (13)
Apr 27, 7-8 PM (26)
Apr 27, 8-9 PM (13)
Apr 27, 9-10 PM (16)
Apr 27, 10-11 PM (42)
Apr 27, 11-12 AM (28)
Apr 28, 12-1 AM (17)
Apr 28, 1-2 AM (8)
Apr 28, 2-3 AM (4)
Apr 28, 3-4 AM (5)
Apr 28, 4-5 AM (5)
Apr 28, 5-6 AM (8)
Apr 28, 6-7 AM (8)
Apr 28, 7-8 AM (37)
Apr 28, 8-9 AM (54)
Apr 28, 9-10 AM (59)
Apr 28, 10-11 AM (53)
Apr 28, 11-12 PM (56)
Apr 28, 12-1 PM (49)
Apr 28, 1-2 PM (54)
Apr 28, 2-3 PM (69)
Apr 28, 3-4 PM (31)
Apr 28, 4-5 PM (14)
Apr 28, 5-6 PM (47)
Apr 28, 6-7 PM (9)
Apr 28, 7-8 PM (9)
Apr 28, 8-9 PM (14)
Apr 28, 9-10 PM (20)
Apr 28, 10-11 PM (34)
Apr 28, 11-12 AM (29)
Apr 29, 12-1 AM (13)
Apr 29, 1-2 AM (1)
Apr 29, 2-3 AM (1)
Apr 29, 3-4 AM (6)
Apr 29, 4-5 AM (1)
Apr 29, 5-6 AM (4)
Apr 29, 6-7 AM (12)
Apr 29, 7-8 AM (45)
Apr 29, 8-9 AM (75)
Apr 29, 9-10 AM (49)
Apr 29, 10-11 AM (28)
Apr 29, 11-12 PM (51)
Apr 29, 12-1 PM (39)
Apr 29, 1-2 PM (21)
Apr 29, 2-3 PM (66)
Apr 29, 3-4 PM (25)
Apr 29, 4-5 PM (36)
Apr 29, 5-6 PM (16)
Apr 29, 6-7 PM (10)
Apr 29, 7-8 PM (14)
Apr 29, 8-9 PM (13)
Apr 29, 9-10 PM (17)
Apr 29, 10-11 PM (25)
Apr 29, 11-12 AM (29)
Apr 30, 12-1 AM (6)
Apr 30, 1-2 AM (8)
Apr 30, 2-3 AM (1)
Apr 30, 3-4 AM (6)
Apr 30, 4-5 AM (2)
Apr 30, 5-6 AM (8)
Apr 30, 6-7 AM (15)
Apr 30, 7-8 AM (17)
Apr 30, 8-9 AM (100)
Apr 30, 9-10 AM (19)
Apr 30, 10-11 AM (50)
Apr 30, 11-12 PM (120)
Apr 30, 12-1 PM (69)
Apr 30, 1-2 PM (45)
Apr 30, 2-3 PM (117)
Apr 30, 3-4 PM (29)
Apr 30, 4-5 PM (34)
Apr 30, 5-6 PM (9)
Apr 30, 6-7 PM (20)
Apr 30, 7-8 PM (23)
Apr 30, 8-9 PM (28)
Apr 30, 9-10 PM (13)
Apr 30, 10-11 PM (25)
Apr 30, 11-12 AM (15)
May 01, 12-1 AM (18)
May 01, 1-2 AM (15)
May 01, 2-3 AM (6)
May 01, 3-4 AM (7)
May 01, 4-5 AM (3)
May 01, 5-6 AM (5)
May 01, 6-7 AM (8)
May 01, 7-8 AM (13)
May 01, 8-9 AM (24)
May 01, 9-10 AM (16)
May 01, 10-11 AM (16)
May 01, 11-12 PM (16)
May 01, 12-1 PM (37)
May 01, 1-2 PM (29)
May 01, 2-3 PM (19)
May 01, 3-4 PM (13)
May 01, 4-5 PM (15)
May 01, 5-6 PM (0)
3,745 commits this week Apr 24, 2026 - May 01, 2026
asteria-game: strip testnet to nodes + asteria-game only
testnets/asteria_game/ is here to exercise the asteria game under
fault injection — the supporting cast (tracer, tracer-sidecar,
sidecar, log-tailer, tx-generator) was carried over from
cardano_node_master but adds no signal we score in this testnet.

Removed services:
  - tracer + tracer-sidecar — unused; the asteria-game container
    has its own SDK fallback path at /tmp/sdk.jsonl.
  - sidecar + log-tailer — would have surfaced node logs in the
    Antithesis report but we aren't scoring node-internal events
    here.
  - tx-generator — produces background traffic. The asteria
    workload is the only chain activity worth tracking.
  - tracer-config.yaml — dead config file, deleted.

Also drops the @--tracer-socket-path-connect@ flag from the node
commands (no tracer to connect to) and the @tracer:@ volume +
mounts.

Resulting service list: configurator (one-shot), p1/p2/p3
producers, relay1/relay2 relays, asteria-game.

Locally validated end-to-end on the stripped cluster:
  - bootstrap: asteria_bootstrap_asteria_created
  - spawn pass id=1: asteria_player_ship_spawned_1
  - admin_singleton invariant: count=1, hit=true
  - consistency invariant: counter=0/ships=0, hit=true
testnet(adversary): strip workload services, only nodes + adversary
Remove tx-generator, asteria-stub, log-tailer from the
cardano_node_adversary testnet. Only the cardano-node containers
(producers + relays), configurator, tracer, tracer-sidecar, sidecar,
and the adversary daemon itself remain.

Reason: the adversary testnet exists to isolate the adversary's
effect on the cluster. Running tx-generator and asteria-stub
mingled their findings with the adversary's, producing 11 of 12
findings that were tx-generator / asteria-stub related, not
adversary-related. Stripping them to:

- configurator (bootstrap)
- tracer + tracer-sidecar (chainPoints.log feed for adversary)
- sidecar (Antithesis setup-complete signal)
- p1, p2, p3 (block producers)
- relay1, relay2 (relays)
- adversary (the daemon under test)

No tx-generator, no asteria-stub, no log-tailer. The only workload
in this testnet is the adversary's chain-sync attacks against the
producers.

cardano_node_master is unchanged.
fix(workflow): honour inputs.test in workflow_dispatch
The Antithesis dispatch workflow defines `inputs.test` but the
`TESTNET` env was hardcoded to "cardano_node_master", so every
manual `gh workflow run "Antithesis on cardano-node testnet"
-f test=<other>` silently ran against the master testnet anyway.

Run 25211920161 dispatched with -f test=cardano_node_adversary
ended up running against cardano_node_master per the moog request
body ("directory":"testnets/cardano_node_master") — the new
adversary testnet was never exercised, the adversary container
never started, and the 8 findings_new were the master testnet's
own behaviour without an adversary daemon at all.

Reading inputs.test with a fall-through to the existing default:

    TESTNET: ${{ inputs.test || 'cardano_node_master' }}

Cron behaviour is unchanged — cron events have no inputs, so the
fall-through returns "cardano_node_master" exactly as before. Only
manual dispatches with -f test=<other> are affected.

This is a CI workflow change, not a testnets/cardano_node_master
edit. It does NOT modify the master testnet directory.
fix(testnet): make cardano_node_adversary coexist on a shared host
Two changes to allow the adversary testnet to run alongside other
local cardano-node-antithesis testnets (asteria_game, cardano_amaru,
the master itself) without container_name or network-name clashes:

- Drop the explicit `container_name:` directive on every service.
  Compose's auto-generated <project>_<service>_1 names are unique
  per project; explicit container_name forces a single global name
  that collides with any other compose file using the same value.
  Service-name DNS (p1.example via hostname:) is unaffected.

- Drop the explicit network `name: cardano-node-testnet`. With
  `docker compose -p cardano_node_adversary up`, the default
  network becomes cardano_node_adversary_default, which never
  collides with the master testnet's network or any other
  per-project network on the host.

Master testnet (cardano_node_master) keeps container_name on
purpose because the publish-images / smoke-test scripts already
expect those globally-stable names. The adversary testnet is the
iteration loop, used in parallel with other local testnets — it
needs the project-scoped naming.

Verified locally:

- docker compose -p cardano_node_adversary up -d brings all 12
  services up alongside an existing cardano_amaru cluster (no
  clash).
- tracer-sidecar writes /tracer/chainPoints.log (15+ entries
  within ~1 min).
- All three composer scripts (parallel_driver_flaky_chain_sync,
  eventually_adversary_active, finally_adversary_summary) exit 0
  and emit all 5 SDK assertions with condition=true / hit=true.
- Daemon stderr shows 'Right (Point (At (Block ... SlotNo 316
  ...)))' meaning real chain-sync work completed against p1.
feat(testnet): add cardano_node_adversary as iteration testnet
New testnet that mirrors cardano_node_master and adds the
long-running cardano-adversary daemon. Created as a separate
iteration loop for the adversary roadmap so:

- The scheduled cron continues to target cardano_node_master only.
  Findings on the adversary testnet cannot regress the master
  baseline because nothing automatic consumes this testnet.
- New adversarial endpoints (Tier 1.2 chain_sync_thrash,
  1.3 slow_loris, Tier 2 / 3 / 4) iterate here first, dispatched
  manually via:
    gh workflow run "Antithesis on cardano-node testnet" \
      --ref <branch> -f test=cardano_node_adversary -f duration=1
- Promotion of any change to cardano_node_master happens only
  after multiple branch-dispatched runs against this testnet
  produce findings_new ≤ master baseline (currently 9), and
  with explicit user approval.

Layout:

- testnets/cardano_node_adversary/ — full clone of master plus
  one new `adversary` service. Same producers (p1/p2/p3),
  relays (relay1/relay2), tracer, tracer-sidecar, sidecar,
  tx-generator, asteria-bootstrap, asteria-player, log-tailer.
- adversary service mounts relay1-state for the control socket
  and tracer:ro for chainPoints.log; depends_on relay1 +
  tracer-sidecar + configurator.
- Image pinned to ghcr.io/cardano-foundation/cardano-node-antithesis/adversary:cc628d5
  (current built tag from PR #104). Will be bumped per follow-up
  branches as adversary code evolves.

Docs:

- docs/testnets/cardano-node-adversary.md — overview,
  scheduling discipline, promotion criteria.
- docs/testnets/index.md — entry pointing at the new page.
- docs/testnets/cardano-node-master.md — sidecar row updated
  to drop the now-relocated adversary driver mention.
- mkdocs.yml — nav entry for the new testnet doc.

components/adversary/ is unchanged (the wrapper image keeps
building from the same tree), and components/sidecar/ is
unchanged.

The helper_sdk_lib.sh on main is byte-identical to
tx-generator's; tx-generator's SDK assertions are ingested by
Antithesis successfully, so we keep it as-is and use the first
branch-dispatched run on this new testnet as the diagnostic
for whether the adversary container's assertions are or aren't
reaching the SDK ingest channel.
asteria-game: add asteria-invariant binary + anytime/finally drivers
Rounds out the asteria_game testnet's report-assertion surface with
two new property checks driven by a third exec, /bin/asteria-invariant:

  - admin_singleton (sdkAlways) — exactly one asteriaAdmin NFT exists
    at the asteria spend address. The bootstrap mints the NFT once;
    nothing in the designed game flow burns or duplicates it, so any
    deviation is a real bug.
  - consistency (sdkSometimes) — the asteria UTxO's ship_counter
    equals the count of SHIP* tokens at the spacetime spend address.
    True after pure-spawn flows; later quit/mine flows will burn
    ships and break the equality, so this is a sometimes-property.

components/asteria-game/app/InvariantMain.hs reads ASTERIA_INVARIANT
and emits exactly one SDK assertion per invocation.
components/asteria-game/asteria-game.cabal exposes a third executable
asteria-invariant (built into the docker image).
components/asteria-game/composer/stub/anytime_asteria_admin_singleton.sh
fires the always-property at random points in the test.
components/asteria-game/composer/stub/finally_asteria_consistency.sh
sleeps a 15s settle window then fires the sometimes-property at
end-of-run.

Locally validated on testnets/asteria_game/:
  - pre-bootstrap: admin_count=0, hit=false (correctly flags missing
    deploy)
  - post-bootstrap: admin_count=1, hit=true
  - consistency at ship_counter=0/ships=0: hit=true (vacuously)