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 (14)
May 01, 4-5 PM (25)
May 01, 5-6 PM (10)
May 01, 6-7 PM (3)
3,726 commits this week Apr 24, 2026 - May 01, 2026
asteria-game: one-shot admin_mint policy parameterised on seed UTxO
Closes the always-true admin_mint placeholder PR #67 ships. The
bootstrap's "no double-mint on container restart" contract was
previously enforced only by the off-chain isAlreadyDeployed
check; this commit puts the contract on chain.

Aiken side (components/asteria-game/aiken/validators/admin_mint.ak):
  - validator admin_mint(seed: OutputReference) succeeds iff the
    tx consumes the seed AND the bundle minted under this policy
    is exactly [(asteriaAdmin, 1)]. Seed consumption is permanent
    on Cardano UTxO, so admin_mint can fire at most once across
    all chain history.
  - aiken/plutus.json regenerated; admin_mint now declares one
    parameter "seed". All 91 existing tests pass.
  - apply-params.sh / plutus-applied.json no longer in the build
    path — Haskell applies the seed at runtime.

Haskell side:
  - new Asteria.Validators.applyScripts: takes a seed TxIn and
    returns AppliedScripts { adminMint, pellet, asteria,
    spacetime } with both Plutus scripts and ledger-side hashes.
    Built on plutus-ledger-api's uncheckedDeserialiseUPLC +
    UntypedPlutusCore.applyProgram + serialiseUPLC. Hash
    dependencies are threaded in declaration order
    (admin_mint → pellet → asteria → spacetime).
  - new Asteria.Deploy module: read/write
    /asteria-deploy/seed.json. Bootstrap is the only writer;
    player + invariant are readers. ASTERIA_DEPLOY_DIR env var
    overrides the path.
  - BootstrapMain: reads seed.json on startup. If present,
    re-derives the same scripts (deterministic). If absent,
    picks a fresh wallet UTxO via pickWalletUtxo, writes the
    seed to disk BEFORE submitting the deploy tx (durable order
    so a crash leaves either no file or a consistent file).
    isAlreadyDeployed simplified to "any UTxO at the per-deploy
    asteria addr" — under the one-shot policy at most one such
    UTxO can ever exist.
  - PlayerMain + InvariantMain: read seed.json, applyScripts,
    use AppliedScripts.as{Asteria,Spacetime,Pellet,AdminMint}
    {Script,Hash} in place of former top-level constants. If
    seed file missing, emit asteria_*_seed_missing_<id>
    sdkUnreachable and exit cleanly (correct: bootstrap hasn't
    run yet on this cluster).

Compose:
  - new asteria-deploy named volume mounted at /asteria-deploy
    on the asteria-game container.

Locally validated end-to-end on testnets/asteria_game/:
  - pre-bootstrap: asteria_invariant_seed_missing_admin_singleton
    fires (correct)
  - bootstrap cold: asteria_bootstrap_seed_picked +
    asteria_bootstrap_seed_persisted + asteria_bootstrap_asteria_created
    (asteria addr is now per-seed: 77a02b8e... instead of the
    previous hardcoded 0824601a...)
  - container restart: asteria_bootstrap_seed_reused +
    asteria_bootstrap_already_deployed short-circuit
  - spawn: asteria_player_ship_spawned_1 succeeds with the
    seed-derived validators
  - consistency: ship_counter=1, ship_token_count=1, hit=true
  - admin_singleton: count=1, hit=true (post-bootstrap and
    post-spawn)
testnet(adversary): bump sidecar to a9e7743 (post-PR-99 image)
Findings root cause from run https://github.com/cardano-foundation/cardano-node-antithesis/actions/runs/25224228713
report: the failing
"Always: Commands finish with zero exit code →
chain-sync-client/parallel_driver_flaky_chain_sync.sh"
property was tripped by the SIDECAR container, not the adversary
container.

The Antithesis composer walks every container's
/opt/antithesis/test/v1/ for scripts and dispatches anything it
finds. The adversary container's parallel_driver dispatches and
returns the daemon's structured response; that's the intended
path. But the sidecar container, pinned at sidecar:f889dbc, is
from BEFORE PR #99 removed components/sidecar/composer/chain-sync-client/.
That stale image still ships the script that requires
CHAINPOINT_FILEPATH (which we no longer set on the sidecar
service) — and it just `adversary "$@"`s with no SDK assertions,
so it failed immediately:

  chain-sync-client/parallel_driver_flaky_chain_sync.sh sidecar.example
  Error: CHAINPOINT_FILEPATH environment variable is not set

Bumping the pin to a9e7743 (current branch tip, which exists on
origin) forces publish-images to rebuild the sidecar image from
this commit's components/sidecar/composer/ tree — which has only
`convergence/`, no `chain-sync-client/`. Once published, the
sidecar container in this testnet won't dispatch any chain-sync
script, and the only `parallel_driver_flaky_chain_sync.sh` in
play comes from the adversary container as intended.

cardano_node_master is unchanged (its sidecar pin stays at
:f889dbc per the master-readonly rule). When the time comes to
fix master, that's a separate explicit PR.
chore(testnet): un-park tx-generator on this branch for Antithesis dispatch
Main parks tx-generator (43bf739/8c45f76/d78c7b1 in
cardano-node-antithesis) so the cron run on main targets
a tx-generator-free reference cluster. But Antithesis
launches the cluster from compose AS-IS; passing
'-f tx-generator.disabled.yaml' isn't an option through
moog's launcher. So a 1h workflow_dispatch on this
branch needs the service un-parked HERE — otherwise the
Antithesis cluster has no daemon and the run is a no-op
for our verification.

