AkurAI Build
Menu

Public repository / 22da10d5eeb459769d6d21108a8ba1b363d5b313

AkurAI-VPN / .github/workflows/ci.yml

Latest change 50973c6c157ae8a57689d219b4327f683d24d4bb - feat(node): echo-peer + CI encrypted E2E on single macOS/Windows host by Claude Code

name: ci

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  # The cryptographic + transport + protocol core is pure-std and must build AND
  # pass its tests on every desktop OS — this is the cross-platform foundation a
  # macOS/Windows client is built on.
  portable-core:
    name: portable core (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: build core crates
        run: cargo build -p akurai-crypto -p akurai-transport -p akurai-common -p akurai-dns
      - name: test core crates
        run: cargo test -p akurai-crypto -p akurai-transport -p akurai-common -p akurai-dns

  # Full workspace (incl. control plane + relay + Linux node) on Linux.
  linux-full:
    name: linux full workspace
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt
      - run: cargo fmt --all --check
      - run: cargo clippy --workspace --all-targets
      - run: cargo build --workspace
      - run: cargo test --workspace

  # Desktop node — builds the client daemon on macOS + Windows on real runners.
  # macOS has a real utun data plane; Windows compiles (Wintun data plane pending).
  # This is now a hard gate: a regression in the cross-platform build fails CI.
  desktop-node:
    name: desktop node (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: build node (release)
        run: cargo build --release -p akurai-node
      # Publish the built client so it can be downloaded and runtime-tested on
      # real Mac/Windows hardware (CI runners can't create a TUN/utun to test it).
      - name: upload client binary
        uses: actions/upload-artifact@v4
        with:
          name: akurai-node-${{ matrix.os }}
          path: |
            target/release/akurai-node
            target/release/akurai-node.exe
          if-no-files-found: warn

  # Runtime smoke test: RUN the client on real macOS/Windows and prove the full
  # platform packet path. `selftest` brings up the utun/Wintun device and echoes
  # ICMP, so a real `ping` to an overlay IP (routed to the device) only succeeds
  # if BOTH TunDevice::recv and send move packets correctly on that OS — the part
  # most likely to compile yet fail at runtime.
  runtime-smoke:
    name: runtime smoke (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: build node (release)
        run: cargo build --release -p akurai-node
      - name: macOS — utun recv/send via ping echo
        if: runner.os == 'macOS'
        run: |
          BIN=./target/release/akurai-node
          "$BIN" install --home /tmp/akv
          sudo "$BIN" selftest --home /tmp/akv --overlay-ip 100.88.0.3 --secs 18 > smoke.log 2>&1 &
          PID=$!
          sleep 5
          echo "--- node output ---"; cat smoke.log
          echo "--- ping an overlay IP (routed to utun; selftest must echo it) ---"
          if ping -c 3 -t 6 100.88.0.99; then RESULT=0; else RESULT=1; fi
          sudo kill "$PID" 2>/dev/null || true
          echo "--- node output after ping ---"; cat smoke.log
          [ $RESULT -eq 0 ] && echo "PASS: macOS utun recv+send move packets" \
            || { echo "FAIL: no ICMP echo over utun"; exit 1; }
      - name: Windows — Wintun recv/send via ping echo
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          Invoke-WebRequest -Uri "https://www.wintun.net/builds/wintun-0.14.1.zip" -OutFile wintun.zip
          Expand-Archive wintun.zip -DestinationPath wintun-extract -Force
          Copy-Item wintun-extract/wintun/bin/amd64/wintun.dll target/release/wintun.dll
          $home2 = Join-Path $env:TEMP akv
          ./target/release/akurai-node.exe install --home $home2
          $p = Start-Process -FilePath ./target/release/akurai-node.exe `
            -ArgumentList "selftest","--home",$home2,"--overlay-ip","100.88.0.3","--secs","18" `
            -RedirectStandardError smoke.log -RedirectStandardOutput smoke.out -PassThru -NoNewWindow
          Start-Sleep -Seconds 6
          Get-Content smoke.log -ErrorAction SilentlyContinue
          Write-Host "--- ping an overlay IP (routed to Wintun; selftest must echo it) ---"
          ping -n 3 -w 3000 100.88.0.99
          $ok = ($LASTEXITCODE -eq 0)
          Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
          Get-Content smoke.log -ErrorAction SilentlyContinue
          if ($ok) { Write-Host "PASS: Windows Wintun recv+send move packets" }
          else { Write-Host "FAIL: no ICMP echo over Wintun"; exit 1 }

  # Full ENCRYPTED end-to-end on a single real macOS/Windows host: a relay + a
  # real `tunnel` node A (utun/Wintun) + a transport-level `echo-peer` node B
  # (no TUN, so its overlay IP is never local → no kernel short-circuit). A ping
  # to node B traverses A's full data plane (utun → Noise-encrypt → relay →
  # decrypt by B → encrypted echo → A's utun). A reply proves the whole encrypted
  # client path works at runtime on that OS.
  e2e-desktop:
    name: e2e encrypted (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: build node + relay (release)
        run: cargo build --release -p akurai-node -p akurai-relay
      - name: macOS — encrypted ping A→B over the relay
        if: runner.os == 'macOS'
        run: |
          BIN=./target/release/akurai-node
          RELAY=./target/release/akurai-relay
          "$BIN" install --home /tmp/a; "$BIN" install --home /tmp/b
          APUB=$(cat /tmp/a/config/identity.pub); BPUB=$(cat /tmp/b/config/identity.pub)
          echo "100.88.0.4 $BPUB nodeb" > /tmp/a/config/peers
          echo "100.88.0.3 $APUB nodea" > /tmp/b/config/peers
          RELAY_PORT=51820 "$RELAY" serve > relay.log 2>&1 &
          sleep 1
          "$BIN" echo-peer --home /tmp/b --overlay-ip 100.88.0.4 --relay 127.0.0.1:51820 --peers /tmp/b/config/peers --secs 40 > b.log 2>&1 &
          sudo "$BIN" tunnel --home /tmp/a --overlay-ip 100.88.0.3 --relay 127.0.0.1:51820 --peers /tmp/a/config/peers > a.log 2>&1 &
          APID=$!
          sleep 6
          echo "--- node A ---"; cat a.log; echo "--- echo-peer B ---"; cat b.log
          echo "--- ENCRYPTED ping A→B (100.88.0.4) over the relay ---"
          if ping -c 5 -t 10 100.88.0.4; then RESULT=0; else RESULT=1; fi
          sudo kill "$APID" 2>/dev/null || true
          echo "--- echo-peer B after ping ---"; cat b.log
          [ $RESULT -eq 0 ] && echo "PASS: macOS encrypted end-to-end works" \
            || { echo "FAIL: no encrypted round-trip"; cat relay.log; exit 1; }
      - name: Windows — encrypted ping A→B over the relay
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          Invoke-WebRequest -Uri "https://www.wintun.net/builds/wintun-0.14.1.zip" -OutFile wintun.zip
          Expand-Archive wintun.zip -DestinationPath wintun-extract -Force
          Copy-Item wintun-extract/wintun/bin/amd64/wintun.dll target/release/wintun.dll
          $BIN = "./target/release/akurai-node.exe"
          & $BIN install --home C:\a; & $BIN install --home C:\b
          $apub = Get-Content C:\a\config\identity.pub; $bpub = Get-Content C:\b\config\identity.pub
          "100.88.0.4 $bpub nodeb" | Out-File -Encoding ascii C:\a\config\peers
          "100.88.0.3 $apub nodea" | Out-File -Encoding ascii C:\b\config\peers
          $env:RELAY_PORT="51820"
          Start-Process ./target/release/akurai-relay.exe -ArgumentList "serve" -RedirectStandardError relay.log -PassThru -NoNewWindow | Out-Null
          Start-Sleep 1
          Start-Process $BIN -ArgumentList "echo-peer","--home","C:\b","--overlay-ip","100.88.0.4","--relay","127.0.0.1:51820","--peers","C:\b\config\peers","--secs","40" -RedirectStandardError b.log -PassThru -NoNewWindow | Out-Null
          $pa = Start-Process $BIN -ArgumentList "tunnel","--home","C:\a","--overlay-ip","100.88.0.3","--relay","127.0.0.1:51820","--peers","C:\a\config\peers" -RedirectStandardError a.log -PassThru -NoNewWindow
          Start-Sleep 7
          Write-Host "--- node A ---"; Get-Content a.log -ErrorAction SilentlyContinue
          Write-Host "--- echo-peer B ---"; Get-Content b.log -ErrorAction SilentlyContinue
          Write-Host "--- ENCRYPTED ping A->B (100.88.0.4) over the relay ---"
          ping -n 5 -w 3000 100.88.0.4
          $ok = ($LASTEXITCODE -eq 0)
          Stop-Process -Id $pa.Id -Force -ErrorAction SilentlyContinue
          if ($ok) { Write-Host "PASS: Windows encrypted end-to-end works" }
          else { Write-Host "FAIL: no encrypted round-trip"; Get-Content relay.log -ErrorAction SilentlyContinue; exit 1 }