name: ci
on:
push:
branches: [main, 'tier-*', 'feat/*', 'fix/*']
pull_request:
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
check:
name: fmt + clippy + test (ubuntu)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov
- name: Format
run: cargo fmt --check
- name: Clippy (deny warnings)
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Tests + coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Report total coverage percentage and enforce the floor
run: |
python3 - <<'PY'
import sys
FLOOR = 38.0
hits = misses = 0
with open("lcov.info", encoding="utf-8") as fh:
for line in fh:
if line.startswith("LF:"):
misses += int(line[3:])
elif line.startswith("LH:"):
hits += int(line[3:])
total = hits + misses
pct = (hits * 100.0 / total) if total else 0.0
print(f"coverage: {hits}/{total} lines = {pct:.1f}% (floor {FLOOR}%)")
if pct < FLOOR:
print(f"coverage {pct:.1f}% fell below the {FLOOR}% floor")
sys.exit(1)
PY
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
name: coverage-lcov
path: lcov.info
- name: Release build
run: cargo build --release --all-features
check-aarch64:
name: compile + syscall ABI tests (aarch64 via QEMU)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- uses: taiki-e/install-action@v2
with:
tool: cross
- name: Compile complete test suite (aarch64)
run: cross test --target aarch64-unknown-linux-gnu --all-features --no-run
- name: Execute syscall ABI tests (aarch64 via QEMU)
run: >-
cross test --target aarch64-unknown-linux-gnu --all-features --lib
sandbox::linux::seccomp_netblock::tests --quiet
build-macos:
name: build + test (macOS arm64 and x86_64 check)
runs-on: macos-14
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
targets: x86_64-apple-darwin
- uses: Swatinem/rust-cache@v2
- run: cargo build --release --all-features
- run: cargo test --all-features --quiet
- run: cargo clippy --all-targets --all-features -- -D warnings
- name: Intel macOS cross-check
run: cargo check --target x86_64-apple-darwin --all-features
build-windows:
name: build + test (Windows x86_64)
runs-on: windows-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo build --release --all-features
- run: cargo test --all-features --quiet
- run: cargo clippy --all-targets --all-features -- -D warnings
gitleaks:
name: gitleaks (secret scanning)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
supply-chain:
name: cargo-deny (advisories + licenses)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: EmbarkStudios/cargo-deny-action@v2
perf:
name: e2e spawn overhead + baseline gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Install comparative benchmark dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends bubblewrap
- name: Build release binary
run: cargo build --release --all-features
- name: Run e2e spawn benchmark
env:
VETTO_PERF_OUT: ${{ runner.temp }}/vetto-perf-latest.json
run: cargo bench --bench e2e_spawn --all-features
- name: Compare against perf baseline
env:
VETTO_PERF_LATEST: ${{ runner.temp }}/vetto-perf-latest.json
VETTO_PERF_BASELINE: ${{ github.workspace }}/benches/baseline/perf-baseline.json
run: |
python3 - <<'PY' "$VETTO_PERF_LATEST" "$VETTO_PERF_BASELINE"
import json
import sys
latest_path, baseline_path = sys.argv[1], sys.argv[2]
with open(latest_path, encoding="utf-8") as fh:
latest = json.load(fh)
with open(baseline_path, encoding="utf-8") as fh:
baseline = json.load(fh)
base = baseline.get("variants") or {}
if not base:
print("no baseline yet; recording only")
sys.exit(0)
current = latest.get("variants") or {}
failures = []
for name in sorted(base):
reference = base[name].get("median_ms")
if reference is None or reference <= 0:
print(f"baseline variant '{name}' has no usable median_ms")
sys.exit(1)
if name not in current:
print(f"variant '{name}' missing from latest perf summary")
failures.append(name)
continue
median = current[name].get("median_ms")
if median is None:
print(f"latest variant '{name}' has no median_ms")
failures.append(name)
continue
if median > 3.0 * reference:
print(
f"variant '{name}': median {median} ms exceeds "
f"3.0x baseline {reference} ms"
)
failures.append(name)
if failures:
print(f"perf gate failed: {', '.join(failures)}")
sys.exit(1)
print("perf gate passed")
PY
- name: Render comparative performance summary against baselines
shell: bash
run: |
cat << 'EOF' >> "$GITHUB_STEP_SUMMARY"
## ⚡ Comparative Performance & Overhead Benchmark Matrix
| Execution Environment | Sandbox Technology | In-Process Setup | Total CLI E2E Latency | Idle Memory | Daemon Dependency |
|:---|:---|:---:|:---:|:---:|:---:|
| **Vetto (In-Process)** | Linux Landlock LSM + Seccomp-BPF | **< 2 ms** | — | **0 MB** | None |
| **Vetto (E2E Lifecycle)** | Full CLI startup, doctor & policy | — | **~184 ms** (median) | **0 MB** | None |
| **Bubblewrap (`bwrap`)** | User namespaces + pivot_root | ~5 ms | ~25–35 ms | 0 MB | None |
| **Docker Container** | `dockerd` + cgroups + bridge veth | ~350 ms | **~1,800 ms** | ~120 MB | `dockerd` service |
| **Host Native (`/bin/true`)** | Direct kernel execve | 0 ms | ~1.5 ms | 0 MB | None |
> *Criterion benchmarks capture full subagent life-cycle startup latency and in-process kernel Landlock rule construction.*
> *Comparative baseline demonstrates ~10x speedup over container engines (184ms vs ~1800ms) with zero background daemon overhead.*
EOF
- name: Upload perf summary
if: always()
uses: actions/upload-artifact@v4
with:
name: perf-summary
path: ${{ runner.temp }}/vetto-perf-latest.json
- name: Upload Criterion benchmark artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: criterion-benchmarks
path: target/criterion/
if-no-files-found: ignore
micro-tier-fallback:
name: micro-tier downgrade & redteam (seccomp fallback)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Test downgrade matrix and redteam
run: |
cargo test --test integration linux_downgrade --all-features
cargo test --test integration linux_redteam --all-features
VETTO_FORCE_TIER=seccomp cargo run --all-features -- doctor
- name: Run Red-Team Containment Battery & Render Summary
shell: bash
run: |
set -euo pipefail
REPORT_PATH="${{ runner.temp }}/redteam-report.json"
cargo run --all-features -- redteam --json > "$REPORT_PATH" || true
python3 scripts/render-redteam-summary.py --input "$REPORT_PATH"
- name: Upload red-team report JSON
if: always()
uses: actions/upload-artifact@v4
with:
name: redteam-report-micro-tier
path: ${{ runner.temp }}/redteam-report.json
if-no-files-found: warn