Image points at the tag we pre-pushed earlier
(tx-generator:8c53529, digest sha256:3c13a0f3...).

When this PR merges, tx-generator becomes active on
master again. If we want to keep main tx-generator-free,
the merge should be staged with a follow-up commit that
re-parks it after Antithesis verification.
chore(tx-generator): repoint pin to upstream main 67935ff (post-#114 merge)
PR #114 merged to upstream main (commit
67935ff9c8dadbbdd8ec1b92a14e1f5e9e5d1f80). Repoint per
the *Pins main only* rule — PR #98 can now merge to
downstream main without a feature-branch pin.

narHash unchanged from the pre-merge feature-branch SHA;
image content (tx-generator:8c53529) is byte-identical
to what was already pre-pushed to GHCR.
testnet(adversary): re-add log-tailer
Passive observer — tails cardano-tracer's per-node JSON logs to its
own stdout so Antithesis Logs Explorer indexes them under
source=log-tailer. Read-only on the tracer volume; does not
generate any workload of its own.

Removed earlier when stripping workload services for adversary
isolation, but log-tailer isn't a perturbator — it's a diagnostic
sidecar. Adding it back lets us query node-level events via Logs
Explorer when triaging adversary effects.
asteria-game: drop tx-generator from testnets/asteria_game/
The tx-generator daemon produces background transaction traffic.
On testnets/asteria_game/ we explicitly want the asteria game's
own spawnShip / move / mine / quit traffic to be the only chain
activity — anything else is noise that distorts the report's
view of the workload we're scoring.

Service list now: configurator (one-shot), p1/p2/p3 producers,
relay1/relay2 relays, tracer, tracer-sidecar, sidecar,
log-tailer, asteria-game. Observability + chain telemetry
preserved; only the synthetic-traffic generator removed.
fix(composer): always-exit-0 + Reachability for not-applicable + daemon-unreachable handling
Three composer-side fixes folded into one commit, against
findings from prior Antithesis runs (most recent:
https://cardano.antithesis.com/report/tilehuSggX4cnuy5qyXfwpqI/2ZUJSYUipLqm3Dlbo9R3rjrS7i7dYJ_mc8FwikFqYLg.html).

1. (https://github.com/cardano-foundation/cardano-node-antithesis/issues/105)
   Switch tx_generator_*_not_applicable from
   'sdk_sometimes false' to 'sdk_reachable'. Antithesis
   grades a Sometimes assertion as PASSING when ≥1 sample
   has condition=true; we always emit condition=false for
   not-applicable, so that type can never satisfy.
   Reachability is the right shape — accumulates samples
   without a pass/fail grade.

2. (https://github.com/cardano-foundation/cardano-node-antithesis/issues/106)
   Replace 'set -euo pipefail' with 'set -u'. Always exit
   0; encode tick state purely via SDK assertions
   (asteria-stub convention: parallel_driver_heartbeat.sh
   in components/asteria-stub/composer/stub/).
   Antithesis's built-in 'Always: Commands finish with
   zero exit code' property has no opt-out; any non-zero
   exit fails it. The previous 'exit 1 on not-applicable'
   pattern would have worked if the property could be
   suppressed but it can't.

3. Wrap nc in '|| true', check for empty RSP, and on
   empty emit a tx_generator_*_daemon_unreachable
   reachability event then exit 0. Fixes the case where
   the composer fires a driver before the daemon's control
   socket is bound during early boot — without this, the
   'set -e' (now removed) would kill the script before any
   exit-0 path runs, and even with 'set -u' alone, the
   subsequent jq parse on empty input emits stderr noise.

4. eventually_population_grew.sh: distinguish "daemon
   unreachable" from "daemon says populationSize=0". Fire
   did_not_grow only when the snapshot RSP is non-empty
   AND parses as JSON. Otherwise emit
   tx_generator_eventually_daemon_unreachable
   reachability + exit 0.

5. parallel_driver_refill.sh: include 'index-not-ready' in
   the not-applicable case set. After
   cardano-node-clients#110 (freshness gate) and #114
   (pre-submit chain-tip probe), the refill arm returns
   IndexNotReady during the post-reconnect stale window;
   the composer should treat this as a documented
   not-applicable, not unknown-failure.

The submit-rejected paths keep their sdk_unreachable
(strict) framing — the daemon-side fixes (#105/#110/#114)
are expected to drive these to zero. The next 1h
Antithesis dispatch on this branch verifies the full stack.
chore(tx-generator): pin to upstream PR #114 (post-#105 + #110 + #114)
Picks up the pre-submit chain-tip UTxO probe — the
second-line defense for duplicate-submit-after-reconnect
that PR #110's freshness gate alone couldn't cover
(https://github.com/lambdasistemi/cardano-node-clients/pull/114
closes lambdasistemi/cardano-node-clients#111).

Stacks the full reconnect-resilience suite from upstream:
  * #105 — N2C reconnect supervisor + BlockedIndefinitelyOnSTM catch
  * #110 — post-reconnect indexer freshness gate (rsIndexFresh)
  * #114 — pre-submit chain-tip UTxO probe (this bump)

Will be re-pinned to the upstream main merge SHA before
this PR merges, per *Pins main only*.