mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 09:40:40 +00:00
docs: add linux hosting validation runbook
This commit is contained in:
1 parent
2de7ea073c
commit
4e49762a85
6 files changed
+322
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
@@ -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)"
|
||||
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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 "------------------------------------------------------------------"
|
||||
Reference in new issue
Block a user