Changelog

Honest log of changes shipped to the public Syntarie testnet. Newest first. Every entry references a verifiable commit on dev/main so you can read the source.

For deeper context on what's audited and what's still queued, see the v1 readiness audit and project status.


2026-04-27

Trader Mode, third wallet UX mode (322184c5)

A trader-tuned wallet face alongside Operator and Simple. Tuned for the Base-mainnet trader who arrives via the testnet site link and has zero interest in operator surfaces. WalletMode enum {Operator, Simple, Trader} replaces the legacy simple_mode: bool field. [m] cycles Simple → Trader → Operator → Simple and persists to ~/.local/share/scc-tui/mode.txt (V1 fallback preserved exactly: persisted wins, else Simple under --testnet, else Operator). Trader header shows USD-equivalent balance via presentation::trader_usd_value() (testnet-only $0.10/wSCC placeholder; mainnet will swap to a real price feed) plus a reserved bridge-state slot. Trader tab strip is Wallet / History / Settings, Recipients/Watchlist/Intents/Disclosure stay reachable via help popup but aren't surfaced in the default tabs. History filter defaults to Transfer-only with [a] to toggle "show all". Trader help popup is the tightest of the three ([r] [t] [s] [$] [a] [m] [?] [q]), no F-keys, no panel-picker hint. F-key references in the status bar + Getting Started section are now gated behind is_operator() so Simple + Trader modes don't see operator-only chrome. 8 regression tests in tests_trader_mode.rs cover cycle order, persistence, USD-equiv math, tab-strip exclusions, default Transfer-only filter, [a] toggle, minimal help popup, Settings tab. Combined floor: 7,384 / 0 / 1.

Sepolia bridge codec verified, public state surveyed, lessons-learned monitoring (1077b6d8)

Three landings on the pre-flight + post-v1 ops trail. (1) Codec gate PASSED at fa584663: Python generate-withdrawal-msg.py produces signatures the deployed Sepolia BridgeVault accepts. 3/3 withdrawal sigs verify, 3/3 rotation sigs verify, proof tamper detection works. Highest-likelihood "drill fails for code reasons" class is ruled out. (2) Public RPC survey at 06ae8ab3: BridgeVault is currently paused (must unpause before drills fire), owner is 0xac5d91...728251 (controls both Vault + wSCC), wSCC totalSupply is 0. Critical clarification: prover signing seeds are PUBLIC, committed at ops/bridge-base/deploy/base-sepolia-r94-test-vectors.json with explicit "Disposable testnet" warning. The credential ask narrows to ONE wallet key for 0xac5d91...728251. (3) Two lessons-learned tickets shipped at 1077b6d8: deploy-node.sh --profile testnet|mainnet (testnet picks PEER_AUTH_MODE=open, mainnet picks producers, closes the 2026-04-26 incident) + public-client-probe.sh synthetic external probe (checks /status + lightclient_first_frame_total advancing over 30 s; non-zero exit = external clients rejected RIGHT NOW). Combined the two close the operator-error class that took ~12 hours to detect last time.

Testnet readiness report + RefreshScope::{Watchlist,Intents} wiring + flake fix (5853a5b4)

Three landings in one push. (1) docs/evidence/testnet-readiness-report-2026-04-27.md, the capital-decision artifact. One-line verdict: engineering for v1 mainnet is done; remaining blockers are operator labor (~1 day SSH-ops) + capital decisions ($20–50k seed liquidity + $0–60k optional risk mitigants). Includes per-goal % deltas, the 3-condition v1-done gate status (technical/commercial/organizational, all NOT MET, none gated on engineering), and a costed sequence of remaining work. (2) Wallet UX update (bad6b3b3): closes the #[allow(dead_code)] on RefreshScope::Watchlist + Intents by replacing the synchronous self-refresh pattern at watchlist add/remove + recurring-intent submit/cancel with mark_dirty calls. The V2 dirty-flag dispatcher picks them up next tick (2 s per-scope throttle). 8 regression tests in tests_dirty_refresh_post_v2.rs. ~150 net LOC. (3) Flake fix (ffc8a694): sample_app + minimal_app test fixtures now use tempfile::tempdir().keep() for unique-per-call wallet data dirs, removing the --test-threads=2 cross-test contention on the suspicious-flow code path. Verified at 878 / 0 / 0 under the full scc-tui suite in 1116 s. Combined floor: 7,376 / 0 / 1. Per the batch-binary-releases rule, the GitHub release artifact remains at the V3.3 sha (b6d26eab...) until the next deploy day.

