Skip to content

Pre-public functional verification

This page is the v1.3.36.8 verification gate -- a single-source inventory that maps every shipped feature of argos-edge to a smoke script (or documents why no smoke exists). Run before making the repo public; re-run on any future release that might regress a covered surface.

Summary

Pre-v1.3.32 smoke scripts 9
Verification gap fillers (v1.3.32) 4
Post-v1.3.32 smoke scripts (v1.3.33-v1.3.36.x) 5
v1.3.38.x smoke rows (refetch loop, cold latency, warm set, boot, certs, 7d, threats bytes) 7 (6 scripts)
v1.3.39 smoke rows (WAF sources agree) 1
v1.3.40 smoke rows (log pipeline, strip cursor) 2
v1.3.41 smoke rows (read pool) 1
v1.3.42 smoke rows (rollup agree) 1
v1.3.42.1 UI verification (captures + manual checklist, no new smoke script) -
v1.3.42.2 smoke rows (event storm) 1
v1.3.42.3 smoke rows (rollup readers) 1
Total smoke scripts 31
EFFECT-verified PASS against prod stack (panel binary v1.3.35 / v1.3.38.x / v1.3.39.x) 24
PASS on prod after the 48 h reading of 2026-09-28 (logs-pipeline; v1.3.40.0-.3) 1
EFFECT-verified PASS after the v1.3.40.4 deploy (strip-cursor) 1
PASS gates 0-2 on prod after the v1.3.41.0 deploy; gate 3 FAIL CPU-bound, see v1.3.41.1 (read-pool) 1
v1.3.41.1 gate 3 FAIL on prod by the pinned-refresh overlap (known issue, v1.3.42.0); gates 0-2 PASS (same script)
PASS on prod after the v1.3.42.0 deploy (rollup-agree); drift gate 8 of 8 verdicts at 0 by 2026-09-28 19:58 UTC 1
PASS on prod after the v1.3.42.2 deploy, first pull (event-storm); the two-pull window follows 1
Gated on operator-mediated input (creds / TOTP) 1 (auth-flow)
Legacy regression test (intentionally tests broken path) 1 (country-block)
Blockers preventing public release 0

The four post-v1.3.32 additions reflect features shipped or deploy-pipeline incidents addressed since the original verification gate was drawn:

  • country-reconciler.sh (v1.3.33) — 5min ticker EFFECT for expansion-divergence detection
  • lapi-flush-cap.sh (v1.3.33) — alert-shape verification (one alert with N decisions, mirroring CAPI/community-blocklist shape) after the eight-strike CAPI cascade-flush incident
  • deploy-rebuild.sh (v1.3.34.3) — make deploy-prod actually rebuilds the panel image (closes eleventh-strike silent-no-op gap)
  • demo-environment.sh (v1.3.35) — ~/argos-demo parallel stack self-smoke (separate volumes/network from ~/argos-prod)
  • capture-automation.sh (v1.3.36.x) — Playwright capture spec self-smoke (storageState wiring, safeClick blocklist, per-surface selector regression-guards). 14 phases of static checks.

Smoke matrix

Each row: feature, smoke script, last EFFECT verified.

