Skip to content

Commit 5c3991f

Browse files
committed
Add sandbox launcher
1 parent 50f9e46 commit 5c3991f

8 files changed

Lines changed: 193 additions & 13 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ All CI jobs are defined in [`.github/workflows/ci.yml`](.github/workflows/ci.yml
316316
| Job | Workflow Link | What It Proves |
317317
|-----|--------------|---------------|
318318
| `go-build-and-test` | [View job](https://github.com/SecAI-Hub/SecAI_OS/actions/workflows/ci.yml) | 428 Go tests across 9 services with `-race` (build, test, vet) |
319-
| `python-test` | [View job](https://github.com/SecAI-Hub/SecAI_OS/actions/workflows/ci.yml) | 1,132 Python tests (unit/integration + adversarial/acceptance), ruff lint, bandit security scan (enforced on HIGH/HIGH), mypy type checking |
319+
| `python-test` | [View job](https://github.com/SecAI-Hub/SecAI_OS/actions/workflows/ci.yml) | 1,133 Python tests (unit/integration + adversarial/acceptance), ruff lint, bandit security scan (enforced on HIGH/HIGH), mypy type checking |
320320
| `appsec-lint` | [View job](https://github.com/SecAI-Hub/SecAI_OS/actions/workflows/ci.yml) | Hadolint for container build files and Semgrep project security rules |
321321
| `security-regression` | [View job](https://github.com/SecAI-Hub/SecAI_OS/actions/workflows/ci.yml) | Adversarial test suite: prompt injection, policy bypass, containment, recovery |
322322
| `supply-chain-verify` | [View job](https://github.com/SecAI-Hub/SecAI_OS/actions/workflows/ci.yml) | SBOM generation via Syft, cosign availability, provenance keywords in release/build workflows |
@@ -338,7 +338,7 @@ All CI jobs are defined in [`.github/workflows/ci.yml`](.github/workflows/ci.yml
338338
| [API Reference](docs/api.md) | HTTP API for all services |
339339
| [Policy Schema](docs/policy-schema.md) | Full policy.yaml schema reference |
340340
| [Security Status](docs/security-status.md) | Implementation status of all 54 milestones |
341-
| [Test Matrix](docs/test-matrix.md) | Test coverage: 1,560 tests across Go and Python (see [test-counts.json](docs/test-counts.json)) |
341+
| [Test Matrix](docs/test-matrix.md) | Test coverage: 1,561 tests across Go and Python (see [test-counts.json](docs/test-counts.json)) |
342342
| [Compatibility Matrix](docs/compatibility-matrix.md) | GPU, VM, and hardware support |
343343
| [Security Test Matrix](docs/security-test-matrix.md) | Security feature test coverage |
344344
| [FAQ](docs/faq.md) | Common questions |
@@ -462,7 +462,7 @@ for svc in airlock registry tool-firewall gpu-integrity-watch mcp-firewall \
462462
(cd services/$svc && go test -v -race ./...)
463463
done
464464

465-
# Python tests (1,132 total)
465+
# Python tests (1,133 total)
466466
python -m pip install -r requirements-ci.txt
467467
PYTHONPATH=services python -m pytest tests/ -v
468468

@@ -564,7 +564,7 @@ services/
564564
search-mediator/ Python -- Tor-routed web search (:8485)
565565
ui/ Python/Flask -- Web UI (:8480)
566566
common/ Python -- Shared utilities (audit, auth, mlock)
567-
tests/ 1,132 Python tests, 428 Go tests (1,560 total)
567+
tests/ 1,133 Python tests, 428 Go tests (1,561 total)
568568
docs/ Architecture, API, threat model, install guides
569569
schemas/ OpenAPI spec, JSON Schema for config files
570570
examples/ Task-oriented walkthroughs

docs/install/sandbox.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ Treat the host OS, container runtime, and anyone with host admin access as fully
3434

3535
## Start The Stack
3636

37+
**Windows (one-command launcher from the repo root)**
38+
39+
```powershell
40+
.\secai-sandbox.cmd start
41+
.\secai-sandbox.cmd open
42+
```
43+
3744
**Linux / macOS**
3845

3946
```bash
@@ -123,6 +130,12 @@ bash scripts/sandbox/start.sh --with-search --with-airlock --with-inference
123130

124131
## Stop The Stack
125132

133+
**Windows (one-command launcher from the repo root)**
134+
135+
```powershell
136+
.\secai-sandbox.cmd stop
137+
```
138+
126139
**Linux / macOS**
127140

128141
```bash

docs/security-status.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ All M5 security assurance criteria are met. The controls below have been impleme
1919
| Tool Firewall, default-deny policy | Implemented | M4 | Go tool-firewall service on :8475, default-deny egress |
2020
| Online Airlock, sanitization | Implemented | M5 | Go airlock service on :8490, disabled by default (privacy risk) |
2121
| Systemd sandboxing, kernel hardening, nftables | Implemented | M6 | Systemd unit hardening, sysctl tuning, nftables rules |
22-
| CI/CD, Go/Python tests, shellcheck | Implemented | M7 | GitHub Actions ci.yml. See docs/test-counts.json for current counts (428 Go, 1132 Python as of 2026-04-29) |
22+
| CI/CD, Go/Python tests, shellcheck | Implemented | M7 | GitHub Actions ci.yml. See docs/test-counts.json for current counts (428 Go, 1133 Python as of 2026-04-29) |
2323
| Image/video generation, diffusion worker | Implemented | M8 | Diffusion worker for image generation workloads |
2424
| Multi-GPU support (NVIDIA/AMD/Intel/Apple) | Implemented | M9 | CUDA, ROCm/HIP, XPU/Vulkan, Metal/MPS backends |
2525
| Tor-routed search, SearXNG, PII stripping | Implemented | M10 | Search mediator with Tor routing and PII redaction |
@@ -60,7 +60,7 @@ All M5 security assurance criteria are met. The controls below have been impleme
6060
| Production readiness hardening | Implemented | M45 | Incident recorder file-backed persistence (survives restarts), graceful shutdown (SIGTERM/SIGINT with connection draining) for all 9 Go services, HTTP server timeouts for mcp-firewall and gpu-integrity-watch, systemd production hardening (TimeoutStartSec, TimeoutStopSec, StartLimitInterval, StartLimitBurst) for all 12 daemon units, first-boot health validation script, audit log rotation via logrotate, CI dependency vulnerability scanning (govulncheck + pip-audit), production operations guide (upgrade, key rotation, capacity limits, monitoring) |
6161
| Operational maturity | Implemented | M46 | Bootstrap trust gap fix (cosign verify before unverified rebase, documented trust gap rationale), CI runs on all changes (removed blanket paths-ignore for .md files), Python quality gates (ruff lint + bandit security scan + split test suites into unit/integration and adversarial/acceptance), docs-validation CI job (broken link detection, required docs check, test-counts.json validation), production-readiness checklist (formal release gate), SLOs (availability/latency/correctness targets + alerting thresholds), release channel policy (stable/candidate/dev + versioning + upgrade paths + security patch SLA), support lifecycle (hardware matrix, driver versions, support windows, deprecation policy, scope boundaries), CI evidence table with current job descriptions and workflow links, sample verification output for verify-release.sh |
6262
| CI enforcement hardening | Implemented | M47 | Enforced vulnerability scanning: bandit fails CI on HIGH-severity/HIGH-confidence findings, govulncheck fails on unwaived Go vulns, pip-audit fails on unwaived Python vulns. Waiver mechanism (`.github/vuln-waivers.json`) with mandatory expiry dates for reviewed/accepted findings. mypy type checking gate for security-sensitive services (common, agent, quarantine, ui). Pinned reproducible Python CI dependencies (`requirements-ci.txt`). Go 1.23->1.25 upgrade fixing 12 stdlib CVEs (crypto/tls, crypto/x509, encoding/asn1, net/url, os). Flask 3.1.1->3.1.3 (GHSA-68rp-wp8r-4726). Verification-first bootstrap documentation (signed rebase as default quickstart, unverified bootstrap moved to labeled recovery section). |
63-
| Production hardening | Implemented | M48 | Build script fail-closed for required services, quarantine scanners, search mediator, and signing policy material; final binary verification gate; incident store fsync (f.Sync() before close on both incident persistence and audit log writes); GPU backend metadata recording (`/etc/secure-ai/gpu-backend.json` written at build time with backend/version/timestamp); llama-server watchdog (Type=notify wrapper with startup health gate + WatchdogSec=30 continuous monitoring); model catalog externalization (`/etc/secure-ai/model-catalog.yaml` with YAML loading + hardcoded fallback); circuit breaker for Python services; post-upgrade model verification in Greenboot; cosign key rotation documentation. Current automated suite: 428 Go + 1132 Python tests (1,560 total). |
63+
| Production hardening | Implemented | M48 | Build script fail-closed for required services, quarantine scanners, search mediator, and signing policy material; final binary verification gate; incident store fsync (f.Sync() before close on both incident persistence and audit log writes); GPU backend metadata recording (`/etc/secure-ai/gpu-backend.json` written at build time with backend/version/timestamp); llama-server watchdog (Type=notify wrapper with startup health gate + WatchdogSec=30 continuous monitoring); model catalog externalization (`/etc/secure-ai/model-catalog.yaml` with YAML loading + hardcoded fallback); circuit breaker for Python services; post-upgrade model verification in Greenboot; cosign key rotation documentation. Current automated suite: 428 Go + 1133 Python tests (1,561 total). |
6464
| Signed-first install path | Implemented | M49 | Signed bootstrap script (`secai-bootstrap.sh`) configures container signing policy (policy.json + registries.d + cosign public key) before first rebase -- eliminates unverified transport from production install path. Digest-pinned install flow (CI publishes image digest in build summary and release assets). First-boot setup wizard (interactive verification of image integrity, transport, vault setup, TPM2 sealing, health check). Signing policy files baked into OS image (`/etc/pki/containers/secai-cosign.pub`, `/etc/containers/registries.d/secai-os.yaml`, policy.json merge in build script). Recovery/dev bootstrap path separated into dedicated doc with clear warnings. |
6565
| Production operations package | Implemented | M50 | Backup script (`secai-backup.sh`) with full/config/logs/keys categories, age/gpg encryption, internal SHA256 manifest, LUKS header backup. Restore script (`secai-restore.sh`) with integrity verification, staging extraction, double-confirmation LUKS header restore, post-restore health check. Production operations doc extended with rollback decision matrix (Greenboot auto-rollback triggers + manual criteria), 5 break-glass recovery procedures (token loss, attestation failure, Level 1 panic lockout, signing policy break, Greenboot exhaustion), formal data retention policy (7 data classes with retention periods, disk capacity thresholds at 70/80/90/95%). |
6666
| Stronger observability | Implemented | M51 | Unified appliance health dashboard (trusted/degraded/recovery_required state derived from runtime attestor + integrity monitor + incident recorder). Live SLO compliance monitoring (in-process tracker measuring uptime % and P95 latency against docs/slos.md targets, 7-day rolling window). Webhook alerting hooks for containment events (fire-and-forget POST with retry, configurable per-event-type filtering in incident-containment.yaml). Forensic bundle export wired to HTTP mux (was implemented but unregistered), enriched with real audit log entries and policy digest, accessible via UI download button, Flask proxy, and CLI script (`secai-forensic.sh`). Recovery ceremony endpoints also wired (ack, reattest, status). |

docs/security-test-matrix.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ Last updated: 2026-04-29
7878

7979
| Language | Current Automated Tests | Source of Truth |
8080
|----------|--------------------------|-----------------|
81-
| Python | 1132 | `docs/test-counts.json` and `pytest --collect-only` |
81+
| Python | 1133 | `docs/test-counts.json` and `pytest --collect-only` |
8282
| Go | 428 | `docs/test-counts.json` and `go test -v -count=1 ./...` |
83-
| **Total** | **1560** | Enforced by `.github/scripts/check-test-counts.sh` |
83+
| **Total** | **1561** | Enforced by `.github/scripts/check-test-counts.sh` |
8484

8585
Security coverage overlaps heavily with functional coverage, so the feature tables above use exact file or service totals rather than attempting to split each test into exclusive "security" and "non-security" buckets.
8686

docs/test-counts.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
"incident-recorder": 97
1313
},
1414
"go_total": 428,
15-
"python_total": 1132,
16-
"grand_total": 1560
15+
"python_total": 1133,
16+
"grand_total": 1561
1717
}

docs/test-matrix.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Last updated: 2026-04-29
1212
| Language | Test Count | Runner |
1313
|----------|-----------|--------|
1414
| Go | 428 | `go test -race ./...` |
15-
| Python | 1132 | `pytest` |
15+
| Python | 1133 | `pytest` |
1616
| Shell | CI-scoped scripts plus Makefile target for all repo shell scripts | `shellcheck` |
1717

1818
## Go Tests (428 total)
@@ -29,7 +29,7 @@ Last updated: 2026-04-29
2929
| Integrity Monitor | services/integrity-monitor/ | 50 | Baseline computation, continuous scanning, violation detection, state machine, HMAC baselines, incident-recorder integration |
3030
| Incident Recorder | services/incident-recorder/ | 97 | Incident creation, auto-containment, lifecycle management, severity ranking, policy loading, containment execution, enforcement chain integration, recovery ceremony, severity escalation, forensic bundle export (M43), persistence durability (fsync) |
3131

32-
## Python Tests (1132 total)
32+
## Python Tests (1133 total)
3333

3434
| Test File | Location | Tests | Description |
3535
|-----------|----------|-------|-------------|
@@ -59,7 +59,7 @@ Last updated: 2026-04-29
5959
| test_recipe_validation.py | tests/ | 26 | Recipe and packaged-file validation |
6060
| test_release_artifacts.py | tests/ | 52 | Release workflow, artifact manifest, and verification UX consistency |
6161
| test_sandbox.py | tests/ | 31 | Sandbox compose, policy, and runtime constraints |
62-
| test_sandbox_bundle.py | tests/ | 7 | Sandbox bundle and artifact checks |
62+
| test_sandbox_bundle.py | tests/ | 8 | Sandbox bundle and artifact checks |
6363
| test_search.py | tests/ | 36 | Search mediator, PII stripping, injection detection |
6464
| test_secure_boot.py | tests/ | 38 | Secure boot and measured boot behavior |
6565
| test_traffic_analysis.py | tests/ | 41 | Padding, timing jitter, dummy traffic generation |

secai-sandbox.cmd

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
@echo off
2+
setlocal EnableExtensions EnableDelayedExpansion
3+
4+
set "REPO_ROOT=%~dp0"
5+
set "START_SCRIPT=%REPO_ROOT%scripts\sandbox\start.ps1"
6+
set "STOP_SCRIPT=%REPO_ROOT%scripts\sandbox\stop.ps1"
7+
set "COMPOSE_FILE=%REPO_ROOT%deploy\sandbox\compose.yaml"
8+
set "ENV_FILE=%REPO_ROOT%deploy\sandbox\.env"
9+
10+
set "ACTION=%~1"
11+
if "%ACTION%"=="" set "ACTION=start"
12+
if /I "%ACTION%"=="help" goto help
13+
if "%ACTION%"=="-h" goto help
14+
if "%ACTION%"=="--help" goto help
15+
16+
if /I "%ACTION%"=="start" (
17+
shift
18+
goto start_stack
19+
)
20+
if /I "%ACTION%"=="up" (
21+
shift
22+
goto start_stack
23+
)
24+
if /I "%ACTION%"=="stop" goto stop_stack
25+
if /I "%ACTION%"=="down" goto stop_stack
26+
if /I "%ACTION%"=="restart" goto restart_stack
27+
if /I "%ACTION%"=="status" goto status_stack
28+
if /I "%ACTION%"=="ps" goto status_stack
29+
if /I "%ACTION%"=="logs" goto logs_stack
30+
if /I "%ACTION%"=="open" goto open_ui
31+
32+
echo Unknown command: %ACTION%
33+
echo.
34+
goto help_error
35+
36+
:start_stack
37+
set "PS_ARGS="
38+
:parse_start_args
39+
if "%~1"=="" goto run_start
40+
if /I "%~1"=="--with-inference" (
41+
set "PS_ARGS=!PS_ARGS! -WithInference"
42+
shift
43+
goto parse_start_args
44+
)
45+
if /I "%~1"=="-WithInference" (
46+
set "PS_ARGS=!PS_ARGS! -WithInference"
47+
shift
48+
goto parse_start_args
49+
)
50+
if /I "%~1"=="--with-diffusion" (
51+
set "PS_ARGS=!PS_ARGS! -WithDiffusion"
52+
shift
53+
goto parse_start_args
54+
)
55+
if /I "%~1"=="-WithDiffusion" (
56+
set "PS_ARGS=!PS_ARGS! -WithDiffusion"
57+
shift
58+
goto parse_start_args
59+
)
60+
if /I "%~1"=="--with-search" (
61+
set "PS_ARGS=!PS_ARGS! -WithSearch"
62+
shift
63+
goto parse_start_args
64+
)
65+
if /I "%~1"=="-WithSearch" (
66+
set "PS_ARGS=!PS_ARGS! -WithSearch"
67+
shift
68+
goto parse_start_args
69+
)
70+
if /I "%~1"=="--with-airlock" (
71+
set "PS_ARGS=!PS_ARGS! -WithAirlock"
72+
shift
73+
goto parse_start_args
74+
)
75+
if /I "%~1"=="-WithAirlock" (
76+
set "PS_ARGS=!PS_ARGS! -WithAirlock"
77+
shift
78+
goto parse_start_args
79+
)
80+
echo Unknown start option: %~1
81+
exit /b 2
82+
83+
:run_start
84+
powershell -NoProfile -ExecutionPolicy Bypass -File "%START_SCRIPT%" !PS_ARGS!
85+
exit /b %ERRORLEVEL%
86+
87+
:stop_stack
88+
powershell -NoProfile -ExecutionPolicy Bypass -File "%STOP_SCRIPT%"
89+
exit /b %ERRORLEVEL%
90+
91+
:restart_stack
92+
call "%~f0" stop
93+
if errorlevel 1 exit /b %ERRORLEVEL%
94+
call "%~f0" start
95+
exit /b %ERRORLEVEL%
96+
97+
:status_stack
98+
where docker >nul 2>nul
99+
if errorlevel 1 (
100+
echo Docker was not found in PATH.
101+
exit /b 1
102+
)
103+
docker compose -f "%COMPOSE_FILE%" --profile search --profile llm --profile diffusion ps
104+
exit /b %ERRORLEVEL%
105+
106+
:logs_stack
107+
where docker >nul 2>nul
108+
if errorlevel 1 (
109+
echo Docker was not found in PATH.
110+
exit /b 1
111+
)
112+
docker compose -f "%COMPOSE_FILE%" --profile search --profile llm --profile diffusion logs -f --tail=100
113+
exit /b %ERRORLEVEL%
114+
115+
:open_ui
116+
set "UI_PORT=8480"
117+
if exist "%ENV_FILE%" (
118+
for /f "tokens=1,* delims==" %%A in ('findstr /R "^SECAI_UI_PORT=" "%ENV_FILE%"') do set "UI_PORT=%%B"
119+
)
120+
start "" "http://127.0.0.1:%UI_PORT%"
121+
exit /b 0
122+
123+
:help
124+
echo SecAI OS Docker sandbox launcher
125+
echo.
126+
echo Usage:
127+
echo secai-sandbox.cmd [command] [options]
128+
echo.
129+
echo Commands:
130+
echo start Build and start the sandbox stack ^(default^)
131+
echo stop Stop the sandbox stack
132+
echo restart Stop, then start the sandbox stack
133+
echo status Show container status
134+
echo logs Follow sandbox logs
135+
echo open Open the UI in your default browser
136+
echo help Show this help
137+
echo.
138+
echo Start options:
139+
echo --with-search Enable Tor and SearXNG search sidecars
140+
echo --with-airlock Enable airlock policy in sandbox mode
141+
echo --with-inference Enable local LLM inference profile
142+
echo --with-diffusion Enable diffusion worker profile
143+
echo.
144+
echo UI:
145+
echo http://127.0.0.1:8480
146+
exit /b 0
147+
148+
:help_error
149+
call "%~f0" help
150+
exit /b 2

tests/test_sandbox_bundle.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def test_sandbox_bundle_has_docs_and_helpers():
8080
"scripts/sandbox/stop.sh",
8181
"scripts/sandbox/start.ps1",
8282
"scripts/sandbox/stop.ps1",
83+
"secai-sandbox.cmd",
8384
]:
8485
assert (REPO_ROOT / rel_path).exists()
8586

@@ -98,6 +99,22 @@ def test_sandbox_start_helpers_use_digest_pinned_alpine():
9899
assert "docker.io/library/alpine:3.20" not in powershell_helper
99100

100101

102+
def test_windows_sandbox_launcher_delegates_to_hardened_helpers():
103+
launcher = (REPO_ROOT / "secai-sandbox.cmd").read_text(encoding="utf-8")
104+
105+
assert "scripts\\sandbox\\start.ps1" in launcher
106+
assert "scripts\\sandbox\\stop.ps1" in launcher
107+
assert "-ExecutionPolicy Bypass" in launcher
108+
for option, ps_switch in {
109+
"--with-search": "-WithSearch",
110+
"--with-airlock": "-WithAirlock",
111+
"--with-inference": "-WithInference",
112+
"--with-diffusion": "-WithDiffusion",
113+
}.items():
114+
assert option in launcher
115+
assert ps_switch in launcher
116+
117+
101118
def test_sandbox_stop_helpers_include_optional_profiles():
102119
shell_helper = (REPO_ROOT / "scripts" / "sandbox" / "stop.sh").read_text(
103120
encoding="utf-8"

0 commit comments

Comments
 (0)