Wallet UX quick wins, trader-friendly defaults (c52a3372)

Three wallet-UX defaults tuned for the testnet user (not just the operator engineer): (1) first-run mode selector + persistence, when you launch with --testnet, the TUI defaults to Simple mode instead of Operator. Mode choice persists at ~/.local/share/scc-tui/mode.txt, so toggling with [m] is remembered across launches. (2) Onboarding wizard skips the Network step when --testnet is passed (the testnet RPC URL is already baked into the preset; asking the user to pick a network is redundant noise). (3) Help popup pruned in Simple mode, only [r] [t] [s] [m] [?] [q] (+ [$] when --testnet); no F-key table, no : palette hint to overwhelm trader users. Operator mode unchanged for power users. +13 regression tests; floor 878.

Evidence: 24h baseline monitor + suspicious-flow flake diagnosis (1aee47f4)

Two artifacts. (1) scripts/testnet-24h-monitor.sh captures hourly snapshots over SSH from all 3 Hetzner validators, to be launched as a nohup background process to drive Goal 5 (Finality / reorg risk) from ~90% toward ~93%. Stub evidence doc at docs/evidence/testnet-24h-baseline-2026-04-27.md ready to fill in once snapshots land. (2) Static diagnosis of the unknown_recipient_direct_send_takes_suspicious_flow_escalation_before_any_submit concurrency flake at docs/evidence/flake-suspicious-flow-diagnosis-2026-04-27.md: most-likely root cause is sample_app passing std::env::temp_dir() as wallet data dir, racing under --test-threads=2 on shared TMPDIR. Fix proposed: swap to tempfile::tempdir() per call (≤10 LOC). Both artifacts shipped to source; the actual 24h soak run + flake fix sprint queued.

Wallet UX V3.3, single-panel-at-a-time invariant (879d8eb2)

Real-world TUI test exposed: pressing : palette + F10 (or any F-key) opened that panel but the previously-open panel's content stayed in the Wallet body, stacking. Cycling F1→F2→F3 stacked all three. Root cause was per-panel toggle state with no close-others step, plus 58 independent render checks that each fired if their flag was true. V3.3 makes current_operator_panel: Option<u8> the source of truth at paint time. F-key handlers became focus-aware toggles (re-pressing the focused F-key closes; pressing a different F-key opens cleanly even from a stale-flag state). Every if app.<panel>_open render block is gated with && matches!(app.current_operator_panel, Some(N) | None) (the | None arm preserves V3.2 display-test fixtures). Bonus: PgDn/PgUp cycle_to_operator_panel stripped from [close-prior, open-next] to a single open-next event since the render gate closes prior by ignoring its now-stale flag. Title oracle switched from detect_open_operator_panel to current_operator_panel for stale-immunity. 6 new V3.3 tests cover the user-visible bug directly. Test floor up to 865.

Em-dash cleanup, replaced prose dashes with commas + real bullet markers (ce1c8e5)

