docs: add linux hosting validation runbook

This commit is contained in:
thePR0M3TH3AN committed 2026-03-19 16:24:22 -04:00
1 parent 2de7ea073c
commit 4e49762a85
6 files changed
+322

No files matched your search

+1
View File
@@ -14,6 +14,7 @@ Linux with systemd is the supported persistent-host deployment target.
Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey remain experimental until their background Tor hosting and auto-recovery paths are validated end-to-end.
See [docs/platform-support-matrix.md](docs/platform-support-matrix.md) for the canonical platform status table and [docs/roadmap-platform-parity.md](docs/roadmap-platform-parity.md) for the parity plan.
For the supported Linux hosting path, use [docs/linux-hosting-validation.md](docs/linux-hosting-validation.md) as the operational validation runbook.
### 1. Standalone Binaries (Recommended)
Download the executable for your OS:
+178
View File
@@ -0,0 +1,178 @@
# Linux Hosting Validation
This runbook defines the operational validation standard for VoxVera's only supported hosting target:
- Linux CLI with `systemd --user`
Everything else remains below that bar until equivalent evidence exists.
## Prerequisites
- `voxvera` installed and on `PATH`
- `tor` installed
- `onionshare-cli` available
- a user account that can run `systemctl --user`
## Quick Verification
Use the helper script first:
```bash
bash scripts/linux-recovery-check.sh
```
This checks:
- `platform-status --json`
- `doctor --json`
- `autostart status --json`
- expected Linux support tier and platform id
- presence of `~/host`
- Tor and OnionShare discovery
- autostart artifacts and timer state
- per-site state for `.onionshare-session`, `onionshare.log`, and `server.pid`
## Core Commands
Install autostart:
```bash
voxvera autostart
```
Inspect autostart:
```bash
voxvera autostart status
systemctl --user status voxvera-start.timer
systemctl --user status voxvera-start.service
```
Remove autostart:
```bash
voxvera autostart uninstall
```
Inspect runtime diagnostics:
```bash
voxvera doctor
voxvera doctor --json
```
## Validation Checklist
### 1. Fresh install
Run the Linux installer or install through the supported CLI path, then confirm:
```bash
voxvera platform-status --json
voxvera doctor --json
voxvera autostart status --json
```
Expected:
- `platform_id` is `linux_cli_systemd`
- support tier is `supported`
- `voxvera_cli`, `onionshare_cli`, and `tor_binary` pass
### 2. Build and serve
Create or edit a site, then build and serve it:
```bash
voxvera build
voxvera serve
```
Expected:
- site output appears under `~/host/<folder_name>/`
- `index.html` and QR code images exist
- `onionshare.log` is written
- `.onionshare-session` appears after the hidden service is started
### 3. Identity persistence
Confirm the site keeps its identity across rebuilds:
```bash
ls -la ~/host/<folder_name>/
voxvera build --config ~/host/<folder_name>/config.json
```
Expected:
- `.onionshare-session` still exists after rebuild
### 4. Autostart persistence
Confirm the recovery timer exists and is enabled:
```bash
voxvera autostart status
systemctl --user is-enabled voxvera-start.timer
systemctl --user is-active voxvera-start.timer
```
Expected:
- timer artifacts exist under `~/.config/systemd/user/`
- timer is enabled
### 5. Reboot recovery
After a reboot, verify:
```bash
bash scripts/linux-recovery-check.sh
systemctl --user status voxvera-start.timer
journalctl --user -u voxvera-start.service --no-pager -n 50
```
Expected:
- timer remains enabled
- service has retried `start-all`
- configured sites are restorable without manual reconfiguration
### 6. Offline/reconnect recovery
Temporarily disconnect the host from the network, reconnect, then wait through at least one timer interval.
Verify:
```bash
systemctl --user status voxvera-start.timer
journalctl --user -u voxvera-start.service --no-pager -n 50
bash scripts/linux-recovery-check.sh
```
Expected:
- timer is still active
- `start-all` retries appear in the service logs
- Tor becomes reachable again
## Paths That Matter
- `~/.config/systemd/user/voxvera-start.service`
- `~/.config/systemd/user/voxvera-start.timer`
- `~/host/<folder_name>/config.json`
- `~/host/<folder_name>/.onionshare-session`
- `~/host/<folder_name>/onionshare.log`
- `~/host/<folder_name>/server.pid`
## Failure Interpretation
If `bash scripts/linux-recovery-check.sh` fails:
- missing `onionshare_cli` or `tor_binary` means the runtime is incomplete
- missing `host_root` means no sites are built yet
- missing autostart artifacts means recovery was never installed or was removed
- `tor_socks_reachable` warning means Tor is not currently reachable even if it is installed
Linux should remain the only supported target until this runbook is repeatable on real hosts without caveats.
+3
View File
@@ -442,6 +442,9 @@ for strategy in install_tarball install_shallow install_pipx install_pip_git ins
msg "\nVoxVera installed/updated successfully. Run 'voxvera check' to verify."
if [[ "$OS_NAME" == linux* ]]; then
msg "Linux systemd user autostart should now retry hidden-service startup every few minutes."
msg "Inspect with: voxvera autostart status"
msg "Remove with: voxvera autostart uninstall"
msg "Validate with: bash scripts/linux-recovery-check.sh"
fi
# Detect if the parent shell will still have a stale hash
msg "If 'voxvera' is not found, run: hash -r (or open a new terminal)"
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
if [[ "$(uname -s)" != "Linux" ]]; then
echo "linux-recovery-check.sh is only for Linux hosts." >&2
exit 1
fi
command -v voxvera >/dev/null 2>&1 || {
echo "voxvera is not on PATH" >&2
exit 1
}
TMP_DIR="${VOXVERA_RECOVERY_TMPDIR:-$(mktemp -d /tmp/voxvera-linux-recovery-XXXXXX)}"
cleanup() {
if [[ -z "${VOXVERA_RECOVERY_TMPDIR:-}" ]]; then
rm -rf "$TMP_DIR"
fi
}
trap cleanup EXIT
PLATFORM_JSON="$TMP_DIR/platform-status.json"
DOCTOR_JSON="$TMP_DIR/doctor.json"
AUTOSTART_JSON="$TMP_DIR/autostart-status.json"
voxvera platform-status --json >"$PLATFORM_JSON"
voxvera doctor --json >"$DOCTOR_JSON"
voxvera autostart status --json >"$AUTOSTART_JSON"
PLATFORM_JSON="$PLATFORM_JSON" \
DOCTOR_JSON="$DOCTOR_JSON" \
AUTOSTART_JSON="$AUTOSTART_JSON" \
python3 - <<'PY'
import json
import os
import sys
from pathlib import Path
platform_status = json.loads(Path(os.environ["PLATFORM_JSON"]).read_text(encoding="utf-8"))
doctor = json.loads(Path(os.environ["DOCTOR_JSON"]).read_text(encoding="utf-8"))
autostart = json.loads(Path(os.environ["AUTOSTART_JSON"]).read_text(encoding="utf-8"))
errors = []
warnings = []
if platform_status.get("platform_id") != "linux_cli_systemd":
errors.append(f"expected linux_cli_systemd, got {platform_status.get('platform_id')}")
if platform_status.get("tier") != "supported":
errors.append(f"expected supported tier, got {platform_status.get('tier')}")
checks = {check["name"]: check for check in doctor.get("checks", [])}
required_ok = ["platform", "voxvera_cli", "onionshare_cli", "tor_binary", "host_root"]
for name in required_ok:
check = checks.get(name)
if not check:
errors.append(f"missing doctor check: {name}")
elif not check.get("ok"):
errors.append(f"{name} failed: {check.get('detail')}")
tor_check = checks.get("tor_socks_reachable")
if not tor_check:
errors.append("missing doctor check: tor_socks_reachable")
elif not tor_check.get("ok"):
warnings.append(f"tor_socks_reachable: {tor_check.get('detail')}")
timer_check = next((check for check in autostart.get("checks", []) if check["name"] == "systemd_timer_enabled"), None)
if not timer_check:
errors.append("missing autostart check: systemd_timer_enabled")
elif not timer_check.get("ok"):
warnings.append(f"systemd_timer_enabled: {timer_check.get('detail')}")
artifacts = [Path(path) for path in autostart.get("artifacts", [])]
missing = [str(path) for path in artifacts if not path.exists()]
if missing:
warnings.append("missing autostart artifacts: " + ", ".join(missing))
host_root = Path.home() / "host"
sites = []
if host_root.exists():
for site_dir in sorted(path for path in host_root.iterdir() if path.is_dir()):
config = site_dir / "config.json"
if not config.exists():
continue
session = site_dir / ".onionshare-session"
log = site_dir / "onionshare.log"
pid = site_dir / "server.pid"
sites.append(
{
"name": site_dir.name,
"session": session.exists(),
"log": log.exists(),
"pid": pid.exists(),
}
)
if errors:
print("Linux recovery check failed:", file=sys.stderr)
for error in errors:
print(f"- {error}", file=sys.stderr)
sys.exit(1)
print(f"Platform: {platform_status['label']} [{platform_status['tier']}]")
print(f"Hosting model: {platform_status.get('hosting_model', '')}")
print(f"Autostart: {autostart.get('message', '')}")
if warnings:
print("Warnings:")
for warning in warnings:
print(f"- {warning}")
else:
print("Warnings: none")
if sites:
print("Site state:")
for site in sites:
print(
f"- {site['name']}: session={'yes' if site['session'] else 'no'}, "
f"log={'yes' if site['log'] else 'no'}, pid={'yes' if site['pid'] else 'no'}"
)
else:
print("Site state: no configured sites under ~/host")
PY
echo "Detailed JSON artifacts written to $TMP_DIR"
+11
View File
@@ -45,6 +45,17 @@ def test_platform_smoke_is_wired_into_validation_surfaces():
for target in ("linux-cli", "macos-cli", "windows-cli", "docker-cli"):
assert target in smoke_text
def test_linux_validation_runbook_and_helper_are_linked():
runbook = REPO_ROOT / "docs" / "linux-hosting-validation.md"
helper = REPO_ROOT / "scripts" / "linux-recovery-check.sh"
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
assert runbook.exists()
assert helper.exists()
assert "docs/linux-hosting-validation.md" in readme
assert "scripts/linux-recovery-check.sh" in runbook.read_text(encoding="utf-8")
def test_locale_completeness():
"""Verify that all non-English locales have the same keys as English."""
locales_dir = REPO_ROOT / "voxvera" / "locales"
+3
View File
@@ -206,4 +206,7 @@ msg " Linux is the supported persistent-host target."
msg " Other package/install paths remain experimental."
msg ""
msg " Run 'voxvera check' to verify your setup after refreshing."
msg " Run 'voxvera autostart status' to inspect the recovery timer."
msg " Run 'voxvera autostart uninstall' to remove the recovery timer."
msg " Run 'bash scripts/linux-recovery-check.sh' from the repo for the Linux validation checklist."
msg "------------------------------------------------------------------"