From 8044cdafcdf2df76a1a1a52c58539d4bf1c7ba87 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Tue, 21 Jul 2026 19:00:12 +0200 Subject: [PATCH] ci: run the offline Tor keygen in CI, add a non-blocking network e2e job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the rootless Tor e2e skip so the deterministic offline checks (the real offline onion keygen and the production-flag guard) run whenever the tor binary is present, while only the live onion round-trip stays gated on LIM_E2E_TOR=1. The pytest job now installs tor so the keygen — which validates the exact production keygen path — runs on every push. A separate continue-on-error tor-network-e2e job exercises the full round-trip without making the public Tor network a blocking merge gate. The full QEMU build/boot/unlock e2e still needs root + KVM + Arch tooling and stays local/opt-in. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/test.yml | 17 +++++++++++++++++ tests/e2e/test_tor_unlock_e2e.py | 16 +++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12991c2..edd93e0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,5 +18,22 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.12" + # tor lets the deterministic offline onion-keygen test run (it validates + # the real production keygen path); that test needs no network. + - run: sudo apt-get update && sudo apt-get install -y tor - run: pip install pytest pyyaml - run: make test + + tor-network-e2e: + # The live onion round-trip depends on the public Tor network, so it is + # informational (continue-on-error) rather than a blocking merge gate. + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: sudo apt-get update && sudo apt-get install -y tor + - run: pip install pytest pyyaml + - run: LIM_E2E_TOR=1 make test-tor PYTHON=python diff --git a/tests/e2e/test_tor_unlock_e2e.py b/tests/e2e/test_tor_unlock_e2e.py index 33c3ad5..37f39f1 100644 --- a/tests/e2e/test_tor_unlock_e2e.py +++ b/tests/e2e/test_tor_unlock_e2e.py @@ -27,9 +27,17 @@ import pytest from lim.image import tor as tor_module from tests.e2e import tor_harness -pytestmark = pytest.mark.skipif( - os.environ.get("LIM_E2E_TOR") != "1" or shutil.which("tor") is None, - reason="needs LIM_E2E_TOR=1 and the tor binary (live network, slow).", +# The offline checks only need the tor binary (no network) and are +# deterministic, so they run in CI whenever tor is installed. Only the live +# onion round-trip needs the public Tor network, so it stays opt-in behind +# LIM_E2E_TOR=1 to keep an external, occasionally-flaky dependency out of the +# blocking gate. +_needs_tor = pytest.mark.skipif( + shutil.which("tor") is None, reason="needs the tor binary" +) +_needs_tor_network = pytest.mark.skipif( + shutil.which("tor") is None or os.environ.get("LIM_E2E_TOR") != "1", + reason="needs the tor binary and LIM_E2E_TOR=1 (live Tor network, slow)", ) PASSPHRASE = b"correct horse battery staple" @@ -51,6 +59,7 @@ def test_offline_keygen_matches_production_flags(): assert "--SocksPort 0" in script +@_needs_tor def test_onion_keygen_is_deterministic_and_offline(workdir): """Keys generate without network and the .onion address is stable.""" address = tor_harness.generate_onion_keys( @@ -62,6 +71,7 @@ def test_onion_keygen_is_deterministic_and_offline(workdir): assert (workdir / "onion" / name).is_file() +@_needs_tor_network def test_unlock_passphrase_travels_over_onion(workdir): """Full rootless round-trip: client -> Tor -> onion -> dropbear stand-in.""" onion_dir = workdir / "onion"