User feedback: too many em-dashes in the website copy, reads heavy. 4-pass sed on MDX (empty out table-cell em-dash placeholders first, then prose em-dashes to commas), manual edits on TSX to preserve list-item semantics (added list-disc on PlainEnglish ULs + removed &mdash; bullets). 20 files, 208 insertions / 210 deletions. Kept intentionally: LiveChainBadge no-data placeholders, JS comments, hyphens in compound words ("Rust-native", "production-ready", those aren't em-dashes).

Plain-English "what is this?" section on the homepage + docs intro

Real-world feedback from a friend with no blockchain background: "I get the Quickstart, but the product explanation could use simpler language." The Hero subhead is engineering-grade jargon, "permissioned consortium settlement chain", "L1", "private-first transaction submission", etc. Added a new section right after the Hero that translates the product into plain language with two side-by-side cards. Same explainer is now at the top of /docs/intro so readers who land on the docs first get the same translation. The technical Hero stays, it still serves engineers, the plain-English section is additive.

Wallet UX V3.2, operator panel takes over the Wallet body (dc6ca0f5)

"I select a panel via : and I just see panel 2 (Wallet)", the panel WAS opening, but its content was being appended below ~30 lines of standard account info inside a single non-scrolling Paragraph. On a typical laptop terminal it sat below the visible fold. Fix: when any operator panel is open, its content now renders above the standard account sections with a "── Account info below, [PgDn]/[PgUp] cycle ──" separator. The Wallet block title also flips to My Wallet, F<N> · <Panel Name> so you always know what surface you're on. Empty-panel render path is bit-for-bit identical to V3.1, no regression for users who don't open panels. New state::detect_open_operator_panel(app) -> Option<u8> helper handles direct-F-key opens too (V3.1's current_operator_panel only tracks palette + cycler opens).

Wallet UX V3.1, palette caching, contextual help title, PgDn/PgUp cycle panels (28a0db1a)

Four follow-ups to the V3 command palette, all from real-world laptop testing. (1) Palette no longer feels laggy, CommandPaletteState.cached_filter is rebuilt once per state-changing keystroke instead of being recomputed in both the key handler and the renderer; selected_idx clamps to the new length so the wrong row can't be selected after a backspace. (2) Help popup title now reads Help, <Tab Name> tab so users know the help is intentionally tab-aware (different keybinds per tab is by design, not a bug). (3) PgDn / PgUp now open panels instead of just cycling a hint-bar group. New App.current_operator_panel: Option<u8> tracks what's open; PgDn walks forward (wrap 44→1), PgUp walks backward (wrap 1→44). Implemented via a synthetic-key queue (VecDeque<KeyEvent>) so close-prior + open-next dispatch through the existing F-key handlers, zero changes to the 44 panel handlers. (4) Lazy-mount contract documented inline above the F-key match in app.rs: panels are NOT preloaded; each <panel>_data field is None until the F-key handler fires the RPC; closing keeps cached data. Test floor up to 854 (+11 new V3.1 tests).

Operator palette: opens globally + auto-switches to the Wallet tab so panels actually render (7ff743bf)

Two real-world laptop bugs from the panel-picker test. First, pressing : was gated to the Wallet tab, users on Status / Send / etc. couldn't open the picker. Second, even when they could open it, selecting a panel from another tab did nothing visible because the 44 operator panels render inside the Wallet-tab body (e.g. if app.epoch_panel_open { content.push(...) }), they're not standalone full-screen overlays. So the F-key handler flipped <panel>_open=true but the body that renders it never ran. Fix: removed the Wallet-only guard on the : opener, and the Enter resolution path now sets app.tab_index = 1 before queuing the synthetic F-key. Two-line change. The remaining UX gaps (laggy palette state, PgUp/PgDn cycling, Help context-awareness, lazy-load doc) are queued for V3.1.

Help popup: : panel picker is now the primary path (28e01038)

Real-world laptop test exposed the gap: the ? Help popup listed F1–F44 with no mention of the V3 command palette. Users on laptops opened Help, saw 44 F-key shortcuts they couldn't actually use (OS hijacks F-keys for brightness/volume), and missed : entirely. Help now leads with [:] open the panel picker and a one-line note about why F-keys fall short on laptops + most keyboards stopping at F12. F-key table stays beneath as a power-user fallback. Same source-of-truth panel list, no behaviour change, just framing.

Live chain status on the home page (60968b1)

The marketing site used to show "60 min live soak", frozen-in-time text. Now the third stat card on the home-page Hero is a live chain head readout: height + reachable-validator count + sync phase, with a green "live" pulse. Driven by a Next.js API route (/api/testnet-status) that proxies /status across all 3 validators server-side and caches for 2 seconds, so the public site doesn't pile load on the testnet when many people open it at once. Client polls every 3 seconds. Same plumbing makes the test floor / commit / audit-goal stats read from one source-of-truth file (lib/testnet-stats.ts), no more grepping 16 .mdx files when a number changes.


2026-04-26

Test floor at all-time high, 7,341 passed (51bc3f1f)

After the four wallet UX shipments and the protocol patch landed, the full suite was re-run. New floor: interface 6,482 + scc-tui 843 + foundry 16 = 7,341 passed / 1 ignored / 0 failed. Net +42 passing tests over the prior 7,299 floor, all from the regression coverage added for V1 splash, V2 dirty-flag refresh, and V3 command palette.

Wallet UX V1.1, header glyph fix + display test fixtures (51bc3f1f)

A pre-existing typo in the operator-mode header (Govern/Epoch were tagged ⑨/⑩ instead of ⑧/⑨, so the bar read "⑦ Watchlist │ ⑨ Govern" with ⑧ skipped) got caught when V1's new splash test asserted on a header dump. Fixed alongside two test-fixture adjustments, minimal_app() now clears splash_state after App::new so display tests render the post-splash steady state, and the splash test backend rect grew from 120 → 200 columns to fit the operator-mode header without truncation. Production paths untouched. 18 insertions / 5 deletions across 3 files.

Wallet UX V3, command palette for operator panels (31dedaa0)

44 operator panels were bound to F1F44, which most laptops hijack at the OS level for brightness/volume. Now : (colon) opens a fuzzy-search command palette listing all 44 panels by human-readable name. Type to filter (case-insensitive substring + literal f17/17 forms), ↑/↓ to move, Enter to open, Esc to close. Recent picks bubble to the top (cap 8). Existing F-key shortcuts still work for power users, palette is purely additive. Inline operator help line surfaces [:] panel picker (laptop-friendly) so it's discoverable without docs. Implementation trick: synthetic-key dispatch, palette Enter pushes a KeyCode::F(N) into a one-shot queue that the next event-loop iteration consumes through the existing F1–F44 match. Zero changes to the 4000-line operator-mode dispatcher. 12 regression tests cover panel-table contract, name uniqueness, filter forms, recent promotion, default state, recent-cap, tiny-rect render, and empty-result helper text.

Wallet UX V2, dirty-flag live-update (0c6849fc)

The wallet no longer freezes on faucet (the old code blocked the UI for 1.5 s waiting for the next block). New design: per-scope RefreshScope { Status, Account, Watchlist, Intents } with throttled dirty markers. Faucet, transfer-submit, and other write actions queue a deferred refresh instead of blocking. Background Status poll every 5 s keeps the chain head current while the wallet is idle. Idle CPU stays at zero. 11 regression tests cover throttle ordering, idempotency, and pending-marker promotion.

Wallet UX V1, startup splash (4a14c506)

The 1–2 second black-screen gap on cold start is replaced by a brand splash frame that draws within ~50 ms. Shows the ◰ glyph, "S Y N T A R I E" wordmark, tagline, and a status line that updates as the connection progresses (Resolving → Handshaking → Refreshing). Splash dissolves into the normal UI on the first post-refresh draw without flicker. 8 regression tests cover stage labels, App seeding, testnet pill, 1×1 rect resilience, and short-circuit behaviour.

Server-side light-client tolerance (b9bf1dca, deployed to all 3 validators)

The TUI's status query path (StatusReq directly after the encrypted handshake) was being rejected by the server, which expected a validator-style Hello exchange. Symptom for the user: ✗ Unreachable, Read failed: failed to fill whole buffer. Fix: a new HelloOrFirstFrame enum at the proto layer lets the server accept a non-Hello first frame from light clients (wallets) and dispatch it through the normal message loop with default client metadata. Validators still send Hello and keep their version-gate semantics. Telemetry counter p2p.lightclient_first_frame exposes the rate of light-client connections. Rolling-restart deploy to nbg-1 / hel-1 / nbg-3; chain stayed live throughout.

Testnet PEER_AUTH_MODE switched from producers to open

A precondition for the light-client tolerance fix: the per-node systemd env file was set to PEER_AUTH_MODE=producers, which is the right call for mainnet but blocks any non-validator P2P connection. For a public testnet we want open so user wallets can handshake. Validators still authenticate each other via the producer set. Also bumped the deploy template ops/deploy/examples/node.env.example to default open for testnet.

Faucet auto-refresh + review-panel text wrap (c53bd8c1, scc-tui v1)

  • Faucet ([$] on Wallet tab) used to dispense 100 SCC and tell the user to press [r] to see the new balance, now it auto-refreshes after one block. (Superseded by V2 dirty-flag flow above, but lived in the v1 release for users who installed before V2 shipped.)
  • Send-tx review paragraph used to clip at the right edge of the terminal because the Paragraph had no Wrap { trim: false }. Single-line fix.

install.sh, enterprise-grade installer (425b71d in scc-releases)

Treats install / reinstall / upgrade as first-class scenarios:

  • Detects existing install and computes its sha256 against the release manifest. Reports "already at <tag>" or "different version → will be replaced".
  • Asks before overwriting via an interactive prompt that reads from /dev/tty (so it works under curl | sh). SCC_YES=1 to skip for CI.
  • Verifies sha256 of every download against SHA256SUMS-<platform>.txt. Refuses to install on mismatch, won't silently install a tampered or corrupted binary.
  • Backs up before replace: existing binary moves to <name>.previous. One mv to roll back.
  • Both binaries by default (scc-tui AND scc-node), set SCC_BINS="scc-tui" to skip the CLI.
  • Friendly errors for missing curl, missing write permission, network failures.
  • Shell-aware PATH guidance, bash/zsh AND fish syntax both shown.

install.sh, hang fix + colors-by-default (2ee4caf in scc-releases)

Two real-world bugs from a CachyOS user retest:

  • Installer hung at "Checking what's already installed" because the version probe ran scc-tui --version, but scc-tui parses args manually (no clap) and on unrecognized flags falls into a config-file path that can enter interactive mode. Fixed: skip --version for scc-tui*; gate other binaries with </dev/null + a 2-second timeout.
  • Color escapes were stripped because [ -t 1 ] is unreliable under curl | sh. Now colors default ON and honor the standard NO_COLOR=1 / TERM=dumb opt-outs.

install.sh, ships scc-node by default (32507ed in scc-releases)

Default SCC_BINS flipped from scc-tui to scc-tui scc-node so any documented command works out of the box. Set SCC_BINS="scc-tui" to skip the CLI binary.

Doc accuracy pass (0dd869d, site repo)

Strict source-of-truth audit against interface/src/config/command_surface.rs:

  • scc-tui keygen / scc-tui key-address were wrong everywhere, these are scc-node subcommands. scc-tui has no clap surface (it's a TUI).
  • scc-tui transfer-intent was wrong, also a scc-node subcommand. Flag corrections: --rpc-url (not --rpc), --to (not --recipient), no --key flag (signing key is read from the path passed at startup).
  • --rpc-write-token is HTTP Bearer auth, not a CLI subcommand flag, added a curl + Authorization: Bearer example for raw HTTP submission.

Hetzner faucet, public access opened

ufw rule on all 3 validators changed from observer-only to public for port 19100. Faucet endpoint on nbg-1 dispenses 100 SCC per request, rate-limited 60 s per IP and 300 s per address.

Test floor reverified, 7,299 passed at 80e5a51

Per-crate: interface 6,482 + 1 ignored, scc-tui 801, foundry 16. +27 net vs the prior floor. Closed 7 audit-fix sprints' worth of findings. See docs/evidence/post-audit-fix-sprint-test-pass-2026-04-25.md in the source repo.


2026-04-25

Public testnet shipped

Three Hetzner validators in Nuremberg + Helsinki + Falkenstein, advancing at ~1–2 blocks/sec. curl https://github.com/syntarie/scc-releases/releases/download/testnet-2026-04-26/install.sh | sh installs the binaries.

Bridge code launch-ready

Python generate-withdrawal-msg.py now signs with real prover private keys (no longer treats public keys as Ed25519 seeds). Rotation drill digest tuple matches the Solidity RotateProverKeys contract. Drill 1/3/4/5 runbooks updated.

SECURITY-1 sibling closed fail-loud

evaluate_policy now rejects payload-supplied caller / height / signatures / count / amount / recipient / memo / cached state facts that conflict with the verified envelope. 8 spoofing axes covered. Goal 9 (smart contracts / execution safety) ticked from ~90% → ~93%.


2026-04-23

Chain-stability gate closed

Live 3-node soak, partial-connectivity survival, sync recovery, and SIGKILL chaos all validated on the post-STABILITY-7 runtime. Remaining STABILITY-4 follow-up A/B and STABILITY-6b/c/d are non-blocking hardening / forensic work.


How this page gets refreshed

This page is updated each time a user-visible change ships to the testnet. Code-only or internal-only changes that don't change behaviour for the wallet user, the operator, or the public RPC surface are not included, read git log dev/main in the source repo for the full history.

If you want notifications when this page changes, subscribe to the syntarie/scc-releases repository on GitHub, every release that ships new binaries will appear there.