Feature Smoke script Status Verifies
Sync-prod operator tooling sync-prod.sh ✅ PASS 5/5 self-gates against tmpdirs (refuse invalid paths, no-op when in sync, drift propagates, operator files protected, excludes work)
LAPI SQLite WAL (v1.3.28) lapi-wal.sh ✅ PASS PRAGMA journal_mode=wal; startup warning absent in current container's logs; .db-wal sidecar present
Scenario descriptions (v1.3.30) scenario-descriptions.sh ✅ PASS Slimmed index produced (115KB); 54/54 scenarios have description; CVE-2017-9841 carries expected text; graceful degrade with file removed; mtime-driven recovery
Scenarios management (v1.3.25) scenarios-toggle.sh ✅ PASS PATCH disable -> sentinel -> setup-appsec.sh -> cscli scenarios list confirms removed -> re-enable -> cscli confirms back
AppSec tuning (v1.3.25) appsec-tuning.sh ✅ PASS PATCH inbound 12 -> sentinel -> reload -> argos-tuning.yaml carries new threshold -> restore round-trip
Drift detection (v1.3.27) drift-detection.sh ✅ PASS 12 phases: scenarios + tuning surfaces both flip drift_detected=true on PATCH+wait65s and clear on setup-appsec.sh+wait65s
True detect mode (v1.3.29) true-detect-mode.sh ✅ PASS PUT true_detect_mode=true -> profiles.yaml splice -> crowdsec restart -> synthetic LAPI alert with target_fqdn=test_host produces 0 decisions; toggle off produces 1 decision (default_ip_remediation baseline)
Country expansion async (v1.3.31) country-expansion-async.sh ✅ PASS 8 phases: BR async expand 11/11 chunks 5009 ranges in <60s; failure path with crowdsec stopped produces state=failed + error_message; recovery to healthy within 30s
Country expansion legacy (v1.3.21) country-block.sh ⊘ SKIP-LEGACY Tests upstream-broken cscli scope=Country path the bouncer plugin doesn't natively handle (seven-strike #2). Replaced by country-expansion-async.sh. Refuses to run with placeholder defaults by design
Authentication lifecycle (NEW) auth-flow.sh ⏸ DEFERRED Login -> session cookie -> /me -> logout -> 401. Requires operator credentials (ARGOS_USERNAME + ARGOS_PASSWORD); cannot run unattended in CI. Detects TOTP-pending response and exits PASS-PARTIAL
Host CRUD + Caddy reconcile (NEW) host-crud.sh ✅ PASS 7 phases: POST host -> GET echo -> toggle flips enabled -> PUT updates auth_required -> DELETE -> 404 -> caddy admin status reachable (proxy for "reconciler healthy")
Whitelist round-trip (NEW) whitelist-roundtrip.sh ✅ PASS 8 phases: POST whitelist -> GET contains -> sentinel updated -> setup-appsec.sh -> argos-whitelist.yaml has the IP -> DELETE -> sentinel + yaml clean
Banned IPs round-trip (NEW) banned-ips-roundtrip.sh ✅ PASS 5 phases: cscli add -> panel /security/decisions lists with origin=cscli -> panel DELETE -> cscli confirms gone (15s cache TTL on Client.ListDecisions accounted for)
Country expansion reconciler (v1.3.33) country-reconciler.sh ✅ PASS 5min ticker compares panel-tracked CIDR count against actual LAPI Range decisions for the country; flips state='drifted' when divergent; clears on next reconcile after expansion completes
LAPI alert-shape cap (v1.3.33) lapi-flush-cap.sh ✅ PASS Mirror CAPI/community-blocklist shape: 1 alert with N decisions inside decisions[] (NOT N alerts with 1 decision each). NG +1 chunk + IR +3 chunks under 5000-item flush.max_items default; no cascade flush observed
Deploy-pipeline rebuild (v1.3.34.3) deploy-rebuild.sh ✅ PASS make deploy-prod actually rebuilds the panel image (post-fix for the eleventh-strike build: !reset + image-pin silent no-op that let v1.3.34.1+v1.3.34.2 ship without deploying). Verifies image hash changes after a known source edit
Demo environment isolation (v1.3.35) demo-environment.sh ✅ PASS ~/argos-demo parallel stack self-smoke — separate compose project, volumes, and docker bridge from ~/argos-prod; ensures demo-stack mods can never bleed into operator's prod
Playwright capture spec (v1.3.36.x) capture-automation.sh ✅ PASS (14/14 phases) Static checks: run.sh refuses without .env, .env gitignored, viewport 1440x1080, storageState wiring, safeClick blocklist (13/13), waitForSettled helper, openModal modal-visibility wait, host-row trigger selector, safeClickTab tab nav, DNS-01 selector, threats-decisions selector + screenshot helper
Dashboard refetch loop (v1.3.38.0) dashboard-refetch-loop.sh ✅ PASS Passive AF_PACKET count of GET /api/dashboard/overview on the panel's docker bridge, 60 s, operator's browser tab in the foreground: 1,293 on 1.3.35 (FAIL) -> 2 on 1.3.38.0 (PASS)
Cold dashboard latency (v1.3.38.1) dashboard-latency.sh ✅ PASS curl -w %{time_total} after 40 s idle (30 s cache expired) against prod, threshold 2 s: dashboard/health 16.5 s -> 0.044 s, security/overview 44.8 s -> 0.003 s (1.3.38.0 -> 1.3.38.1)
Dashboard warm set + probe cache (v1.3.38.2) dashboard-latency.sh (MAX_SECONDS=1 EXPECT_CACHE=hit) ✅ PASS After 40 s idle /api/dashboard/health 0.0007 s with X-Argos-Cache: hit; all four default views hit at +60 s after start with nobody in the panel; panel at rest 1.9 % CPU. /api/certs last_checked_at = warm-up probe time; latency still 1.06 s (LIKE in enrichWithLastEvent, v1.3.38.3)
Panel boot path (v1.3.38.3) panel-boot.sh ✅ PASS Read-only, from the container's boot log: boot-to-listen 1.75 s -> 0.139 s; first retention purge 120.5 s after listen (was before listen). /api/hosts during the purge: p50 1.3 ms, max 0.31 s
Certs list latency (v1.3.38.3) certs-latency.sh ✅ PASS /api/certs 0.93 s -> 0.001 s without inline last event; /api/certs/{id}/last-event 200 in 0.10 s (was 404)
Threats server paging (v1.3.38.5) threats-page-bytes.sh ✅ PASS Prod after deploy, operator's tab on /threats in the foreground, 24k CAPI decisions: 6 requests, 180,376 B on the wire in 60 s, 30,062 B per response (1.3.38.4: 7.2 MB decoded per response). Demo before/after: passive tap of the panel's reply bytes to GET /api/threats/decisions with a headless tab on /threats for 60 s, 7,400 decisions: 1.3.38.4 5 req / 1,661,813 B on the wire (10.1 MB decoded) FAIL -> 1.3.38.5 5 req / 106,489 B (104 KB decoded) PASS; first rows 2,126 ms -> 236-315 ms; hidden tab 35 s: 0 requests; 6 drawer route changes: 0 frames without the Layout header, 1 /api/auth/me per session (1.3.35.4: 3). Browser runs against ~/argos-demo only
Log pipeline (v1.3.40.0-.3) logs-pipeline.sh ✅ PASS (48 h reading 2026-09-28) 48 h reading on prod, 2026-09-28 09:40 UTC (planning doc v1.3.40 section 10): 0 access rows older than the strip watermark carry raw (4,764 rows between the watermark and now - 25 h are the 6 h ticker cadence); first full day with the filters (09-27) stored 44,852 access rows (64-76k per day before) and 175 caddy_error rows (about 3,000 before); excluded counters at 23k per day pace (Uptime-Kuma 8,143 + health checker 1,162 by 09:38 UTC on the 28th); argos.db still 1.72 GB because the stripped rows leave 315k leaf pages 83 % empty (dbstat: 1.07 GB unused inside log_entries pages, live data about 220 MB, freelist 125 MB), the file stops growing once the 7 d retention frees those pages (through 2026-10-03) and only an in-place VACUUM shrinks it (operator's call); nightly backup 128 MB -> 55-60 MB; WAL 14 MB. Earlier: prod after v1.3.40.3: filtered families stored since start 0 / 0 (PASS), 1,088 rows excluded today; strip PENDING until the panel is 25 h old (0 rows older than 25 h carry raw already; 411k rows emptied on 2026-09-26, access raw 661 MB -> 95 MB); purge-path p99 FAIL (204 ms while 214 rows were stripped) for the reason recorded in the v1.3.40 planning doc 4.1: the pinned warm-set refresh blocks reads up to 0.5 s every 24 s on the single connection even with no purge (max 542 ms, 15/3,667 samples over 50 ms), so the gate needs the read pool (moved to 40.1). DB size at 48 h: see the 48 h reading above
Strip cursor plan (v1.3.40.4) strip-cursor.sh PASS Prod after v1.3.40.4 (2026-09-26 14:10 UTC): 18,003 rows stripped in 9.5 s (1,900 rows/s; v1.3.40.3 did 740 rows/s), panel CPU median 4.6 % max 4.8 %, /api/hosts p50 1.5 ms p99 5.8 ms max 32.6 ms on the single connection. Dense demo, 430k rows: strip 392 s -> 242 s, panel CPU 50-60 % -> 2-5 %, cursor SELECT 100+ ms -> 0.4 ms (TestStripCursorPlan pins the plan through the driver)
Read pool (v1.3.41.0) read-pool.sh PASS gates 0-2; gate 3 FAIL CPU-bound, see 41.1 Prod after v1.3.41.0 (2026-09-26 15:10 UTC): boot log read pool enabled; idle p99 5.1 ms (n=1,085), strip p99 3.8 ms, export x3 p99 884 ms with p50 2.4 ms (FAIL, threshold kept): all slow samples fall inside the slowest export while the panel sits at 99-102 % of its 1-CPU cap (CSV encoder vs handler on GOMAXPROCS=1, not locking); 10 min RSS 38-74 MB, WAL 7.2 MB stable. Dense demo, final image, dd IO pressure (host io avg10 mean 22 %, peak 44 %): idle 6.2 ms, strip of 427k rows 8.0 ms, export x3 13.2 ms, cap purge 15.5 ms; same image with ARGOS_READ_POOL=0: 79 / 648 / 219 / 14,661 ms
Export keyset + pace (v1.3.41.1) read-pool.sh gate 3 FAIL on prod by the pinned-refresh overlap (p99 63 ms; export alone p99 7.4 ms); known issue, closes in v1.3.42.0 Prod after v1.3.41.1 (2026-09-26 16:03 UTC): 9 exports of 65,600 rows aggregated n=457, p50 1.5 ms, p95 7.1 ms, p99 63.0 ms, max 75.4 ms, 8 samples over 50 ms (v1.3.41.0: p99 884 ms); three timed exports right after: p99 7.4 ms, 0 of 180 over 50 ms, 8 throttled periods in 15 s. The slow samples coincide with the pinned 24 h refresh ticks on top of the export (nr_throttled +50 during the run). Dense demo: pool off p95 249 -> 24 ms, p99 395 -> 38 ms; pool on p99 9.8 ms (run a) and 67.7 ms (run b, same overlap)
Hourly rollup (v1.3.42.0) rollup-agree.sh PASS on prod; drift gate 8 of 8 at 0 (2026-09-26 23:56 to 2026-09-28 19:58 UTC) Drift verdicts since the v1.3.42.1 restart, each re-run read-only against the volume with both sides: hours 09-26 20:00, 09-27 02:00 / 08:00 / 14:00 / 20:00, 09-28 02:00 / 08:00 / 16:00 UTC; rows 2,010 / 3,741 / 1,676 / 1,700 / 1,758 / 1,678 / 2,239 / 1,685 and bytes 13,451,312 / 14,966,063 / 15,410,524 / 24,520,081 / 61,543,160 / 12,409,027 / 14,813,890 / 30,392,485 equal on log_entries and log_hourly, per-class counts equal, drift 0 in all eight (verdict 8 at 19:58 UTC on the cadence restarted by the v1.3.42.2 deploy); job lag 0 (rollup.last_hour 08:00 at 09:02 UTC on the 28th). Dense demo, PHASE 0 probe with the same SQL inside the panel cgroup under dd IO pressure: 168 h backfill 2 m 15 s, /api/hosts p99 7.2 ms, 36 of 1,362 periods throttled, divergence 0 on a closed hour (3,031 rows, 57,931,839 bytes). Final image on the demo: 169 h backfill in 39.0 s after the boot purge, /api/hosts p99 5.7 ms (0 of 1,172 over 50 ms), rollup-agree.sh PASS (lag 0, 3,049 rows / 55,240,994 bytes / 5 classes equal). Prod after v1.3.42.0 (2026-09-26 16:51 UTC): migration 034 applied at boot, 173 h backfilled in 55.8 s after the boot purge (slowest hour 432 ms), /api/hosts p99 7.7 ms meanwhile, rollup-agree.sh PASS (lag 0, 2,128 rows / 177,798,187 bytes / 5 classes equal); 48 h drift gate running
Event storm (v1.3.42.2) event-storm.sh PASS on prod after the 2026-09-28 13:56 UTC deploy: first pull (14:59 UTC, 15,000 decisions rewritten) 0 queue-full lines, 0.01 MB per day of panel log; self-run on 42.1 before the deploy: FAIL as expected, 21,395 lines in 4 h across 2 pulls, 19.1 MB per day; two-pull run at 18:00 UTC: PASS, 0 queue-full lines across 2 pulls (14:59 and 16:59 UTC, 15,000 decisions rewritten each), 5 panel log lines in 4 h (0.01 MB per day against 21) Unit: a simulated pull of 15,000 CAPI + 10 lists decisions with new IDs and one new local decision gives one threat_ip_banned, one summary with added 15,011, zero drops. Prod before the fix: 230,813 events dropped in 40 h, 219,607 WARN lines, 21 MB of journal per day
Rollup readers (v1.3.42.3) rollup-readers.sh PASS on the dense demo (rc3, 2026-09-28) and on prod after the 2026-09-29 19:26 UTC deploy (422,230 requests over 7 d equal on both sides, 7d 63 ms / 30d 113 ms cold); the p99 open-bucket point (the hour's maximum, on prod a websocket lifetime of 5.3 days) closed by v1.3.42.3.1 at 20:21 UTC: 7d p99 maximum 5,000 on the API, 0 points above, rollup-readers.sh and dashboard-latency.sh PASS after the deploy Dense demo, 500k rows: stats over a closed 7 d window equal the volume (499,867 rows, per class equal), timeseries sum equal, 7d / 30d shapes, X-Argos-Path; read-pool.sh gates 0-3 with IO pressure PASS, export x3 p99 6.5 ms (the v1.3.41.1 gate 3); range-7d-latency.sh and dashboard-latency.sh PASS
FilterBar, URL state, header search (v1.3.42.1) scripts/capture/run-demo.sh before/after + manual checklist (planning doc section 5) PASS (prod, operator checklist 2026-09-26) Demo, rc image: 27 surfaces captured before and after with the same 4 skips; deep links /logs?ip=&range=24h&source=, /threats?country=&type=, /security?tab=, /appsec?range=, /notifications?tab=&range=, /hosts?q= land filtered; Logs edits give /logs?range=6h&q=vault and back returns /logs?q=vault; header search on an IP shows decisions / logs (1,853 in 24 h) / whitelist / WAF with totals; countries response 1,704 bytes. Prod after v1.3.42.1 (2026-09-26 17:54 UTC): countries 1,593 bytes; read-pool.sh gates 0-2 PASS (idle p99 4.8 ms, strip 1.7 ms; gate 3 also 8.6 ms this run), dashboard-latency.sh strict and panel-boot.sh PASS; boot backfill re-filled the newest hour in 117 ms (rollup recovery after a restart). Operator checklist PASS 2026-09-26
WAF sources agree (v1.3.39.0, gate 1 % since v1.3.39.1) waf-sources-agree.sh ✅ PASS Prod after the v1.3.39.0 deploy: dashboard 260 / overview 260 / AppSec 260 / LAPI 266, 2 % (the 6 crowdsec-appsec-outofband alerts missed the appsec- prefix; fixed in v1.3.39.1, gate lowered to 1 %: prod after the v1.3.39.1 deploy 266 / 266 / 266 / 266, 0 %, PASS). Four readings of "AppSec events, last 24 h" within 5 %: dashboard/security waf_engines.appsec.events, security/overview appsec_hits_24h + appsec_bans_24h, appsec/metrics total_hits, LAPI through cscli alerts list (kind=waf + the two appsec-* scenarios). Before on prod (1.3.38.5): dashboard 0 / overview 0 / appsec 260 / LAPI 266, FAIL
7-day ranges (v1.3.38.4) range-7d-latency.sh ✅ PASS Cold traffic?range=7d 52.8 s -> 1.83 s, logs/stats 7d 35.4 s -> 1.18 s, busiest-host 7d 0.61 s (threshold 5 s); logs/timeseries 7d 27 s -> 0.74 s

Coverage gaps documented

Feature Why no automated smoke Mitigation
Recovery CLI subcommands (reset-password, disable-2fa, migrate, restore) CLI invocation against the panel binary needs a separate test process + container exec; meaningful test would require seeding a known user state and asserting post-conditions. Not blocking; CLI is operator-only and exercised manually during incident recovery Documented invocation in docs/operations/troubleshooting.md (existing); each subcommand has a --help that the operator validates before running it for real
Self-block detection / banner v2 Requires the operator's actual public IP to be banned in CrowdSec to surface the banner; cannot synthesise without breaking the operator's own connectivity to the panel. The underlying API endpoint (GET /api/security/check-self) is exercised via the auth-flow.sh smoke (the panel returns the data; the banner is pure UI) Manual: operator follows the documented "self-block recovery" flow at first onboarding to validate the banner renders correctly
Activity / audit log queries Read-only endpoint with no behavioural side effect; an empty response is indistinguishable from a working query against a fresh DB. Smoke would mostly assert "200 OK + JSON-shape" which adds little signal Read-only; if the endpoint breaks the only impact is the Activity tab renders empty. No incident risk
Dashboard widget stats Aggregated counters (banned count, whitelist count, etc.) computed live from the same endpoints other smokes already exercise; if those work the dashboard math works Implicit via banned-ips + whitelist + scenarios smokes
TOTP / 2FA enrollment + verification flow Requires interactive operator (TOTP secret + a real authenticator app); not scriptable Manual: documented in docs/features/auth-local.md
OIDC SSO end-to-end Depends on an external IdP (Keycloak / Authentik / etc.); operator's choice of IdP varies per deployment Per-IdP smoke would belong in the IdP's own test surface, not argos-edge
Backup + restore round-trip Existing argos backup CLI + argos restore are exercised manually during incident recovery; building a smoke would require tearing down the panel mid-test which conflicts with running other smokes alongside Documented manual path; make sync-prod covers operator-tooling sweeps
Reverse-proxy live healthcheck propagation Caddy's healthcheck pings backend targets; would need to spin up a stub backend that answers 200 vs 503 to assert; out of scope for a single-stack homelab smoke Implicit via host-crud (Caddy admin reachable post-reconcile)
Notifications (Slack / push / email) External delivery side-effects; smoking these would spam real channels. Each provider has its own test surface Per-provider configurability test exists in panel UI ("Send test notification" button)

Recommendation: ready for public

All 16 in-scope smokes PASS against the v1.3.35 panel binary. The 1 deferred (auth) is an operator-credential concern, not a code defect; the underlying handlers are exercised indirectly by every other session-bearing smoke. The 1 legacy-skip (country-block) tests an upstream-known-broken path that v1.3.21 worked around.

The thirteen-strike upstream-behaviour pattern (strike 12, v1.3.40.0: a test with an invented schema let a NOT NULL violation reach prod, since v1.3.40.1 tests use internal/db/dbtest with the real migrations; strike 13, v1.3.40.1: an EFFECT gate without prod density let a quadratic raw strip stall the panel, since v1.3.40.2 long writes must run against the dense demo seed first; documented in CLAUDE.md and the per-strike memory file) is now reflected in the smoke matrix: every external-protocol surface that caused an incident has a dedicated EFFECT-verifying smoke (LAPI WAL, scenarios source-of-truth, AppSec tuning, drift detection, true-detect-mode, country expansion async, country reconciler, alert-shape cap, deploy-pipeline rebuild).

Zero blockers. The repo is functionally ready for public release. The pre-public audit (docs/operations/pre-public-audit.md, v1.3.37) covers the non-functional gates (sanitization, doc currency, GitHub governance files).

How to re-run

SESSION=$(docker run --rm -v argos_prod_data:/data alpine sh -c \
    "apk add --no-cache sqlite >/dev/null 2>&1
     sqlite3 /data/argos.db \"SELECT token FROM sessions
       WHERE expires_at > datetime('now')
       ORDER BY id DESC LIMIT 1;\"")

# Cheap (no-auth, ~5-30s each):
./scripts/smoke/sync-prod.sh
CROWDSEC_CONTAINER=argos-prod-crowdsec ./scripts/smoke/lapi-wal.sh

# Auth-needing happy paths (~15-90s each):
ARGOS_SESSION_TOKEN="${SESSION}" \
PANEL_BASE_URL=http://localhost:9180 \
CROWDSEC_CONTAINER=argos-prod-crowdsec \
  ./scripts/smoke/scenario-descriptions.sh

# ... repeat the env block for the other smokes ...

# Long (4-min drift detector + 90s country async):
ARGOS_SESSION_TOKEN="${SESSION}" \
PANEL_BASE_URL=http://localhost:9180 \
CROWDSEC_CONTAINER=argos-prod-crowdsec \
  ./scripts/smoke/drift-detection.sh

ARGOS_SESSION_TOKEN="${SESSION}" \
PANEL_BASE_URL=http://localhost:9180 \
CROWDSEC_CONTAINER=argos-prod-crowdsec \
COMPOSE_DIR=$HOME/argos-prod \
SKIP_FAILURE_PATH=1 \
  ./scripts/smoke/country-expansion-async.sh

# Operator-credentials smoke (manual):
ARGOS_USERNAME=admin ARGOS_PASSWORD='...' \
  ./scripts/smoke/auth-flow.sh

What this report does NOT prove

  • Frontend visual rendering. Smokes exercise the API; the React UI is verified by the operator's browser pass.
  • Load behaviour at scale. Smokes are one-shot single-host exercises; sustained load testing is out of scope.
  • Cross-version migration path. Smokes run against the current schema; incremental upgrade from older argos-edge versions is documented in each release's notes and would warrant a dedicated migration smoke if released as a product (not yet a homelab need).