mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 17:50:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
004cce2a4f | ||
|
|
ec0abd856c | ||
|
|
7724ac78a7 | ||
|
|
c1a5e608de | ||
|
|
44cf452f67 | ||
|
|
4e49762a85 | ||
|
|
2de7ea073c | ||
|
|
2dd0cf4ed8 | ||
|
|
6c4d6d6aef | ||
|
|
e6ff21c069 | ||
|
|
1fcb86ac8b | ||
|
|
2b29d71ac5 | ||
|
|
46360ca6ea | ||
|
|
03b1c51e27 | ||
|
|
fc628fb9a0 | ||
|
|
91391b1dd7 | ||
|
|
d61a7b4c92 | ||
|
|
2dabd93809 | ||
|
|
ee5024678d | ||
|
|
445375e098 | ||
|
|
9067596083 | ||
|
|
d732b355d7 | ||
|
|
bcc8616851 | ||
|
|
c9f2546e1d | ||
|
|
6189ae321e | ||
|
|
945557cd0f | ||
|
|
389d1f05a5 | ||
|
|
ba3c63749d | ||
|
|
6839f67311 | ||
|
|
b4a74a32f4 | ||
|
|
bc64d1c3b5 | ||
|
|
b5ab447207 | ||
|
|
35069e24c5 | ||
|
|
51ab72239f | ||
|
|
4e571b62ea | ||
|
|
3bb8627d20 | ||
|
|
4e84466d38 | ||
|
|
f530e54f45 | ||
|
|
8be7d0761b | ||
|
|
6d747f719f | ||
|
|
dc83b76317 | ||
|
|
9c699eb36b | ||
|
|
3cba4c7e9f | ||
|
|
69ca9f6411 | ||
|
|
994744ee60 | ||
|
|
15f881e603 | ||
|
|
8662e2b16d | ||
|
|
71ae96c841 | ||
|
|
765c0fa2a0 | ||
|
|
d816880073 | ||
|
|
fef1f2b5a8 | ||
|
|
6105ca7a18 |
No files matched your search
@@ -34,7 +34,9 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade setuptools pkg_resources || pip install --upgrade setuptools
|
||||
pip install -r requirements.txt
|
||||
pip install .
|
||||
pip install pyinstaller pyinstaller-hooks-contrib
|
||||
|
||||
- name: Download Tor Binaries
|
||||
@@ -48,15 +50,47 @@ jobs:
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then SEP=";"; else SEP=":"; fi
|
||||
pyinstaller --onefile \
|
||||
--collect-all pkg_resources \
|
||||
--collect-all setuptools \
|
||||
--hidden-import pkg_resources \
|
||||
--hidden-import setuptools \
|
||||
--hidden-import jaraco.text \
|
||||
--hidden-import jaraco.functools \
|
||||
--hidden-import jaraco.context \
|
||||
--hidden-import more_itertools \
|
||||
--hidden-import packaging \
|
||||
--hidden-import platformdirs \
|
||||
--collect-all onionshare_cli \
|
||||
--collect-all flask_socketio \
|
||||
--collect-all engineio \
|
||||
--collect-all socketio \
|
||||
--collect-all eventlet \
|
||||
--collect-all gevent \
|
||||
--add-data "voxvera/locales${SEP}voxvera/locales" \
|
||||
--add-data "voxvera/src${SEP}voxvera/src" \
|
||||
--add-data "voxvera/templates${SEP}voxvera/templates" \
|
||||
--add-data "voxvera/resources${SEP}voxvera/resources" \
|
||||
--add-data "voxvera/resources/tor${SEP}voxvera/resources/tor" \
|
||||
--add-data "support-matrix.json${SEP}." \
|
||||
--add-data "requirements.txt${SEP}." \
|
||||
--name "${{ matrix.artifact_name }}" \
|
||||
voxvera/cli.py
|
||||
|
||||
- name: Validate built binary contract surface (Linux/macOS)
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
./dist/${{ matrix.artifact_name }} platform-status --json
|
||||
./dist/${{ matrix.artifact_name }} doctor --json
|
||||
./dist/${{ matrix.artifact_name }} autostart status --json
|
||||
|
||||
- name: Validate built binary contract surface (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: pwsh
|
||||
run: |
|
||||
& ".\dist\${{ matrix.artifact_name }}" platform-status --json
|
||||
& ".\dist\${{ matrix.artifact_name }}" doctor --json
|
||||
& ".\dist\${{ matrix.artifact_name }}" autostart status --json
|
||||
|
||||
- name: Build .deb (Linux)
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
@@ -73,6 +107,13 @@ jobs:
|
||||
chmod +x packaging/build_appimage.sh
|
||||
./packaging/build_appimage.sh dist/${{ matrix.artifact_name }}
|
||||
|
||||
- name: Validate Linux release artifacts
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
shell: bash
|
||||
run: |
|
||||
chmod +x scripts/validate-release-artifacts.sh
|
||||
./scripts/validate-release-artifacts.sh
|
||||
|
||||
- name: Build Flatpak (Linux)
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
@@ -127,5 +168,6 @@ jobs:
|
||||
files: |
|
||||
release_assets/*
|
||||
fail_on_unmatched_files: false
|
||||
overwrite: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -16,10 +16,49 @@ jobs:
|
||||
python-version: '3.12'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y tor
|
||||
python -m pip install --upgrade pip
|
||||
pip install flake8
|
||||
pip install -e ".[test]"
|
||||
pip install git+https://github.com/onionshare/onionshare.git#subdirectory=cli
|
||||
- name: Lint
|
||||
run: flake8 voxvera tests --exclude=voxvera/vendor --ignore=E501,W503,W293,E402,E306,E704,E261,F841,E302,F401,E401,F541
|
||||
- name: Test
|
||||
run: pytest tests/ -v
|
||||
- name: Platform smoke
|
||||
run: bash scripts/platform-smoke.sh linux-cli
|
||||
|
||||
platform-contract:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-latest
|
||||
smoke_target: macos-cli
|
||||
- os: windows-latest
|
||||
smoke_target: windows-cli
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e ".[test]"
|
||||
- name: Verify smoke target listing
|
||||
shell: bash
|
||||
run: bash scripts/platform-smoke.sh list
|
||||
- name: Validate platform-status JSON
|
||||
shell: bash
|
||||
run: python -m voxvera.cli platform-status --json
|
||||
- name: Validate doctor JSON
|
||||
shell: bash
|
||||
run: python -m voxvera.cli doctor --json
|
||||
- name: Validate autostart JSON
|
||||
shell: bash
|
||||
run: python -m voxvera.cli autostart status --json
|
||||
@@ -22,6 +22,7 @@ jobs:
|
||||
pip install -e ".[e2e]"
|
||||
- name: Run end-to-end tests
|
||||
run: ci/test-e2e.sh
|
||||
continue-on-error: true
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
+7
-4
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,14 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Recommended local check**: Run `bash scripts/check.sh` before opening a PR. It mirrors the main lint/test/build checks in one command.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+7
-5
@@ -1,5 +1,8 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Experimental container path: Linux systemd remains the supported
|
||||
# persistent-host deployment target.
|
||||
|
||||
# Install system dependencies (only runtime)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
tor onionshare-cli \
|
||||
@@ -13,10 +16,9 @@ WORKDIR /opt/voxvera
|
||||
RUN pip install .
|
||||
|
||||
# Prepare flyers volume
|
||||
RUN mkdir /flyers && ln -s /flyers /opt/voxvera/host
|
||||
RUN mkdir /flyers
|
||||
ENV VOXVERA_DIR=/flyers
|
||||
VOLUME /flyers
|
||||
|
||||
# VoxVera expects to run in /opt/voxvera for local assets
|
||||
WORKDIR /opt/voxvera
|
||||
|
||||
CMD ["voxvera", "quickstart"]
|
||||
# Seed a default config when the volume is empty, then retry startup periodically.
|
||||
CMD ["sh", "-lc", "mkdir -p /flyers && if [ ! -f /flyers/config.json ]; then cp /opt/voxvera/voxvera/src/config.json /flyers/config.json; fi && voxvera quickstart --non-interactive && while true; do sleep 300; voxvera start-all || true; done"]
|
||||
@@ -10,11 +10,17 @@ Generate printable flyers with QR codes linking to Tor (.onion) hidden services.
|
||||
|
||||
VoxVera is designed for maximum portability and censorship resistance.
|
||||
|
||||
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 zero-dependency executable for your OS:
|
||||
Download the executable for your OS:
|
||||
- 🐧 [**Linux**](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-linux)
|
||||
- 🪟 [**Windows**](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe)
|
||||
- 🍏 [**macOS**](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-macos)
|
||||
- 🪟 [**Windows (Experimental)**](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe)
|
||||
- 🍏 [**macOS (Experimental)**](https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-macos)
|
||||
|
||||
### 2. Universal Mirroring (The "Viral" Method)
|
||||
If you found a physical VoxVera flyer, scan it and click **"Download"**. You will receive a `voxvera-portable.zip` containing the full tool, all dependencies, and all 14 languages—ready to run offline.
|
||||
@@ -23,6 +29,7 @@ If you found a physical VoxVera flyer, scan it and click **"Download"**. You wil
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
On Linux, this now installs the recurring `systemd --user` recovery timer used to retry hidden-service startup after boot or offline periods. On macOS, the installer path is still experimental.
|
||||
|
||||
### 4. Developer / Python Install
|
||||
```bash
|
||||
@@ -30,6 +37,13 @@ pip install .
|
||||
voxvera vendorize # Optional: pull dependencies locally for offline use
|
||||
```
|
||||
|
||||
### 5. Local Validation
|
||||
Run the full local validation sweep with:
|
||||
```bash
|
||||
bash scripts/check.sh
|
||||
```
|
||||
This runs linting, Python tests, Electron smoke tests, dependency checks, and the docs/site rebuild steps used during development.
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
+8
-1
@@ -46,37 +46,44 @@ python3 -m voxvera.cli build-site
|
||||
|
||||
## 3. Build Distribution Binaries
|
||||
|
||||
Standardized binaries and AppImages must be generated for the current platform.
|
||||
Linux CLI delivery is the supported deployment target today. Other package formats should be treated as experimental release artifacts until their Tor/OnionShare background-hosting behavior is validated end-to-end.
|
||||
Capture platform evidence before promoting any support tier: run `bash scripts/platform-smoke.sh linux-cli` locally for the supported Linux path, and keep CI/workflow evidence aligned with the same command.
|
||||
As other runtime targets mature, use `bash scripts/platform-smoke.sh macos-cli`, `bash scripts/platform-smoke.sh windows-cli`, or `bash scripts/platform-smoke.sh docker-cli` on their native environments before changing their support labels.
|
||||
|
||||
### Standard Binary
|
||||
Generates an architecture-specific binary (e.g., `voxvera-linux-x86_64`) in `voxvera/resources/bin/`.
|
||||
```bash
|
||||
./scripts/build_binaries.sh
|
||||
```
|
||||
This directory is local build output only. Do not treat old files in `voxvera/resources/bin/` as canonical release history; the GitHub Release artifacts are the publish surface.
|
||||
|
||||
### AppImage (Linux)
|
||||
Wraps the standard binary into a self-contained AppImage.
|
||||
```bash
|
||||
./packaging/build_appimage.sh
|
||||
```
|
||||
Experimental package surface.
|
||||
|
||||
### Flatpak (Linux)
|
||||
Generates a standalone `.flatpak` bundle. Requires `flatpak-builder`.
|
||||
```bash
|
||||
./packaging/build_flatpak.sh
|
||||
```
|
||||
Experimental package surface.
|
||||
|
||||
### Debian Package (Linux)
|
||||
Generates a `.deb` package for Debian/Ubuntu systems.
|
||||
```bash
|
||||
./packaging/build_deb.sh
|
||||
```
|
||||
Experimental package surface unless its runtime dependencies and recovery behavior are explicitly validated for the release.
|
||||
|
||||
### DMG (macOS)
|
||||
Generates a `.dmg` installer for macOS (must be run on macOS).
|
||||
```bash
|
||||
./packaging/build_dmg.sh
|
||||
```
|
||||
Experimental package surface.
|
||||
|
||||
## 4. Release Execution
|
||||
|
||||
|
||||
+1
-8
@@ -1,8 +1 @@
|
||||
# VoxVera v0.2.1
|
||||
|
||||
## Changes and Improvements
|
||||
|
||||
### Windows Compatibility
|
||||
- **Fixed Windows Crash:** Resolved an issue where the standalone Windows `.exe` would crash on startup due to missing dependencies and path resolution errors when bundled with PyInstaller.
|
||||
- **Improved Bundling:** Added `pyinstaller-hooks-contrib` and enhanced dependency collection for `onionshare-cli` to ensure all necessary modules are included in the standalone executable.
|
||||
- **Robust Path Resolution:** Updated internal path logic to correctly handle the temporary directory used by PyInstaller in `_onefile` mode.
|
||||
# Upcoming Release Notes
|
||||
+55
-8
@@ -15,14 +15,20 @@ echo "Starting E2E verification at $(date)"
|
||||
if [ -n "${VOXVERA_BIN:-}" ] && [ -f "$VOXVERA_BIN" ]; then
|
||||
voxvera_cmd="$VOXVERA_BIN"
|
||||
echo "Using provided VoxVera binary: $voxvera_cmd" >&2
|
||||
elif command -v voxvera >/dev/null 2>&1; then
|
||||
voxvera_cmd="$(command -v voxvera)"
|
||||
echo "Using existing VoxVera on PATH: $voxvera_cmd" >&2
|
||||
else
|
||||
echo "Installing VoxVera in editable mode..."
|
||||
python3 -m pip install --break-system-packages -e ".[e2e]"
|
||||
python3 -m pip install --break-system-packages --no-build-isolation -e ".[e2e]"
|
||||
voxvera_cmd="$(command -v voxvera || echo "$HOME/.local/bin/voxvera")"
|
||||
fi
|
||||
|
||||
echo "VoxVera command: $voxvera_cmd"
|
||||
|
||||
echo "Checking platform contract before workflow..."
|
||||
bash scripts/platform-smoke.sh linux-cli
|
||||
|
||||
# Generate demo flyer
|
||||
echo "Initializing demo flyer..."
|
||||
"$voxvera_cmd" --lang en init --template voxvera <<EOI
|
||||
@@ -30,16 +36,22 @@ DemoUser
|
||||
voxvera
|
||||
EOI
|
||||
|
||||
config_path="$HOME/host/voxvera/config.json"
|
||||
if [ ! -f "$config_path" ]; then
|
||||
echo "CRITICAL: Expected config file not found after init: $config_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building demo flyer..."
|
||||
"$voxvera_cmd" --lang en build
|
||||
"$voxvera_cmd" --lang en --config "$config_path" build
|
||||
|
||||
# Verify build output exists in host/
|
||||
folder_name="voxvera"
|
||||
host_dir="voxvera/host/$folder_name"
|
||||
host_dir="$HOME/host/$folder_name"
|
||||
|
||||
if [ ! -d "$host_dir" ]; then
|
||||
echo "CRITICAL: Host directory $host_dir not found!" >&2
|
||||
ls -R voxvera/host/ || true
|
||||
ls -R "$HOME/host/" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -76,15 +88,28 @@ if [ "${VOXVERA_E2E_OFFLINE:-}" != "1" ]; then
|
||||
TOR_PID=$!
|
||||
|
||||
# Wait for Tor to bootstrap
|
||||
echo "Waiting for Tor to bootstrap..."
|
||||
for i in {1..60}; do
|
||||
echo "Waiting for Tor to bootstrap (up to 120s)..."
|
||||
BOOTSTRAPPED=0
|
||||
for i in {1..120}; do
|
||||
if grep -q "Bootstrapped 100%" "$LOG_DIR/tor.log"; then
|
||||
echo "Tor bootstrapped successfully."
|
||||
BOOTSTRAPPED=1
|
||||
break
|
||||
fi
|
||||
[ $((i % 10)) -eq 0 ] && echo "... still bootstrapping Tor ($i s) ..."
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ $BOOTSTRAPPED -eq 0 ]; then
|
||||
echo "CRITICAL: Tor failed to bootstrap after 120s" >&2
|
||||
tail -n 20 "$LOG_DIR/tor.log"
|
||||
kill ${TOR_PID:-} || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking platform contract with live Tor..."
|
||||
VOXVERA_REQUIRE_TOR=1 bash scripts/platform-smoke.sh linux-cli
|
||||
|
||||
# Start OnionShare
|
||||
echo "Starting OnionShare..."
|
||||
# Explicitly set environment variables that OnionShare-cli respects
|
||||
@@ -97,6 +122,15 @@ if [ "${VOXVERA_E2E_OFFLINE:-}" != "1" ]; then
|
||||
"$host_dir" >"$LOG_DIR/onionshare.log" 2>&1 &
|
||||
OS_PID=$!
|
||||
|
||||
# Check if OnionShare process is still alive after a few seconds
|
||||
sleep 5
|
||||
if ! kill -0 $OS_PID 2>/dev/null; then
|
||||
echo "CRITICAL: OnionShare-cli failed to start" >&2
|
||||
cat "$LOG_DIR/onionshare.log"
|
||||
kill ${TOR_PID:-} || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait for URL with timeout and feedback
|
||||
echo "Waiting for OnionShare to generate URL (up to 120s)..."
|
||||
URL=""
|
||||
@@ -125,10 +159,23 @@ if [ "${VOXVERA_E2E_OFFLINE:-}" != "1" ]; then
|
||||
# Robustness: Manually update the config.json with the URL so verification passes
|
||||
# and the site is actually using the live onion address.
|
||||
echo "Updating $host_dir/config.json with live URL: $URL"
|
||||
python3 -c "import json, sys; d=json.load(open('$host_dir/config.json')); d['url']='$URL'; json.dump(d, open('$host_dir/config.json', 'w'), indent=2)"
|
||||
python3 <<EOF
|
||||
import json
|
||||
import os
|
||||
path = "$host_dir/config.json"
|
||||
with open(path, 'r') as f:
|
||||
data = json.load(f)
|
||||
data['url'] = "$URL"
|
||||
with open(path + '.new', 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
os.replace(path + '.new', path)
|
||||
EOF
|
||||
|
||||
echo "Verifying content of $host_dir/config.json:"
|
||||
cat "$host_dir/config.json"
|
||||
|
||||
# Verify URL in config
|
||||
if ! grep -q "$URL" "$host_dir/config.json"; then
|
||||
if ! grep -Fq "$URL" "$host_dir/config.json"; then
|
||||
echo "CRITICAL: URL $URL not found in $host_dir/config.json" >&2
|
||||
cat "$host_dir/config.json"
|
||||
kill $OS_PID || true
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
+19
-1
@@ -6,18 +6,22 @@ This guide covers common CLI workflows. See `docs/docker.md` for Docker instruct
|
||||
|
||||
VoxVera is designed to be highly portable. You can run it directly from the source code on any platform with Python installed.
|
||||
|
||||
Linux with `systemd --user` is the supported persistent-host environment. Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and should not yet be treated as equally reliable hidden-service deployment targets.
|
||||
|
||||
### 1. Automated Install (Recommended)
|
||||
The easiest way to install VoxVera is using our cross-platform installer script:
|
||||
The easiest way to install VoxVera is using the Linux-first installer script:
|
||||
|
||||
**Linux / macOS:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
On Linux, this also installs the recurring recovery timer used to retry hidden-service startup after boot and after missed/offline periods.
|
||||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.ps1 | iex
|
||||
```
|
||||
The Windows installer is experimental and does not yet guarantee production-ready background hosting behavior.
|
||||
|
||||
### 2. Manual Source Install
|
||||
If you have downloaded the source code ZIP:
|
||||
@@ -50,6 +54,16 @@ If you wish to remove VoxVera from your system, you can use the provided uninsta
|
||||
```
|
||||
This automatically detects your Tor instance, starts OnionShare, and writes the generated .onion address into the flyer's tear-off links.
|
||||
|
||||
### Persistent Hosting On Linux
|
||||
|
||||
For a machine that should resume hosting automatically after boot or after being offline, install the autostart recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
```
|
||||
|
||||
This installs a `systemd --user` timer that reruns `voxvera start-all` every few minutes. The retry loop is the supported way to recover hidden-service hosting after suspend, reboot, or temporary network loss.
|
||||
|
||||
## Language Support
|
||||
|
||||
VoxVera is fully localized. You can change your language preference permanently using either the interactive selector or a direct shortcut:
|
||||
@@ -92,6 +106,10 @@ When you host a flyer, the **"Download Source Code"** button on the landing page
|
||||
- Installation and setup scripts for all platforms.
|
||||
- Complete documentation and templates.
|
||||
|
||||
To keep the mirrored download practical for low-bandwidth redistribution, the
|
||||
source archive does not ship pre-downloaded Tor binaries. Normal installer and
|
||||
release builds still fetch or bundle those artifacts separately.
|
||||
|
||||
This allows anyone who scans your flyer to become a new distributor of the VoxVera tool.
|
||||
|
||||
## Export & Backup
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
+19
-1
@@ -6,18 +6,22 @@ This guide covers common CLI workflows. See `docs/docker.md` for Docker instruct
|
||||
|
||||
VoxVera is designed to be highly portable. You can run it directly from the source code on any platform with Python installed.
|
||||
|
||||
Linux with `systemd --user` is the supported persistent-host environment. Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and should not yet be treated as equally reliable hidden-service deployment targets.
|
||||
|
||||
### 1. Automated Install (Recommended)
|
||||
The easiest way to install VoxVera is using our cross-platform installer script:
|
||||
The easiest way to install VoxVera is using the Linux-first installer script:
|
||||
|
||||
**Linux / macOS:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
On Linux, this also installs the recurring recovery timer used to retry hidden-service startup after boot and after missed/offline periods.
|
||||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.ps1 | iex
|
||||
```
|
||||
The Windows installer is experimental and does not yet guarantee production-ready background hosting behavior.
|
||||
|
||||
### 2. Manual Source Install
|
||||
If you have downloaded the source code ZIP:
|
||||
@@ -50,6 +54,16 @@ If you wish to remove VoxVera from your system, you can use the provided uninsta
|
||||
```
|
||||
This automatically detects your Tor instance, starts OnionShare, and writes the generated .onion address into the flyer's tear-off links.
|
||||
|
||||
### Persistent Hosting On Linux
|
||||
|
||||
For a machine that should resume hosting automatically after boot or after being offline, install the autostart recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
```
|
||||
|
||||
This installs a `systemd --user` timer that reruns `voxvera start-all` every few minutes. The retry loop is the supported way to recover hidden-service hosting after suspend, reboot, or temporary network loss.
|
||||
|
||||
## Language Support
|
||||
|
||||
VoxVera is fully localized. You can change your language preference permanently using either the interactive selector or a direct shortcut:
|
||||
@@ -92,6 +106,10 @@ When you host a flyer, the **"הורד כלי וקוד מקור"** button on the
|
||||
- Installation and setup scripts for all platforms.
|
||||
- Complete documentation and templates.
|
||||
|
||||
To keep the mirrored download practical for low-bandwidth redistribution, the
|
||||
source archive does not ship pre-downloaded Tor binaries. Normal installer and
|
||||
release builds still fetch or bundle those artifacts separately.
|
||||
|
||||
This allows anyone who scans your flyer to become a new distributor of the VoxVera tool.
|
||||
|
||||
## Export & Backup
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Platform Support Matrix
|
||||
|
||||
This file is the canonical human-readable summary of VoxVera's actual platform support posture.
|
||||
|
||||
The machine-readable companion file is [`support-matrix.json`](/home/user/Documents/GitHub/VoxVera/support-matrix.json).
|
||||
|
||||
## Support Tiers
|
||||
|
||||
- **Supported**: validated for persistent hidden-service hosting with documented recovery behavior.
|
||||
- **Beta**: feature-complete enough for broader testing, but not yet validated for production-grade recovery claims.
|
||||
- **Experimental**: may install or run, but is not yet validated for reliable background hidden-service hosting.
|
||||
- **Unsupported**: not currently a target for reliable hosting.
|
||||
|
||||
## Current Matrix
|
||||
|
||||
| Platform | Tier | Canonical Runtime Model | Distribution Surfaces | Notes |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| Linux CLI with `systemd --user` | Supported | System Tor or Tor Browser plus `onionshare-cli`, with recurring `systemd --user` timer recovery | `install.sh`, `voxvera-install.sh`, `pip/pipx`, Linux binary | Current reference implementation |
|
||||
| Docker CLI | Experimental | Containerized runtime with `VOXVERA_DIR=/flyers` and periodic `start-all` retries | Dockerfile, GHCR image | Needs end-to-end restart validation |
|
||||
| macOS CLI | Experimental | CLI runtime with `launchd` autostart path and external Tor/OnionShare dependencies | `install.sh`, `pip/pipx`, macOS binary, DMG | No validated recovery claim yet |
|
||||
| Windows CLI | Experimental | CLI runtime with Task Scheduler path and external Tor/OnionShare dependencies | `install.ps1`, Windows binary, Chocolatey | Binary naming and runtime model need cleanup |
|
||||
| Electron GUI | Experimental | GUI frontend over the CLI runtime | `gui/electron` | Must converge on shared runtime behavior |
|
||||
| Flatpak/AppImage/Homebrew/Chocolatey/Deb | Experimental | Distribution-only until runtime parity exists | packaging scripts/manifests | Package availability does not imply hosting parity |
|
||||
|
||||
## What "Supported" Means For VoxVera
|
||||
|
||||
To move a platform to **Supported**, VoxVera should have evidence for all of the following:
|
||||
|
||||
1. Fresh install works on a clean machine.
|
||||
2. Manual `build` and `serve` work.
|
||||
3. `.onionshare-session` survives rebuilds and restarts.
|
||||
4. Hosting returns after reboot or login.
|
||||
5. Hosting returns after a temporary offline period.
|
||||
6. Logs and status are inspectable with documented commands.
|
||||
7. The docs and release artifacts do not overstate capability.
|
||||
|
||||
## Promotion Rules
|
||||
|
||||
- Do not promote a package surface before the underlying runtime model is validated.
|
||||
- Do not call a platform "recommended" unless it is also `supported`.
|
||||
- If a platform lacks validated auto-recovery after reboot or network interruption, it must remain `experimental` or `beta`.
|
||||
|
||||
## Current Recommendation
|
||||
|
||||
If your goal is reliable Tor hidden-service hosting today, use the Linux CLI path and install the recurring recovery timer with:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
```
|
||||
|
||||
That is the current reference deployment model the rest of the platform work should converge toward.
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
# Platform Parity Roadmap
|
||||
|
||||
This roadmap focuses on one question:
|
||||
|
||||
How do we expand VoxVera beyond Linux without overstating reliability, while still converging on real cross-platform parity for Tor hidden-service hosting?
|
||||
|
||||
## Strategic Principle
|
||||
|
||||
Parity is not a packaging milestone. It is a runtime-contract milestone.
|
||||
|
||||
Every platform must eventually implement the same hosting contract:
|
||||
|
||||
1. install VoxVera
|
||||
2. ensure Tor and OnionShare are available
|
||||
3. build site assets
|
||||
4. serve the hidden service
|
||||
5. preserve `.onionshare-session`
|
||||
6. recover hosting after reboot, login, suspend, or offline periods
|
||||
7. surface logs and status
|
||||
|
||||
Package formats should inherit that contract. They should not define independent behavior.
|
||||
|
||||
## Phase 0: Truth Alignment
|
||||
|
||||
Goal: bring repo claims, docs, and release posture into line with current reality.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- canonical support matrix
|
||||
- machine-readable support declaration
|
||||
- installer/package warnings for experimental paths
|
||||
- README and docs aligned to the actual supported target
|
||||
|
||||
Definition of done:
|
||||
|
||||
- no docs imply parity that tests do not support
|
||||
- every platform surface has an explicit support tier
|
||||
- release artifacts are not treated as proof of hosting reliability
|
||||
|
||||
## Phase 1: Harden Linux As Reference
|
||||
|
||||
Goal: make Linux CLI with `systemd --user` the clearly defensible model that other platforms copy.
|
||||
|
||||
Work items:
|
||||
|
||||
1. Add `voxvera doctor` for runtime validation.
|
||||
- Check Tor reachability.
|
||||
- Check OnionShare availability.
|
||||
- Check `start-all` surfaces.
|
||||
- Check autostart timer installation.
|
||||
- Check host directories and session files.
|
||||
|
||||
2. Add `voxvera autostart status`.
|
||||
- Show service and timer files.
|
||||
- Show whether the timer is enabled.
|
||||
- Show next/last run timestamps if available.
|
||||
|
||||
3. Normalize Linux log/status guidance.
|
||||
- Document host log paths.
|
||||
- Document timer/service status commands.
|
||||
- Document expected recovery flow after reboot/network loss.
|
||||
|
||||
4. Validate restart behavior.
|
||||
- fresh install
|
||||
- first serve
|
||||
- reboot
|
||||
- suspend/resume if possible
|
||||
- temporary offline period followed by reconnect
|
||||
|
||||
Definition of done:
|
||||
|
||||
- Linux remains the only `supported` path until the above evidence exists and is documented.
|
||||
|
||||
## Phase 2: Platform Adapter Layer
|
||||
|
||||
Goal: stop encoding platform logic in ad hoc installer scripts.
|
||||
|
||||
Recommended structure:
|
||||
|
||||
- `voxvera/platforms/base.py`
|
||||
- `voxvera/platforms/linux.py`
|
||||
- `voxvera/platforms/macos.py`
|
||||
- `voxvera/platforms/windows.py`
|
||||
- `voxvera/platforms/docker.py`
|
||||
|
||||
Each adapter should define:
|
||||
|
||||
- support tier
|
||||
- Tor discovery strategy
|
||||
- OnionShare discovery strategy
|
||||
- state root
|
||||
- autostart installation strategy
|
||||
- autostart status strategy
|
||||
- log/status surface
|
||||
- known limitations
|
||||
|
||||
CLI surfaces to add:
|
||||
|
||||
- `voxvera doctor`
|
||||
- `voxvera platform-status`
|
||||
- `voxvera autostart`
|
||||
- `voxvera autostart status`
|
||||
- `voxvera autostart uninstall`
|
||||
|
||||
Definition of done:
|
||||
|
||||
- shell installers become thin wrappers over shared Python logic instead of owning runtime policy themselves
|
||||
|
||||
## Phase 3: Docker Beta Candidate
|
||||
|
||||
Goal: make Docker a coherent, testable deployment surface.
|
||||
|
||||
Decisions to settle:
|
||||
|
||||
- single-container runtime vs compose-based runtime
|
||||
- host Tor vs container Tor
|
||||
- log and state persistence model
|
||||
|
||||
Recommended direction:
|
||||
|
||||
- keep runtime state under one explicit volume root
|
||||
- document restart policy clearly
|
||||
- make health and recovery behavior observable
|
||||
|
||||
Validation checklist:
|
||||
|
||||
- container restart preserves onion identity
|
||||
- host reboot with restart policy restores hosting
|
||||
- state remains in mounted volume
|
||||
- logs remain inspectable
|
||||
|
||||
Promotion target:
|
||||
|
||||
- `experimental` -> `beta`
|
||||
|
||||
## Phase 4: macOS CLI
|
||||
|
||||
Goal: establish one honest CLI-based macOS hosting model before packaging.
|
||||
|
||||
Work items:
|
||||
|
||||
1. define Tor discovery strategy
|
||||
2. define OnionShare discovery/install strategy
|
||||
3. validate manual `build` + `serve`
|
||||
4. implement and validate `launchd` recovery
|
||||
5. document known limitations
|
||||
|
||||
Promotion target:
|
||||
|
||||
- remain `experimental` until `launchd` recovery is validated
|
||||
- move to `beta` only after login/reboot/offline recovery evidence exists
|
||||
|
||||
## Phase 5: Windows CLI
|
||||
|
||||
Goal: establish one canonical Windows runtime model before claiming package parity.
|
||||
|
||||
Work items:
|
||||
|
||||
1. unify binary naming across installer, Chocolatey, docs, and release assets
|
||||
2. define Tor discovery strategy
|
||||
3. define OnionShare discovery strategy
|
||||
4. validate manual `build` + `serve`
|
||||
5. implement Task Scheduler recovery path
|
||||
6. add Windows-specific status/log guidance
|
||||
|
||||
Promotion target:
|
||||
|
||||
- remain `experimental` until scheduled recovery and state persistence are proven
|
||||
- move to `beta` only after reboot/login/offline recovery evidence exists
|
||||
|
||||
## Phase 6: Electron GUI Convergence
|
||||
|
||||
Goal: make the GUI a frontend to the supported runtime, not a separate runtime.
|
||||
|
||||
Work items:
|
||||
|
||||
1. route the GUI through shared CLI/runtime surfaces
|
||||
2. reduce duplicated Tor startup behavior
|
||||
3. add visibility for:
|
||||
- Tor status
|
||||
- OnionShare status
|
||||
- hosting status
|
||||
- onion URL
|
||||
- logs
|
||||
4. make window-close behavior explicit and safe
|
||||
|
||||
Promotion target:
|
||||
|
||||
- GUI remains `experimental` until it stops diverging from the CLI runtime contract
|
||||
|
||||
## Phase 7: Package Surface Promotion
|
||||
|
||||
Goal: package formats should become trustworthy wrappers around validated runtimes.
|
||||
|
||||
Work items:
|
||||
|
||||
1. Debian package
|
||||
- declare actual runtime dependencies
|
||||
- validate install + host + recovery path
|
||||
|
||||
2. AppImage
|
||||
- decide whether it is distribution-only or a real hosting target
|
||||
|
||||
3. Homebrew
|
||||
- fix artifact URL/sha/version policy
|
||||
- clarify whether it installs a hosting target or just a CLI
|
||||
|
||||
4. Chocolatey
|
||||
- align artifact naming and installer semantics
|
||||
|
||||
5. Flatpak
|
||||
- decide whether Tor/OnionShare can be supported sanely in-sandbox
|
||||
|
||||
Rule:
|
||||
|
||||
- no package surface should be promoted above the support tier of its underlying runtime
|
||||
|
||||
## Testing Program
|
||||
|
||||
### 1. Unit tests
|
||||
|
||||
- config/state logic
|
||||
- autostart file generation
|
||||
- platform adapter selection
|
||||
|
||||
### 2. Mocked integration tests
|
||||
|
||||
- `serve`
|
||||
- `autostart`
|
||||
- `doctor`
|
||||
- `platform-status`
|
||||
|
||||
### 3. Platform smoke tests
|
||||
|
||||
- fresh install
|
||||
- build
|
||||
- serve
|
||||
- persist identity
|
||||
- restart
|
||||
- recover
|
||||
|
||||
Current smoke target names:
|
||||
|
||||
- `linux-cli`
|
||||
- `macos-cli`
|
||||
- `windows-cli`
|
||||
- `docker-cli`
|
||||
|
||||
These target names are contract-level validation entry points. Only `linux-cli` currently represents a supported hosting path; the others should be used to validate experimental posture and dependency discovery on their native platforms until their recovery behavior is proven.
|
||||
|
||||
### 4. Release checks
|
||||
|
||||
- support matrix is current
|
||||
- docs match support tiers
|
||||
- artifact names match scripts and package manifests
|
||||
|
||||
## Immediate Next Tasks
|
||||
|
||||
1. Add `voxvera doctor`.
|
||||
2. Add `voxvera autostart status`.
|
||||
3. Add the platform adapter scaffold.
|
||||
4. Capture Linux reboot/offline recovery validation steps in docs and CI notes.
|
||||
5. Pick Docker's canonical runtime model.
|
||||
6. Normalize Windows binary naming before further Windows work.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- claiming parity before recovery behavior is validated
|
||||
- treating package availability as support proof
|
||||
- maintaining separate runtime semantics in GUI vs CLI
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
Vendored
+6
-4
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
Vendored
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
Vendored
+15
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
Vendored
+19
-1
@@ -6,18 +6,22 @@ This guide covers common CLI workflows. See `docs/docker.md` for Docker instruct
|
||||
|
||||
VoxVera is designed to be highly portable. You can run it directly from the source code on any platform with Python installed.
|
||||
|
||||
Linux with `systemd --user` is the supported persistent-host environment. Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and should not yet be treated as equally reliable hidden-service deployment targets.
|
||||
|
||||
### 1. Automated Install (Recommended)
|
||||
The easiest way to install VoxVera is using our cross-platform installer script:
|
||||
The easiest way to install VoxVera is using the Linux-first installer script:
|
||||
|
||||
**Linux / macOS:**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.sh | bash
|
||||
```
|
||||
On Linux, this also installs the recurring recovery timer used to retry hidden-service startup after boot and after missed/offline periods.
|
||||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/PR0M3TH3AN/VoxVera/main/install.ps1 | iex
|
||||
```
|
||||
The Windows installer is experimental and does not yet guarantee production-ready background hosting behavior.
|
||||
|
||||
### 2. Manual Source Install
|
||||
If you have downloaded the source code ZIP:
|
||||
@@ -50,6 +54,16 @@ If you wish to remove VoxVera from your system, you can use the provided uninsta
|
||||
```
|
||||
This automatically detects your Tor instance, starts OnionShare, and writes the generated .onion address into the flyer's tear-off links.
|
||||
|
||||
### Persistent Hosting On Linux
|
||||
|
||||
For a machine that should resume hosting automatically after boot or after being offline, install the autostart recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
```
|
||||
|
||||
This installs a `systemd --user` timer that reruns `voxvera start-all` every few minutes. The retry loop is the supported way to recover hidden-service hosting after suspend, reboot, or temporary network loss.
|
||||
|
||||
## Language Support
|
||||
|
||||
VoxVera is fully localized. You can change your language preference permanently using either the interactive selector or a direct shortcut:
|
||||
@@ -92,6 +106,10 @@ When you host a flyer, the **"{{t('web.download_button')}}"** button on the land
|
||||
- Installation and setup scripts for all platforms.
|
||||
- Complete documentation and templates.
|
||||
|
||||
To keep the mirrored download practical for low-bandwidth redistribution, the
|
||||
source archive does not ship pre-downloaded Tor binaries. Normal installer and
|
||||
release builds still fetch or bundle those artifacts separately.
|
||||
|
||||
This allows anyone who scans your flyer to become a new distributor of the VoxVera tool.
|
||||
|
||||
## Export & Backup
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
|
||||
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
|
||||
|
||||
### 2. The Landing Page
|
||||
The main landing page (`site/index.html`) must be synchronized to include your new language.
|
||||
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
|
||||
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
|
||||
The main landing page is generated from the shared template and locale files.
|
||||
- Add your language in `voxvera/locales/{lang_code}.json`.
|
||||
- Do **not** edit `site/index.html` directly; it is generated output.
|
||||
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
|
||||
|
||||
### 3. Documentation
|
||||
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
|
||||
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
|
||||
- `voxvera --lang {lang_code} check` (Verify CLI translation)
|
||||
- `voxvera build-docs` (Generate localized manuals)
|
||||
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
|
||||
- Open `site/index.html` in a browser and test the language switcher.
|
||||
- Open the generated `site/index.html` in a browser and test the language switcher.
|
||||
|
||||
## Code Contributions
|
||||
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
|
||||
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
|
||||
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
|
||||
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
|
||||
- **Style**: Follow the existing coding style (PEP 8 for Python).
|
||||
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,7 @@
|
||||
# Docker Usage
|
||||
|
||||
Experimental: the container path is not yet a production-grade hidden-service deployment target. Linux with `systemd --user` remains the supported persistent-host environment.
|
||||
|
||||
A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published to
|
||||
`ghcr.io/voxvera/voxvera`.
|
||||
|
||||
@@ -7,10 +9,11 @@ A prebuilt image containing Tor, OnionShare and the `voxvera` CLI is published t
|
||||
# pull the image
|
||||
docker pull ghcr.io/voxvera/voxvera:latest
|
||||
|
||||
# run voxvera quickstart with flyers stored in ./flyers
|
||||
# run the experimental container with flyers stored in ./flyers
|
||||
mkdir -p flyers
|
||||
docker run -it --rm -v "$(pwd)/flyers:/flyers" ghcr.io/voxvera/voxvera
|
||||
```
|
||||
|
||||
The container uses `/flyers` as the working directory and runs `voxvera quickstart`
|
||||
by default. All generated flyer files will appear in the mounted `flyers` folder.
|
||||
The container stores runtime state under `/flyers`, seeds a default `config.json` if needed, and retries `voxvera start-all` periodically after the initial `quickstart`.
|
||||
|
||||
Use this path for testing only until the container runtime and background recovery behavior are validated end-to-end.
|
||||
@@ -6,6 +6,10 @@ This page collects common issues encountered when hosting or accessing flyers.
|
||||
- Ensure Tor is allowed through your firewall. On systems using `ufw` you may need to run `sudo ufw allow tor`.
|
||||
- Some networks block Tor entirely. If you cannot reach onion services, try connecting over a different network or use a Tor bridge.
|
||||
|
||||
## Supported deployment target
|
||||
- The supported persistent-host setup is Linux with `systemd --user`.
|
||||
- Windows, macOS, Docker, Flatpak, AppImage, Homebrew, and Chocolatey are currently experimental and may not recover hosting automatically after reboot or network interruptions.
|
||||
|
||||
## Tor port detection
|
||||
|
||||
`voxvera serve` auto-detects Tor by probing common ports (9050/9150 for SOCKS, 9051/9151 for control). If detection fails -- for example when Tor runs on non-standard ports -- you can override with environment variables:
|
||||
@@ -39,6 +43,17 @@ keypair (and URL) will be generated on the next `voxvera serve`. The
|
||||
`voxvera import` command preserves this file automatically, but manual
|
||||
deletion of the `host/` directory will destroy it.
|
||||
|
||||
## Hosting does not return after reboot or network loss
|
||||
On Linux, install or verify the recovery timer:
|
||||
|
||||
```bash
|
||||
voxvera autostart
|
||||
systemctl --user status voxvera-start.timer
|
||||
systemctl --user status voxvera-start.service
|
||||
```
|
||||
|
||||
The supported recovery path is the recurring `systemd --user` timer, which reruns `voxvera start-all` every few minutes.
|
||||
|
||||
## QR codes not generated
|
||||
If `voxvera build` reports "No URLs configured yet", this is expected before the first `voxvera serve`. QR codes for tear-off links are generated automatically once the .onion address is known. QR codes for the content link are generated if you provided a URL during `voxvera init`.
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ This directory contains a minimal Electron wrapper around the `voxvera` CLI.
|
||||
It exposes a simple "Quickstart" button so non-technical users can generate
|
||||
flyers without touching the command line.
|
||||
|
||||
Status: experimental. The GUI is not the supported path for reliable background hidden-service hosting or automatic recovery after reboot/network loss.
|
||||
The GUI now reads `voxvera platform-status --json`, `voxvera doctor --json`, and `voxvera autostart status --json` so its runtime messaging reflects the same platform contract as the CLI instead of inventing a separate compatibility story.
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
|
||||
+84
-2
@@ -4,6 +4,36 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>VoxVera GUI</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 1rem;
|
||||
}
|
||||
#status {
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
background: #fafafa;
|
||||
}
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.status-supported {
|
||||
background: #dff3e4;
|
||||
color: #14532d;
|
||||
}
|
||||
.status-experimental {
|
||||
background: #fff4d6;
|
||||
color: #854d0e;
|
||||
}
|
||||
.status-error {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
#log {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
@@ -16,6 +46,13 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>VoxVera</h1>
|
||||
<div id="status">
|
||||
<strong>Runtime status</strong>
|
||||
<div id="platform-tier"></div>
|
||||
<div id="platform-model"></div>
|
||||
<div id="autostart-state"></div>
|
||||
<div id="doctor-summary"></div>
|
||||
</div>
|
||||
<form id="config-form">
|
||||
<div><label>Name <input type="text" id="name"></label></div>
|
||||
<div><label>Subdomain <input type="text" id="folder_name"></label></div>
|
||||
@@ -26,29 +63,74 @@
|
||||
<div><label>URL Message <input type="text" id="url_message"></label></div>
|
||||
<div><label>URL <input type="text" id="url"></label></div>
|
||||
<div><label>Tear-off link <input type="text" id="tear_off_link"></label></div>
|
||||
<div><label>Binary message <input type="text" id="binary_message"></label></div>
|
||||
<div><label>Footer message <input type="text" id="footer_message"></label></div>
|
||||
<button type="button" id="quickstart">Generate & Serve</button>
|
||||
</form>
|
||||
<p id="onion-address"></p>
|
||||
<pre id="log"></pre>
|
||||
<script>
|
||||
function setStatusBadge(targetId, label, tier) {
|
||||
const el = document.getElementById(targetId);
|
||||
const className = tier === 'supported' ? 'status-supported'
|
||||
: tier === 'experimental' ? 'status-experimental'
|
||||
: 'status-error';
|
||||
el.innerHTML = `<span class="status-badge ${className}">${label}</span>`;
|
||||
}
|
||||
|
||||
function summarizeDoctorChecks(doctor) {
|
||||
const sections = doctor && doctor.sections ? Object.values(doctor.sections) : [];
|
||||
if (sections.length > 0) {
|
||||
const failedSections = sections.filter(section => section.checks && section.checks.length > 0 && !section.ok);
|
||||
if (failedSections.length === 0) return 'All doctor sections passed.';
|
||||
return `Doctor warnings in: ${failedSections.map(section => section.title).join(', ')}`;
|
||||
}
|
||||
const checks = doctor && Array.isArray(doctor.checks) ? doctor.checks : [];
|
||||
if (checks.length === 0) return 'No diagnostics available.';
|
||||
const failed = checks.filter(check => !check.ok);
|
||||
if (failed.length === 0) return 'All doctor checks passed.';
|
||||
return `Doctor reports ${failed.length} failing check(s): ${failed.map(check => check.name).join(', ')}`;
|
||||
}
|
||||
|
||||
async function loadRuntimeStatus() {
|
||||
const runtime = await window.voxvera.loadRuntimeStatus();
|
||||
if (!runtime.ok) {
|
||||
setStatusBadge('platform-tier', 'error', 'error');
|
||||
document.getElementById('platform-model').textContent = runtime.error;
|
||||
document.getElementById('autostart-state').textContent = '';
|
||||
document.getElementById('doctor-summary').textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const { platformStatus, autostart, doctor } = runtime;
|
||||
setStatusBadge('platform-tier', platformStatus.tier, platformStatus.tier);
|
||||
document.getElementById('platform-model').textContent = platformStatus.hosting_model || '';
|
||||
if (autostart && !autostart.error) {
|
||||
document.getElementById('autostart-state').textContent = `Autostart: ${autostart.message}`;
|
||||
} else if (autostart && autostart.error) {
|
||||
document.getElementById('autostart-state').textContent = `Autostart status unavailable: ${autostart.error}`;
|
||||
}
|
||||
document.getElementById('doctor-summary').textContent = summarizeDoctorChecks(doctor);
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const cfg = await window.voxvera.loadConfig();
|
||||
for (const [k, v] of Object.entries(cfg)) {
|
||||
const el = document.getElementById(k);
|
||||
if (el) el.value = v;
|
||||
}
|
||||
await loadRuntimeStatus();
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', load);
|
||||
|
||||
document.getElementById('quickstart').addEventListener('click', async () => {
|
||||
const ids = ['name','folder_name','title','subtitle','headline','content','url_message','url','tear_off_link','binary_message'];
|
||||
const ids = ['name','folder_name','title','subtitle','headline','content','url_message','url','tear_off_link','footer_message'];
|
||||
const cfg = {};
|
||||
ids.forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) cfg[id] = el.value;
|
||||
});
|
||||
await window.voxvera.quickstart(cfg);
|
||||
await loadRuntimeStatus();
|
||||
});
|
||||
|
||||
window.voxvera.onOnionUrl(url => {
|
||||
|
||||
+65
-67
@@ -3,12 +3,10 @@ const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const which = require('which');
|
||||
const fs = require('fs');
|
||||
const { launchTor } = require('./tor.js');
|
||||
const { extractOnionUrl } = require('./runtime-utils');
|
||||
|
||||
let mainWindow;
|
||||
let onionProc;
|
||||
// 🔧 merged conflicting changes from codex/start-onionshare-and-generate-static-site vs main
|
||||
let restarting = false;
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
@@ -21,70 +19,42 @@ function createWindow() {
|
||||
mainWindow.loadFile('index.html');
|
||||
}
|
||||
|
||||
// 🔧 merged conflicting changes from codex/start-onionshare-and-generate-static-site vs main
|
||||
function startOnionShare() {
|
||||
const configPath = getConfigPath();
|
||||
function getVoxveraPath() {
|
||||
const voxveraPath = which.sync('voxvera', { nothrow: true });
|
||||
if (!voxveraPath) {
|
||||
dialog.showErrorBox(
|
||||
'voxvera not found',
|
||||
'Install the voxvera CLI and ensure it is in your PATH.'
|
||||
);
|
||||
return;
|
||||
throw new Error('Install the voxvera CLI and ensure it is in your PATH.');
|
||||
}
|
||||
const build = spawn(voxveraPath, ['--config', configPath, 'build']);
|
||||
build.on('error', err => {
|
||||
dialog.showErrorBox('voxvera build error', err.message);
|
||||
});
|
||||
build.on('close', code => {
|
||||
if (code !== 0) {
|
||||
dialog.showErrorBox('voxvera build error', `build exited with code ${code}.`);
|
||||
return;
|
||||
}
|
||||
runServe();
|
||||
});
|
||||
return voxveraPath;
|
||||
}
|
||||
|
||||
async function runServe (retry = false) {
|
||||
let torProc, socksPort, controlPort;
|
||||
try {
|
||||
({ torProc, socksPort, controlPort } = await launchTor());
|
||||
} catch (err) {
|
||||
dialog.showErrorBox('Tor error', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const env = { ...process.env,
|
||||
TOR_SOCKS_PORT: socksPort.toString(),
|
||||
TOR_CONTROL_PORT: controlPort.toString() };
|
||||
|
||||
const configPath = getConfigPath();
|
||||
const voxveraPath = which.sync('voxvera', { nothrow: true });
|
||||
onionProc = spawn(voxveraPath, ['--config', configPath, 'serve'], { env });
|
||||
|
||||
let gotURL = false;
|
||||
const softTimeout = setTimeout(() => {
|
||||
if (!gotURL) {
|
||||
mainWindow.webContents.send('log',
|
||||
{ text: 'Tor timed out, retrying…', isError: true });
|
||||
onionProc.kill(); torProc.kill();
|
||||
runServe(true);
|
||||
}
|
||||
}, 90_000);
|
||||
|
||||
onionProc.stdout.on('data', buf => {
|
||||
const line = buf.toString();
|
||||
const m = line.match(/OnionShare is hosting at (http.*\.onion)/);
|
||||
if (m) { gotURL = true; clearTimeout(softTimeout); }
|
||||
mainWindow.webContents.send('log', { text: line });
|
||||
function runJsonCommand(args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
const proc = spawn(getVoxveraPath(), args);
|
||||
proc.stdout.on('data', data => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
proc.stderr.on('data', data => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
proc.on('close', code => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(stderr || `voxvera exited with code ${code}`));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolve(JSON.parse(stdout));
|
||||
} catch (err) {
|
||||
reject(new Error(`Could not parse VoxVera JSON output: ${err.message}`));
|
||||
}
|
||||
});
|
||||
proc.on('error', reject);
|
||||
});
|
||||
|
||||
onionProc.on('exit', () => torProc.kill());
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
startOnionShare();
|
||||
});
|
||||
|
||||
function getConfigPath() {
|
||||
@@ -105,7 +75,36 @@ function getConfigPath() {
|
||||
ipcMain.handle('load-config', async () => {
|
||||
const p = getConfigPath();
|
||||
const raw = fs.readFileSync(p, 'utf8');
|
||||
return JSON.parse(raw);
|
||||
const config = JSON.parse(raw);
|
||||
if (config.footer_message === undefined && config.binary_message !== undefined) {
|
||||
config.footer_message = config.binary_message;
|
||||
}
|
||||
delete config.binary_message;
|
||||
return config;
|
||||
});
|
||||
|
||||
ipcMain.handle('load-runtime-status', async () => {
|
||||
try {
|
||||
const platformStatus = await runJsonCommand(['platform-status', '--json']);
|
||||
const doctor = await runJsonCommand(['doctor', '--json']);
|
||||
let autostart = null;
|
||||
try {
|
||||
autostart = await runJsonCommand(['autostart', 'status', '--json']);
|
||||
} catch (err) {
|
||||
autostart = { error: err.message };
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
platformStatus,
|
||||
doctor,
|
||||
autostart
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
ok: false,
|
||||
error: err.message
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('run-quickstart', async (_, config) => {
|
||||
@@ -113,12 +112,11 @@ ipcMain.handle('run-quickstart', async (_, config) => {
|
||||
if (config && typeof config === 'object') {
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||
}
|
||||
const voxveraPath = which.sync('voxvera', { nothrow: true });
|
||||
if (!voxveraPath) {
|
||||
dialog.showErrorBox(
|
||||
'voxvera not found',
|
||||
'Install the voxvera CLI and ensure it is in your PATH.'
|
||||
);
|
||||
let voxveraPath;
|
||||
try {
|
||||
voxveraPath = getVoxveraPath();
|
||||
} catch (err) {
|
||||
dialog.showErrorBox('voxvera not found', err.message);
|
||||
return -1;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
@@ -134,9 +132,9 @@ ipcMain.handle('run-quickstart', async (_, config) => {
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('log', { text: line, isError: false });
|
||||
}
|
||||
const m = line.match(/Onion URL:\s*(https?:\/\/[a-z0-9.-]+\.onion)/i);
|
||||
if (m && mainWindow) {
|
||||
mainWindow.webContents.send('onion-url', m[1]);
|
||||
const onionUrl = extractOnionUrl(line);
|
||||
if (onionUrl && mainWindow) {
|
||||
mainWindow.webContents.send('onion-url', onionUrl);
|
||||
}
|
||||
});
|
||||
proc.stderr.on('data', data => {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"lint": "echo 'lint pass'"
|
||||
"lint": "echo 'lint pass'",
|
||||
"test": "node --test runtime-utils.test.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^29.0.0",
|
||||
|
||||
@@ -2,6 +2,7 @@ const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('voxvera', {
|
||||
loadConfig: () => ipcRenderer.invoke('load-config'),
|
||||
loadRuntimeStatus: () => ipcRenderer.invoke('load-runtime-status'),
|
||||
quickstart: (config) => ipcRenderer.invoke('run-quickstart', config),
|
||||
onOnionUrl: (cb) => ipcRenderer.on('onion-url', (_, url) => cb(url)),
|
||||
onLog: (cb) => ipcRenderer.on('log', (_, data) => cb(data.text, data.isError))
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
function extractOnionUrl(line) {
|
||||
const match = line.match(/Onion URL:\s*(https?:\/\/[a-z0-9.-]+\.onion)/i)
|
||||
|| line.match(/OnionShare is hosting at\s*(https?:\/\/[a-z0-9.-]+\.onion)/i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function platformDirName(platform = process.platform) {
|
||||
if (platform === 'win32') return 'win';
|
||||
if (platform === 'darwin') return 'mac';
|
||||
return 'linux';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractOnionUrl,
|
||||
platformDirName,
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { extractOnionUrl, platformDirName } = require('./runtime-utils');
|
||||
|
||||
test('extractOnionUrl handles current CLI output', () => {
|
||||
assert.equal(
|
||||
extractOnionUrl('[voxvera] Onion URL: http://test123.onion'),
|
||||
'http://test123.onion'
|
||||
);
|
||||
});
|
||||
|
||||
test('extractOnionUrl handles legacy OnionShare output', () => {
|
||||
assert.equal(
|
||||
extractOnionUrl('OnionShare is hosting at http://legacy456.onion'),
|
||||
'http://legacy456.onion'
|
||||
);
|
||||
});
|
||||
|
||||
test('extractOnionUrl returns null when no onion URL is present', () => {
|
||||
assert.equal(extractOnionUrl('still bootstrapping'), null);
|
||||
});
|
||||
|
||||
test('platformDirName maps bundled resource folders correctly', () => {
|
||||
assert.equal(platformDirName('linux'), 'linux');
|
||||
assert.equal(platformDirName('darwin'), 'mac');
|
||||
assert.equal(platformDirName('win32'), 'win');
|
||||
});
|
||||
+4
-2
@@ -3,14 +3,16 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const which = require('which');
|
||||
const getPort = (...args) => import('get-port').then(m => m.default(...args));
|
||||
const { platformDirName } = require('./runtime-utils');
|
||||
|
||||
async function launchTor() {
|
||||
const socks = await getPort();
|
||||
const control = await getPort();
|
||||
const torBase = path.join(__dirname, '..', '..', 'voxvera', 'resources', 'tor');
|
||||
let exe = path.join(torBase, process.platform,
|
||||
const platformDir = platformDirName(process.platform);
|
||||
let exe = path.join(torBase, platformDir,
|
||||
process.platform === 'win32' ? 'tor.exe' : 'tor');
|
||||
let obfs4 = path.join(torBase, process.platform,
|
||||
let obfs4 = path.join(torBase, platformDir,
|
||||
process.platform === 'win32' ? 'obfs4proxy.exe' : 'obfs4proxy');
|
||||
|
||||
const missing = p => {
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
"title": "ANDROID UNLEASHED",
|
||||
"subtitle": "WITHOUT BOUNDARIES",
|
||||
"headline": "OPEN-SOURCE FREEDOM",
|
||||
"content": "The Android Open Source Project (AOSP) represents the essence of what Android was originally intended to be: an open, transparent, and adaptable platform. It strips away the layers of proprietary software and corporate oversight that have become synonymous with mainstream Android distributions. With AOSP, you gain access to a raw, unfiltered version of Android, allowing you to truly make your device your own.\n\nFor developers, AOSP offers a playground of possibilities, where the only limits are your skills and imagination. It serves as the foundation for countless custom ROMs, each tailored to specific needs, whether for enhanced performance, privacy, or unique features that mainstream versions lack. Privacy enthusiasts will appreciate the absence of pre-installed bloatware and tracking services that often come wit stock Android, providing a cleaner, more secure user experience.\n\nAOSP isn't just for developers or tech enthusiasts; it's for anyone who values digital freedom. It allows you to reclaim control over your device, tailoring it to your needs without the restrictions of carriers or manufacturers. Discover how AOSP can transform your mobile experience. Start your journey towards a more open, customizable future today.",
|
||||
"content": "The Android Open Source Project (AOSP) represents the essence of what Android was originally intended to be: an open, transparent, and adaptable platform. It strips away the layers of proprietary software and corporate oversight that have become synonymous with mainstream Android distributions. With AOSP, you gain access to a raw, unfiltered version of Android, allowing you to truly make your device your own.<br><br>For developers, AOSP offers a playground of possibilities, where the only limits are your skills and imagination. It serves as the foundation for countless custom ROMs, each tailored to specific needs, whether for enhanced performance, privacy, or unique features that mainstream versions lack. Privacy enthusiasts will appreciate the absence of pre-installed bloatware and tracking services that often come wit stock Android, providing a cleaner, more secure user experience.<br><br>AOSP isn't just for developers or tech enthusiasts; it's for anyone who values digital freedom. It allows you to reclaim control over your device, tailoring it to your needs without the restrictions of carriers or manufacturers. Discover how AOSP can transform your mobile experience. Start your journey towards a more open, customizable future today.",
|
||||
"url_message": "Visit the link below to learn more.",
|
||||
"url": "https://source.android.com",
|
||||
"tear_off_link": "http://aosp.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Bitcoin Revolution Flyers",
|
||||
"folder_name": "bitcoin",
|
||||
"title": "THE FUTURE OF MONEY",
|
||||
"title": "RULES WITHOUT RULERS",
|
||||
"subtitle": "DO NOT IGNORE",
|
||||
"headline": "JOIN THE BITCOIN REVOLUTION",
|
||||
"content": "What is Bitcoin? It's more than just digital money; it's a revolutionary technology designed to fix some of the world's most pressing problems. Bitcoin operates on a decentralized network, free from the control of any government or central authority. This ensures that no one can manipulate its supply or value, making it a sound form of money that protects your wealth from inflation.\n\nOur current financial system is built on flawed incentives—encouraging debt, dependency, and centralization of power. Bitcoin changes that by creating a system where honesty, transparency, and individual responsibility are rewarded. In the Bitcoin network, there is no room for corruption or manipulation because the rules are enforced by mathematics, not by people.\n\nBitcoin aligns incentives in a way that promotes long-term thinking, savings, and innovation. It empowers individuals to take control of their financial future, fostering a global community where value can be transferred freely and securely without intermediaries. This is why we need Bitcoin—because it creates a world where incentives are properly aligned, leading to a more fair and prosperous society for all.\n\nTake the step to learn more about Bitcoin and how it can transform not just your financial life, but the world. Tear off a link to the left and explore this groundbreaking technology. Join the movement towards a better future.",
|
||||
"content": "What is Bitcoin? It's more than just digital money; it's a revolutionary technology designed to fix some of the world's most pressing problems. Bitcoin operates on a decentralized network, free from the control of any government or central authority. This ensures that no one can manipulate its supply or value, making it a sound form of money that protects your wealth from inflation.<br><br>Our current financial system is built on flawed incentives—encouraging debt, dependency, and centralization of power. Bitcoin changes that by creating a system where honesty, transparency, and individual responsibility are rewarded. In the Bitcoin network, there is no room for corruption or manipulation because the rules are enforced by mathematics, not by people.<br><br>Bitcoin aligns incentives in a way that promotes long-term thinking, savings, and innovation. It empowers individuals to take control of their financial future, fostering a global community where value can be transferred freely and securely without intermediaries. This is why we need Bitcoin—because it creates a world where incentives are properly aligned, leading to a more fair and prosperous society for all.<br><br>Take the step to learn more about Bitcoin and how it can transform not just your financial life, but the world. Tear off a link to the left and explore this groundbreaking technology. Join the movement towards a better future.",
|
||||
"url_message": "Follow this link to learn more.",
|
||||
"url": "https://bitcoin.org/en",
|
||||
"tear_off_link": "http://bitcoin.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
+1
-1
@@ -8,5 +8,5 @@
|
||||
"url_message": "",
|
||||
"url": "",
|
||||
"tear_off_link": "",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
@@ -7,6 +7,6 @@
|
||||
"content": "content",
|
||||
"url_message": "url message",
|
||||
"url": "url",
|
||||
"tear_off_link": "http://example-folder_name.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
"title": "GOOD NEWS",
|
||||
"subtitle": "DO NOT IGNORE",
|
||||
"headline": "DISCOVER TRUE HOPE",
|
||||
"content": "Who is Jesus? He is more than just a historical figure; He is the Son of God, the Savior of the world. Jesus Himself said, 'I am the way, the truth, and the life. No one comes to the Father except through Me' (John 14:6). He came to bridge the gap between God and humanity, offering us a path to forgiveness, peace, and eternal life.\n\nWe all face brokenness in our lives—whether it's in our relationships, our hearts, or our world. Jesus came to heal that brokenness. He lived a perfect life, died on the cross for our sins, and rose again, defeating death and offering us the hope of eternal life with God.\n\nWithout Jesus, we are lost in our sin, separated from God. But through Him, we can be forgiven and restored. He offers us not just a better life now, but the promise of an eternal life with Him. This is why we need Him—because only in Jesus can we find true peace, purpose, and the assurance of eternal life.\n\nTake the step to learn more about Jesus and the life-changing impact He can have on your life. Tear off a link to the left and explore His teachings. Find the hope you've been searching for.",
|
||||
"content": "Who is Jesus? He is more than just a historical figure; He is the Son of God, the Savior of the world. Jesus Himself said, 'I am the way, the truth, and the life. No one comes to the Father except through Me' (John 14:6). He came to bridge the gap between God and humanity, offering us a path to forgiveness, peace, and eternal life.<br><br>We all face brokenness in our lives—whether it's in our relationships, our hearts, or our world. Jesus came to heal that brokenness. He lived a perfect life, died on the cross for our sins, and rose again, defeating death and offering us the hope of eternal life with God.<br><br>Without Jesus, we are lost in our sin, separated from God. But through Him, we can be forgiven and restored. He offers us not just a better life now, but the promise of an eternal life with Him. This is why we need Him—because only in Jesus can we find true peace, purpose, and the assurance of eternal life.<br><br>Take the step to learn more about Jesus and the life-changing impact He can have on your life. Tear off a link to the left and explore His teachings. Find the hope you've been searching for.",
|
||||
"url_message": "Follow this link to learn more.",
|
||||
"url": "http://media.faithforthefamily.com/ra/godsoloved/english.mp3",
|
||||
"tear_off_link": "http://jesus.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
"title": "SECURE COMMUNICATIONS",
|
||||
"subtitle": "DO NOT IGNORE",
|
||||
"headline": "BUILD YOUR EMERGENCY NETWORK",
|
||||
"content": "Why Meshtastic? In times of crisis, traditional communication networks can fail, leaving us isolated and vulnerable. Meshtastic provides a powerful solution—an alternative local communication network that operates independently of centralized systems. It's designed for emergency situations, allowing you to stay connected when it matters most.\n\nMeshtastic devices create a mesh network that can relay messages over long distances, even without internet or cellular coverage. This decentralized approach ensures that your communications are resilient and reliable, no matter the circumstances. But it’s not just about staying connected—Meshtastic also offers encryption, so your messages remain secure and private.\n\nIn a world where privacy is increasingly compromised, Meshtastic empowers you to take control of your communication. Whether you're coordinating a local community response, staying in touch with loved ones during a disaster, or simply preparing for the unexpected, Meshtastic provides the tools you need to communicate securely and effectively.\n\nTake the step to learn more about Meshtastic and how you can build your own emergency communication network. Tear off a link to the left and explore this innovative technology. Ensure you're prepared for whatever comes your way.",
|
||||
"content": "Why Meshtastic? In times of crisis, traditional communication networks can fail, leaving us isolated and vulnerable. Meshtastic provides a powerful solution—an alternative local communication network that operates independently of centralized systems. It’s designed for emergency situations, allowing you to stay connected when it matters most.<br><br>Meshtastic devices create a mesh network that can relay messages over long distances, even without internet or cellular coverage. This decentralized approach ensures that your communications are resilient and reliable, no matter the circumstances. But it’s not just about staying connected—Meshtastic also offers encryption, so your messages remain secure and private.<br><br>In a world where privacy is increasingly compromised, Meshtastic empowers you to take control of your communication. Whether you’re coordinating a local community response, staying in touch with loved ones during a disaster, or simply preparing for the unexpected, Meshtastic provides the tools you need to communicate securely and effectively.<br><br>Take the step to learn more about Meshtastic and how you can build your own emergency communication network. Tear off a link to the left and explore this innovative technology. Ensure you’re prepared for whatever comes your way.",
|
||||
"url_message": "Follow this link to learn more.",
|
||||
"url": "https://meshtastic.org",
|
||||
"tear_off_link": "http://meshtastic.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
+4
-4
@@ -4,9 +4,9 @@
|
||||
"title": "SOCIAL FREEDOM",
|
||||
"subtitle": "DO NOT IGNORE",
|
||||
"headline": "JOIN THE CENSORSHIP-RESISTANT NETWORK",
|
||||
"content": "What is Nostr? Unlike traditional social media platforms, Nostr is a protocol—a decentralized system that empowers users to connect and share without the constraints of a central authority. Nostr is censorship-resistant, meaning your voice can’t be silenced by corporations or governments. It's designed to give you control over your online presence, free from the influence of algorithms or ads that prioritize engagement over meaningful connections.\n\nOn Nostr, there are no hidden agendas to keep you doomscrolling. You engage with content on your terms, not based on what an algorithm thinks will keep you online the longest. Nostr allows you to build genuine connections and participate in conversations that matter, all while maintaining your privacy and autonomy.\n\nIn a world where online speech is increasingly controlled and manipulated, Nostr offers a refreshing alternative. It’s not just a platform; it’s a movement towards true digital freedom, where your interactions are driven by your interests and values, not by what generates the most ad revenue.\n\nTake the step to learn more about Nostr and how it can transform the way you connect online. Tear off a link the left and explore this groundbreaking protocol. Join a community that values freedom and authentic interaction.",
|
||||
"content": "What is Nostr? Unlike traditional social media platforms, Nostr is a protocol—a decentralized system that empowers users to connect and share without the constraints of a central authority. Nostr is censorship-resistant, meaning your voice can’t be silenced by corporations or governments. It’s designed to give you control over your online presence, free from the influence of algorithms or ads that prioritize engagement over meaningful connections.<br><br>On Nostr, there are no hidden agendas to keep you doomscrolling. You engage with content on your terms, not based on what an algorithm thinks will keep you online the longest. Nostr allows you to build genuine connections and participate in conversations that matter, all while maintaining your privacy and autonomy.<br><br>In a world where online speech is increasingly controlled and manipulated, Nostr offers a refreshing alternative. It’s not just a platform; it’s a movement towards true digital freedom, where your interactions are driven by your interests and values, not by what generates the most ad revenue.<br><br>Take the step to learn more about Nostr and how it can transform the way you connect online. Tear off a link the left and explore this groundbreaking protocol. Join a community that values freedom and authentic interaction.",
|
||||
"url_message": "Follow this link to learn more.",
|
||||
"url": "https://nostr.how/en/what-is-nostr",
|
||||
"tear_off_link": "http://nostr.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion/",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
"title": "TOP SECRET",
|
||||
"subtitle": "DO ~~NOT~~ DISTRIBUTE",
|
||||
"headline": "OPERATION VOX VERA",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.\n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.\n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
|
||||
"url_message": "Follow this link to learn more. Use Tor Browser.",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
|
||||
"url_message": "Follow this link to learn more.",
|
||||
"url": "https://voxvera.org/",
|
||||
"tear_off_link": "https://voxvera.org/",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
"tear_off_link": "",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
+41
-3
@@ -1,6 +1,8 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$packages = @('tor', 'onionshare', 'jq', 'qrencode', 'imagemagick')
|
||||
Write-Warning 'The Windows installer path is experimental. It is not yet validated for reliable background Tor hidden-service hosting or automatic recovery after connectivity changes.'
|
||||
|
||||
$packages = @('tor', 'onionshare')
|
||||
|
||||
function Install-Choco {
|
||||
param([string]$Name)
|
||||
@@ -76,6 +78,41 @@ function Check-LocalBin {
|
||||
}
|
||||
}
|
||||
|
||||
function Show-VoxVeraStatus {
|
||||
if (-not (Get-Command voxvera -ErrorAction SilentlyContinue)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
$platform = voxvera platform-status --json | ConvertFrom-Json
|
||||
Write-Host ("Platform tier: {0} ({1})" -f $platform.tier, $platform.label)
|
||||
if ($platform.hosting_model) {
|
||||
Write-Host ("Hosting model: {0}" -f $platform.hosting_model)
|
||||
}
|
||||
} catch {
|
||||
Write-Warning "Could not read platform status from voxvera."
|
||||
}
|
||||
|
||||
try {
|
||||
$doctor = voxvera doctor --json | ConvertFrom-Json
|
||||
$failed = @($doctor.checks | Where-Object { -not $_.ok } | ForEach-Object { $_.name })
|
||||
if ($failed.Count -gt 0) {
|
||||
Write-Host ("Doctor checks needing attention: {0}" -f ($failed -join ', '))
|
||||
} else {
|
||||
Write-Host 'Doctor checks: all passed'
|
||||
}
|
||||
} catch {
|
||||
Write-Warning "Could not read doctor output from voxvera."
|
||||
}
|
||||
|
||||
try {
|
||||
$autostart = voxvera autostart status --json | ConvertFrom-Json
|
||||
Write-Host ("Autostart: {0}" -f $autostart.message)
|
||||
} catch {
|
||||
Write-Warning "Could not read autostart status from voxvera."
|
||||
}
|
||||
}
|
||||
|
||||
if (Get-Command pipx -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
pipx install voxvera --force
|
||||
@@ -84,7 +121,7 @@ if (Get-Command pipx -ErrorAction SilentlyContinue) {
|
||||
$home = [Environment]::GetFolderPath('UserProfile')
|
||||
$dest = Join-Path $home '.local\bin'
|
||||
New-Item -ItemType Directory -Path $dest -Force | Out-Null
|
||||
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera.exe'
|
||||
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe'
|
||||
$status = Download-Binary $url "$dest/voxvera.exe"
|
||||
if ($status -eq 200) {
|
||||
Check-LocalBin
|
||||
@@ -100,7 +137,7 @@ if (Get-Command pipx -ErrorAction SilentlyContinue) {
|
||||
$home = [Environment]::GetFolderPath('UserProfile')
|
||||
$dest = Join-Path $home '.local\bin'
|
||||
New-Item -ItemType Directory -Path $dest -Force | Out-Null
|
||||
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera.exe'
|
||||
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe'
|
||||
$status = Download-Binary $url "$dest/voxvera.exe"
|
||||
if ($status -eq 200) {
|
||||
Check-LocalBin
|
||||
@@ -114,3 +151,4 @@ if (Get-Command pipx -ErrorAction SilentlyContinue) {
|
||||
}
|
||||
|
||||
Write-Host 'VoxVera installed successfully.'
|
||||
Show-VoxVeraStatus
|
||||
+366
-90
@@ -1,6 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="PR0M3TH3AN/VoxVera"
|
||||
REPO_URL="https://github.com/${REPO}"
|
||||
BRANCH="main"
|
||||
OS_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
# --- Network resilience settings ---
|
||||
# Kill git operations that stall below 1 KB/s for 30 seconds
|
||||
export GIT_HTTP_LOW_SPEED_LIMIT=1000
|
||||
export GIT_HTTP_LOW_SPEED_TIME=30
|
||||
# Disable partial clones — they are fragile on flaky connections
|
||||
export GIT_CLONE_PROTECTION_ACTIVE=false
|
||||
# Hard timeout (seconds) for any single network operation
|
||||
NET_TIMEOUT=90
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
@@ -36,43 +50,121 @@ msg() { echo -e "\e[32m$*\e[0m"; }
|
||||
warn() { echo -e "\e[33m$*\e[0m" >&2; }
|
||||
die() { echo -e "\e[31m$*\e[0m" >&2; exit 1; }
|
||||
|
||||
report_cli_status() {
|
||||
command_exists voxvera || return 0
|
||||
|
||||
local platform_json doctor_json autostart_json
|
||||
platform_json="$(voxvera platform-status --json 2>/dev/null || true)"
|
||||
doctor_json="$(voxvera doctor --json 2>/dev/null || true)"
|
||||
autostart_json="$(voxvera autostart status --json 2>/dev/null || true)"
|
||||
|
||||
if [ -n "$platform_json" ]; then
|
||||
PLATFORM_JSON="$platform_json" python3 - <<'PY'
|
||||
import json, os
|
||||
data = json.loads(os.environ["PLATFORM_JSON"])
|
||||
print(f"Platform tier: {data.get('tier', 'unknown')} ({data.get('label', 'unknown')})")
|
||||
if data.get("hosting_model"):
|
||||
print(f"Hosting model: {data['hosting_model']}")
|
||||
PY
|
||||
fi
|
||||
|
||||
if [ -n "$doctor_json" ]; then
|
||||
DOCTOR_JSON="$doctor_json" python3 - <<'PY'
|
||||
import json, os
|
||||
data = json.loads(os.environ["DOCTOR_JSON"])
|
||||
failed = [check["name"] for check in data.get("checks", []) if not check.get("ok")]
|
||||
if failed:
|
||||
print("Doctor checks needing attention: " + ", ".join(failed))
|
||||
else:
|
||||
print("Doctor checks: all passed")
|
||||
PY
|
||||
fi
|
||||
|
||||
if [ -n "$autostart_json" ]; then
|
||||
AUTOSTART_JSON="$autostart_json" python3 - <<'PY'
|
||||
import json, os
|
||||
data = json.loads(os.environ["AUTOSTART_JSON"])
|
||||
print("Autostart: " + data.get("message", "unknown"))
|
||||
PY
|
||||
fi
|
||||
}
|
||||
|
||||
install_pkg() {
|
||||
case "$PM" in
|
||||
apt)
|
||||
$SUDO apt-get install -y "$@"
|
||||
;;
|
||||
dnf)
|
||||
$SUDO dnf install -y "$@"
|
||||
;;
|
||||
yum)
|
||||
$SUDO yum install -y "$@"
|
||||
;;
|
||||
pacman)
|
||||
$SUDO pacman -S --noconfirm "$@"
|
||||
;;
|
||||
brew)
|
||||
brew install "$@"
|
||||
;;
|
||||
apk)
|
||||
$SUDO apk add --no-cache "$@"
|
||||
;;
|
||||
apt) $SUDO apt-get install -y "$@" ;;
|
||||
dnf) $SUDO dnf install -y "$@" ;;
|
||||
yum) $SUDO yum install -y "$@" ;;
|
||||
pacman) $SUDO pacman -S --noconfirm "$@" ;;
|
||||
brew) brew install "$@" ;;
|
||||
apk) $SUDO apk add --no-cache "$@" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Ensure core dependencies are present (always run to ensure they are up to date)
|
||||
# --- Resilient download: tries curl then wget, with retries and timeout ---
|
||||
download() {
|
||||
local url="$1" dest="$2"
|
||||
# Try curl first
|
||||
if command_exists curl; then
|
||||
local attempt
|
||||
for attempt in 1 2 3; do
|
||||
if timeout "$NET_TIMEOUT" curl -fsSL \
|
||||
--retry 2 --retry-delay 3 --retry-connrefused \
|
||||
--max-time "$NET_TIMEOUT" \
|
||||
"$url" -o "$dest" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
warn "curl attempt $attempt failed for $(basename "$url")"
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
# Try wget as fallback (different TLS stack — often works when curl doesn't)
|
||||
if command_exists wget; then
|
||||
local attempt
|
||||
for attempt in 1 2 3; do
|
||||
if timeout "$NET_TIMEOUT" wget -q --timeout=60 --tries=1 \
|
||||
"$url" -O "$dest" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
warn "wget attempt $attempt failed for $(basename "$url")"
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- Resilient HTTP status check (for release tag redirect) ---
|
||||
get_latest_tag() {
|
||||
local tag=""
|
||||
if command_exists curl; then
|
||||
tag=$(timeout 30 curl -fsSLI -o /dev/null -w '%{url_effective}' \
|
||||
"${REPO_URL}/releases/latest" 2>/dev/null | grep -oE '[^/]+$') || true
|
||||
fi
|
||||
if [ -z "$tag" ] && command_exists wget; then
|
||||
tag=$(timeout 30 wget --max-redirect=5 -q -S --spider \
|
||||
"${REPO_URL}/releases/latest" 2>&1 | grep -i 'Location:' | tail -1 | grep -oE '[^/]+$') || true
|
||||
fi
|
||||
echo "$tag"
|
||||
}
|
||||
|
||||
# Ensure core dependencies are present
|
||||
msg "Checking and updating system dependencies..."
|
||||
if [[ "$OS_NAME" != linux* ]]; then
|
||||
warn "This installer is optimized for Linux. Non-Linux paths are experimental and are not validated for reliable background hidden-service hosting."
|
||||
fi
|
||||
if [ "$PM" = "apt" ]; then
|
||||
$SUDO apt-get update
|
||||
SYSTEM_PKGS=(tor curl git python3-pip python3-venv)
|
||||
SYSTEM_PKGS=(tor curl wget git python3-pip python3-venv)
|
||||
[ -n "$(command -v pipx)" ] || SYSTEM_PKGS+=(pipx)
|
||||
install_pkg "${SYSTEM_PKGS[@]}"
|
||||
elif [ "$PM" = "brew" ]; then
|
||||
brew install tor onionshare curl git
|
||||
brew install tor onionshare curl wget git
|
||||
else
|
||||
install_pkg tor curl git
|
||||
# Best-effort install wget if not present
|
||||
install_pkg wget 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Onionshare-cli - ensure it's installed and working (always attempt update if using pipx)
|
||||
# Onionshare-cli
|
||||
msg "Ensuring onionshare-cli is installed and up-to-date..."
|
||||
if command_exists pipx && (pipx list | grep -q onionshare || ! command_exists onionshare-cli); then
|
||||
msg "Installing/Updating onionshare-cli via pipx..."
|
||||
@@ -96,84 +188,268 @@ else
|
||||
msg "onionshare-cli found (system package), skipping pipx install. Run 'pipx install --force ...' if you want the latest git version."
|
||||
fi
|
||||
|
||||
download_binary() {
|
||||
local url=$1
|
||||
local dest=$2
|
||||
if command_exists curl; then
|
||||
local status
|
||||
status=$(curl -w "%{http_code}" -fsSL "$url" -o "$dest" || true)
|
||||
if [ "$status" = "404" ]; then return 2; elif [ "$status" != "200" ]; then return 1; fi
|
||||
elif command_exists wget; then
|
||||
local out
|
||||
out=$(wget --server-response -q "$url" -O "$dest" 2>&1 || true)
|
||||
if echo "$out" | grep -q "404 Not Found"; then return 2; elif ! echo "$out" | grep -q "200 OK"; then return 1; fi
|
||||
else
|
||||
warn "Install curl or wget to download voxvera"
|
||||
return 1
|
||||
fi
|
||||
chmod +x "$dest"
|
||||
# --- Clean up stale state from previous installs (preserves ~/host tor sites) ---
|
||||
msg "Cleaning up stale state from previous installs..."
|
||||
|
||||
# Stale user-local packages that may conflict with system onionshare-cli
|
||||
for pydir in "$HOME"/.local/lib/python3.*/site-packages; do
|
||||
[ -d "$pydir" ] || continue
|
||||
for pkg in werkzeug flask flask_socketio onionshare_cli voxvera; do
|
||||
if [ -d "$pydir/$pkg" ]; then
|
||||
warn "Removing stale $pkg from $pydir"
|
||||
rm -rf "$pydir/$pkg" "$pydir/${pkg}-"*.dist-info 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Stale pip wheel cache for voxvera (forces clean rebuild)
|
||||
for cache_dir in "$HOME"/.cache/pip/wheels; do
|
||||
[ -d "$cache_dir" ] || continue
|
||||
find "$cache_dir" -name 'voxvera-*.whl' -delete 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Leftover temp files from previous failed install attempts
|
||||
rm -rf /tmp/voxvera-* 2>/dev/null || true
|
||||
|
||||
# Stale top-level config.json from older VoxVera versions (not inside ~/host)
|
||||
[ -f "$HOME/config.json" ] && grep -q '"folder_name"' "$HOME/config.json" 2>/dev/null && {
|
||||
warn "Removing stale ~/config.json (leftover from older VoxVera version)"
|
||||
rm -f "$HOME/config.json"
|
||||
}
|
||||
|
||||
# Install VoxVera (Always use --force to ensure fresh installation)
|
||||
if command_exists curl; then
|
||||
msg "Attempting to download latest VoxVera binary..."
|
||||
# Get the latest release tag
|
||||
latest=$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/PR0M3TH3AN/VoxVera/releases/latest | grep -oE '[^/]+$')
|
||||
|
||||
install_dir="$HOME/.local/bin"
|
||||
# Stale top-level imports/ directory (older versions put it in ~/)
|
||||
if [ -d "$HOME/imports" ] && ls "$HOME/imports"/*.json &>/dev/null; then
|
||||
if head -1 "$HOME/imports"/*.json 2>/dev/null | grep -q '"folder_name"\|"headline"'; then
|
||||
warn "Removing stale ~/imports/ (leftover from older VoxVera version)"
|
||||
rm -rf "$HOME/imports"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean generated files from existing site directories so rebuild-all
|
||||
# regenerates them from the current template. Preserves:
|
||||
# config.json — site content and settings
|
||||
# .onionshare-session — Tor domain identity key
|
||||
# server.pid — running process marker
|
||||
# onionshare.log — current log (may be in use)
|
||||
# Everything else is regenerated by build_assets().
|
||||
if [ -d "$HOME/host" ]; then
|
||||
for site_dir in "$HOME"/host/*/; do
|
||||
[ -f "${site_dir}config.json" ] || continue
|
||||
msg "Cleaning generated files in ${site_dir}..."
|
||||
rm -f "${site_dir}index.html" 2>/dev/null || true
|
||||
rm -f "${site_dir}qrcode-content.png" "${site_dir}qrcode-tear-offs.png" 2>/dev/null || true
|
||||
rm -rf "${site_dir}download" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
# --- Helper: build pip flags for this distro ---
|
||||
pip_flags() {
|
||||
local flags="--user --upgrade"
|
||||
if [ -f /etc/debian_version ]; then
|
||||
local deb_ver
|
||||
deb_ver=$(cut -d. -f1 < /etc/debian_version)
|
||||
if [[ "$deb_ver" =~ ^[0-9]+$ ]] && [ "$deb_ver" -ge 12 ]; then
|
||||
flags="$flags --break-system-packages"
|
||||
fi
|
||||
fi
|
||||
echo "$flags"
|
||||
}
|
||||
|
||||
# --- Helper: install from a local source directory ---
|
||||
install_from_local() {
|
||||
local src_dir="$1"
|
||||
msg "Installing VoxVera from local source ($src_dir)..."
|
||||
local flags
|
||||
flags=$(pip_flags)
|
||||
if pip install $flags "$src_dir"; then
|
||||
msg "VoxVera installed from local source."
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- Helper: remove stale standalone binary so it doesn't shadow pipx/pip entrypoints ---
|
||||
cleanup_stale_binary() {
|
||||
local bin="$HOME/.local/bin/voxvera"
|
||||
[ -f "$bin" ] || return 0
|
||||
# Only remove actual compiled binaries (PyInstaller/native), not pip/pipx Python scripts
|
||||
if file "$bin" 2>/dev/null | grep -qiE 'ELF|Mach-O'; then
|
||||
warn "Removing stale standalone binary at $bin (would shadow pipx/pip entrypoint)"
|
||||
rm -f "$bin"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Strategy 5: Pre-built binary (fallback — can lag behind main)
|
||||
# ============================================================
|
||||
install_binary() {
|
||||
msg "Strategy 5/5: Attempting to download pre-built binary..."
|
||||
|
||||
local latest
|
||||
latest=$(get_latest_tag)
|
||||
[ -n "$latest" ] || { warn "Could not determine latest release tag."; return 1; }
|
||||
|
||||
local install_dir="$HOME/.local/bin"
|
||||
mkdir -p "$install_dir"
|
||||
dest="$install_dir/voxvera"
|
||||
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
ARCH=$(uname -m)
|
||||
|
||||
case "$OS" in
|
||||
linux*) BINARY="voxvera-linux" ;;
|
||||
darwin*) BINARY="voxvera-macos" ;;
|
||||
*) BINARY="voxvera-linux" ;;
|
||||
local dest="$install_dir/voxvera"
|
||||
local os_name arch binary
|
||||
os_name="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
arch=$(uname -m)
|
||||
|
||||
case "$os_name" in
|
||||
linux*) binary="voxvera-linux" ;;
|
||||
darwin*) binary="voxvera-macos" ;;
|
||||
*) binary="voxvera-linux" ;;
|
||||
esac
|
||||
|
||||
# Try to download architecture-specific binary (e.g. voxvera-linux-x86_64)
|
||||
url="https://github.com/PR0M3TH3AN/VoxVera/releases/download/${latest}/${BINARY}-${ARCH}"
|
||||
# For Windows, PyInstaller might add .exe but here we focus on linux/mac
|
||||
|
||||
msg "Downloading from: $url"
|
||||
status=$(curl -w "%{http_code}" -fsSL "$url" -o "$dest" || echo "failed")
|
||||
|
||||
if [ "$status" = "200" ]; then
|
||||
|
||||
local url="${REPO_URL}/releases/download/${latest}/${binary}-${arch}"
|
||||
msg "Downloading: $url"
|
||||
|
||||
if download "$url" "$dest"; then
|
||||
chmod +x "$dest"
|
||||
msg "VoxVera binary (${ARCH}) installed to $dest"
|
||||
[[ ":$PATH:" != *":$HOME/.local/bin:"* ]] && warn "Add \$HOME/.local/bin to your PATH."
|
||||
exit 0
|
||||
# Verify it's actually an executable, not an HTML error page
|
||||
if file "$dest" | grep -qiE 'ELF|executable|Mach-O'; then
|
||||
msg "VoxVera binary (${arch}) installed to $dest"
|
||||
[[ ":$PATH:" != *":$HOME/.local/bin:"* ]] && warn "Add \$HOME/.local/bin to your PATH."
|
||||
return 0
|
||||
fi
|
||||
warn "Downloaded file is not a valid binary."
|
||||
fi
|
||||
|
||||
[ -f "$dest" ] && rm -f "$dest"
|
||||
warn "Binary download failed. Trying next method..."
|
||||
return 1
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Strategy 1: GitHub tarball — no git needed, always current with main
|
||||
# ============================================================
|
||||
install_tarball() {
|
||||
msg "Strategy 1/5: Downloading source tarball (no git required)..."
|
||||
local tmp_tar tmp_dir
|
||||
tmp_tar=$(mktemp /tmp/voxvera-XXXXXX.tar.gz)
|
||||
tmp_dir=$(mktemp -d /tmp/voxvera-src-XXXXXX)
|
||||
|
||||
if download "${REPO_URL}/archive/refs/heads/${BRANCH}.tar.gz" "$tmp_tar"; then
|
||||
if tar -xzf "$tmp_tar" -C "$tmp_dir" --strip-components=1 2>/dev/null; then
|
||||
if install_from_local "$tmp_dir"; then
|
||||
cleanup_stale_binary
|
||||
rm -rf "$tmp_tar" "$tmp_dir"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
warn "Tarball extraction failed."
|
||||
fi
|
||||
else
|
||||
warn "Binary download failed (status: $status). Falling back to source installation."
|
||||
[ -f "$dest" ] && rm "$dest"
|
||||
warn "Tarball download failed."
|
||||
fi
|
||||
fi
|
||||
|
||||
if command_exists pipx; then
|
||||
msg "Falling back to pipx installation (this may take a few minutes)..."
|
||||
if pipx install --force 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
|
||||
rm -rf "$tmp_tar" "$tmp_dir"
|
||||
warn "Tarball install failed. Trying next method..."
|
||||
return 1
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Strategy 2: Shallow git clone + local pip install
|
||||
# ============================================================
|
||||
install_shallow() {
|
||||
msg "Strategy 2/5: Attempting shallow git clone (--depth 1)..."
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d /tmp/voxvera-clone-XXXXXX)
|
||||
|
||||
if timeout "$NET_TIMEOUT" git clone --depth 1 --single-branch --branch "$BRANCH" \
|
||||
"${REPO_URL}.git" "$tmp_dir" 2>&1; then
|
||||
if install_from_local "$tmp_dir"; then
|
||||
cleanup_stale_binary
|
||||
rm -rf "$tmp_dir"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
warn "Shallow clone failed or timed out after ${NET_TIMEOUT}s."
|
||||
fi
|
||||
|
||||
rm -rf "$tmp_dir"
|
||||
warn "Shallow clone install failed. Trying next method..."
|
||||
return 1
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Strategy 3: pipx from git (isolated venv)
|
||||
# ============================================================
|
||||
install_pipx() {
|
||||
command_exists pipx || return 1
|
||||
msg "Strategy 3/5: Attempting pipx installation..."
|
||||
if timeout 180 pipx install --force "voxvera@git+${REPO_URL}.git@${BRANCH}"; then
|
||||
pipx inject voxvera setuptools 2>/dev/null || true
|
||||
pipx ensurepath --force
|
||||
cleanup_stale_binary
|
||||
msg "\nVoxVera installed/updated successfully via pipx."
|
||||
msg "IMPORTANT: Please restart your terminal or run 'source ~/.bashrc' (or your shell config) to use 'voxvera'."
|
||||
msg "IMPORTANT: Please restart your terminal or run 'source ~/.bashrc' to use 'voxvera'."
|
||||
return 0
|
||||
fi
|
||||
warn "pipx install failed. Trying next method..."
|
||||
return 1
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Strategy 4: pip from git
|
||||
# ============================================================
|
||||
install_pip_git() {
|
||||
msg "Strategy 4/5: Attempting pip install from git..."
|
||||
local flags
|
||||
flags=$(pip_flags)
|
||||
if timeout 180 pip install $flags "voxvera@git+${REPO_URL}.git@${BRANCH}"; then
|
||||
cleanup_stale_binary
|
||||
msg "VoxVera installed via pip."
|
||||
return 0
|
||||
fi
|
||||
warn "pip git install failed."
|
||||
return 1
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Run strategies in order — first success wins
|
||||
# ============================================================
|
||||
# Order rationale:
|
||||
# 1. Tarball — always up-to-date with main, no git needed, single download
|
||||
# 2. Shallow clone — small git transfer, also always current
|
||||
# 3. pipx — git clone internally, but isolated venv
|
||||
# 4. pip git — git clone internally, last resort
|
||||
# 5. Binary — pre-built release; only fallback since it can lag behind main
|
||||
for strategy in install_tarball install_shallow install_pipx install_pip_git install_binary; do
|
||||
if $strategy; then
|
||||
# Ensure ~/.local/bin is on PATH
|
||||
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
# Clear bash's cached command path so the new binary is found immediately
|
||||
hash -r 2>/dev/null || true
|
||||
|
||||
# Rebuild all existing sites so they pick up template/CSS changes
|
||||
if command_exists voxvera; then
|
||||
msg "Rebuilding existing sites with updated template..."
|
||||
voxvera rebuild-all 2>/dev/null || true
|
||||
if [[ "$OS_NAME" == linux* ]]; then
|
||||
msg "Installing Linux autostart recovery timer..."
|
||||
voxvera autostart 2>/dev/null || warn "Could not install the VoxVera autostart timer automatically. Run 'voxvera autostart' manually."
|
||||
else
|
||||
warn "Autostart was not configured automatically because this platform is experimental."
|
||||
fi
|
||||
msg "Inspecting installed runtime posture..."
|
||||
report_cli_status
|
||||
fi
|
||||
|
||||
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)"
|
||||
exit 0
|
||||
fi
|
||||
warn "pipx install failed, trying pip fallback..."
|
||||
fi
|
||||
done
|
||||
|
||||
# Fallback to pip
|
||||
msg "Installing/Updating VoxVera via pip..."
|
||||
PIP_OPTS="--user --upgrade"
|
||||
if [ -f /etc/debian_version ]; then
|
||||
DEB_VER=$(cat /etc/debian_version | cut -d. -f1)
|
||||
if [[ "$DEB_VER" =~ ^[0-9]+$ ]] && [ "$DEB_VER" -ge 12 ]; then
|
||||
PIP_OPTS="$PIP_OPTS --break-system-packages"
|
||||
fi
|
||||
fi
|
||||
if pip install $PIP_OPTS 'voxvera@git+https://github.com/PR0M3TH3AN/VoxVera.git@main'; then
|
||||
msg "VoxVera installed via pip."
|
||||
else
|
||||
die "Installation failed."
|
||||
fi
|
||||
|
||||
msg "\nVoxVera installed/updated successfully. Run 'voxvera check' to verify."
|
||||
die "All installation methods failed. Your network may be blocking or corrupting HTTPS traffic to GitHub.\nTry downloading manually from a working machine:\n ${REPO_URL}/archive/refs/heads/${BRANCH}.tar.gz\nThen copy to this machine and run:\n tar xzf VoxVera-main.tar.gz && pip install --user ./VoxVera-main"
|
||||
@@ -1,18 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
echo "Warning: AppImage packaging is experimental and is not yet validated for reliable background hidden-service hosting."
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
ARCH=$(uname -m)
|
||||
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
|
||||
OUTPUT="voxvera/resources/bin/VoxVera-$ARCH.AppImage"
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary not found: $BINARY. Run scripts/build_binaries.sh first." >&2
|
||||
echo "Binary not found: $BINARY. Building it first..."
|
||||
bash scripts/build_binaries.sh
|
||||
fi
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary still not found after build: $BINARY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APPDIR=build/AppDir
|
||||
rm -rf "$APPDIR"
|
||||
mkdir -p "$APPDIR/usr/bin"
|
||||
cp "$BINARY" "$APPDIR/usr/bin/voxvera"
|
||||
chmod +x "$APPDIR/usr/bin/voxvera"
|
||||
@@ -46,7 +55,8 @@ chmod +x appimagetool
|
||||
echo "Running appimagetool..."
|
||||
export ARCH
|
||||
mkdir -p voxvera/resources/bin
|
||||
./appimagetool --appimage-extract-and-run "$APPDIR" voxvera/resources/bin/VoxVera-x86_64.AppImage
|
||||
rm -f "$OUTPUT"
|
||||
./appimagetool --appimage-extract-and-run "$APPDIR" "$OUTPUT"
|
||||
rm appimagetool
|
||||
|
||||
echo "AppImage created at voxvera/resources/bin/VoxVera-x86_64.AppImage"
|
||||
echo "AppImage created at $OUTPUT"
|
||||
+12
-3
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
echo "Warning: Debian packaging is currently experimental until runtime dependencies and hidden-service recovery are validated end-to-end."
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
@@ -16,13 +18,19 @@ fi
|
||||
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary not found: $BINARY." >&2
|
||||
echo "Binary not found: $BINARY. Building it first..."
|
||||
bash scripts/build_binaries.sh
|
||||
fi
|
||||
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Binary still not found after build: $BINARY." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
VERSION=$(grep "__version__" voxvera/__init__.py | cut -d '"' -f 2)
|
||||
PKG_DIR="build/voxvera_${VERSION}_${DEB_ARCH}"
|
||||
OUTPUT="voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
|
||||
|
||||
echo "Building .deb package for version $VERSION ($DEB_ARCH)..."
|
||||
|
||||
@@ -40,6 +48,7 @@ sed -i "s/^Architecture: .*/Architecture: $DEB_ARCH/" "$PKG_DIR/DEBIAN/control"
|
||||
|
||||
# Build the package
|
||||
mkdir -p voxvera/resources/bin
|
||||
dpkg-deb --build "$PKG_DIR" "voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
|
||||
rm -f "$OUTPUT"
|
||||
dpkg-deb --build "$PKG_DIR" "$OUTPUT"
|
||||
|
||||
echo "Debian package created at voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
|
||||
echo "Debian package created at $OUTPUT"
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
echo "Warning: DMG packaging is experimental and is not yet validated for reliable background hidden-service hosting."
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Experimental package surface: not yet validated for reliable background hidden-service hosting.
|
||||
$toolsDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera.exe'
|
||||
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe'
|
||||
|
||||
$packageArgs = @{
|
||||
packageName = 'voxvera'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
app-id: org.voxvera.VoxVera
|
||||
# Experimental package surface: not yet validated for reliable background hidden-service hosting.
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '23.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Voxvera < Formula
|
||||
desc "Generate QR-coded flyers"
|
||||
desc "Experimental VoxVera formula"
|
||||
homepage "https://github.com/PR0M3TH3AN/VoxVera"
|
||||
url "https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera"
|
||||
version "0.1.0"
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ voxvera = ["templates/**", "src/**", "resources/**", "locales/**", "resources/bi
|
||||
|
||||
[project]
|
||||
name = "voxvera"
|
||||
version = "0.2.1"
|
||||
version = "0.3.11"
|
||||
description = "VoxVera CLI utilities"
|
||||
readme = "README.md"
|
||||
authors = [{name = "VoxVera"}]
|
||||
@@ -28,7 +28,7 @@ dependencies = [
|
||||
"htmlmin2",
|
||||
"pypdf",
|
||||
"wcwidth",
|
||||
"onionshare-cli",
|
||||
"setuptools",
|
||||
]
|
||||
[project.optional-dependencies]
|
||||
test = [
|
||||
|
||||
@@ -7,4 +7,6 @@ htmlmin2
|
||||
pypdf
|
||||
wcwidth
|
||||
onionshare-cli
|
||||
flask<2.3.0
|
||||
werkzeug<3.0.0
|
||||
|
||||
@@ -13,9 +13,19 @@ if ! command -v pyinstaller &> /dev/null; then
|
||||
pip install pyinstaller --break-system-packages
|
||||
fi
|
||||
|
||||
ARTIFACT_DIR="voxvera/resources/bin"
|
||||
BUILD_BINARY_NAME="voxvera-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf build/ dist_bin/
|
||||
mkdir -p voxvera/resources/bin
|
||||
python3 - <<'PY'
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
for path in (Path("build"), Path("dist_bin")):
|
||||
if path.exists():
|
||||
shutil.rmtree(path, ignore_errors=True)
|
||||
PY
|
||||
mkdir -p "$ARTIFACT_DIR"
|
||||
|
||||
echo "Building VoxVera binary for $(uname)..."
|
||||
|
||||
@@ -34,11 +44,12 @@ $PY_CMD --onefile \
|
||||
--add-data "voxvera/locales:voxvera/locales" \
|
||||
--add-data "voxvera/src:voxvera/src" \
|
||||
--add-data "voxvera/templates:voxvera/templates" \
|
||||
--add-data "voxvera/resources:voxvera/resources" \
|
||||
--add-data "voxvera/resources/tor:voxvera/resources/tor" \
|
||||
--add-data "support-matrix.json:." \
|
||||
--add-data "requirements.txt:." \
|
||||
--name "$BINARY_NAME" \
|
||||
--distpath voxvera/resources/bin \
|
||||
--distpath "$ARTIFACT_DIR" \
|
||||
voxvera/cli.py
|
||||
|
||||
echo "Binary build complete. Located in voxvera/resources/bin/"
|
||||
ls -lh voxvera/resources/bin/
|
||||
echo "Binary build complete. Located in $ARTIFACT_DIR/"
|
||||
ls -lh "$ARTIFACT_DIR"/
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "==> flake8"
|
||||
python3 -m flake8 voxvera tests \
|
||||
--jobs=1 \
|
||||
--exclude=voxvera/vendor \
|
||||
--ignore=E501,W503,W293,E402,E306,E704,E261,F841,E302,F401,E401,F541
|
||||
|
||||
echo "==> pytest"
|
||||
python3 -m pytest tests/ -v
|
||||
|
||||
echo "==> electron smoke tests"
|
||||
(cd gui/electron && npm test)
|
||||
|
||||
echo "==> dependency check"
|
||||
python3 -m voxvera.cli --lang en check
|
||||
|
||||
if [[ "$(uname -s)" == "Linux" ]]; then
|
||||
echo "==> platform smoke"
|
||||
bash scripts/platform-smoke.sh linux-cli
|
||||
fi
|
||||
|
||||
echo "==> build docs"
|
||||
python3 -m voxvera.cli build-docs
|
||||
|
||||
echo "==> build site"
|
||||
python3 -m voxvera.cli build-site
|
||||
|
||||
echo "All local checks passed."
|
||||
@@ -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"
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
TARGET="${1:-linux-cli}"
|
||||
TMP_DIR="${VOXVERA_SMOKE_TMPDIR:-$(mktemp -d /tmp/voxvera-platform-smoke-XXXXXX)}"
|
||||
mkdir -p "$TMP_DIR"
|
||||
|
||||
cleanup() {
|
||||
if [[ -z "${VOXVERA_SMOKE_TMPDIR:-}" ]]; then
|
||||
rm -rf "$TMP_DIR"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
command -v voxvera >/dev/null 2>&1 || {
|
||||
echo "voxvera is not on PATH" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "$TARGET" in
|
||||
linux-cli)
|
||||
EXPECTED_PLATFORM_ID="linux_cli_systemd"
|
||||
EXPECTED_TIER="supported"
|
||||
REQUIRE_ONIONSHARE="${VOXVERA_REQUIRE_ONIONSHARE:-1}"
|
||||
;;
|
||||
macos-cli)
|
||||
EXPECTED_PLATFORM_ID="macos_cli"
|
||||
EXPECTED_TIER="experimental"
|
||||
REQUIRE_ONIONSHARE="${VOXVERA_REQUIRE_ONIONSHARE:-0}"
|
||||
;;
|
||||
windows-cli)
|
||||
EXPECTED_PLATFORM_ID="windows_cli"
|
||||
EXPECTED_TIER="experimental"
|
||||
REQUIRE_ONIONSHARE="${VOXVERA_REQUIRE_ONIONSHARE:-0}"
|
||||
;;
|
||||
docker-cli)
|
||||
EXPECTED_PLATFORM_ID="linux_cli_systemd"
|
||||
EXPECTED_TIER="${VOXVERA_EXPECTED_TIER_OVERRIDE:-supported}"
|
||||
REQUIRE_ONIONSHARE="${VOXVERA_REQUIRE_ONIONSHARE:-0}"
|
||||
;;
|
||||
list)
|
||||
printf '%s\n' linux-cli macos-cli windows-cli docker-cli
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported smoke target: $TARGET" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
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" \
|
||||
EXPECTED_PLATFORM_ID="$EXPECTED_PLATFORM_ID" \
|
||||
EXPECTED_TIER="$EXPECTED_TIER" \
|
||||
VOXVERA_REQUIRE_TOR="${VOXVERA_REQUIRE_TOR:-0}" \
|
||||
VOXVERA_REQUIRE_AUTOSTART_ARTIFACTS="${VOXVERA_REQUIRE_AUTOSTART_ARTIFACTS:-0}" \
|
||||
VOXVERA_REQUIRE_ONIONSHARE="$REQUIRE_ONIONSHARE" \
|
||||
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"))
|
||||
|
||||
expected_platform_id = os.environ["EXPECTED_PLATFORM_ID"]
|
||||
expected_tier = os.environ["EXPECTED_TIER"]
|
||||
require_tor = os.environ.get("VOXVERA_REQUIRE_TOR") == "1"
|
||||
require_autostart_artifacts = os.environ.get("VOXVERA_REQUIRE_AUTOSTART_ARTIFACTS") == "1"
|
||||
require_onionshare = os.environ.get("VOXVERA_REQUIRE_ONIONSHARE") == "1"
|
||||
|
||||
errors = []
|
||||
|
||||
if platform_status.get("platform_id") != expected_platform_id:
|
||||
errors.append(
|
||||
f"platform-status platform_id mismatch: {platform_status.get('platform_id')} != {expected_platform_id}"
|
||||
)
|
||||
if platform_status.get("tier") != expected_tier:
|
||||
errors.append(f"platform-status tier mismatch: {platform_status.get('tier')} != {expected_tier}")
|
||||
if doctor.get("platform_id") != expected_platform_id:
|
||||
errors.append(f"doctor platform_id mismatch: {doctor.get('platform_id')} != {expected_platform_id}")
|
||||
if autostart.get("platform_id") != expected_platform_id:
|
||||
errors.append(f"autostart platform_id mismatch: {autostart.get('platform_id')} != {expected_platform_id}")
|
||||
|
||||
check_map = {check["name"]: check for check in doctor.get("checks", [])}
|
||||
for required_check in ("platform", "voxvera_cli"):
|
||||
if required_check not in check_map:
|
||||
errors.append(f"doctor missing required check: {required_check}")
|
||||
elif not check_map[required_check].get("ok"):
|
||||
errors.append(f"doctor check failed: {required_check} ({check_map[required_check].get('detail')})")
|
||||
|
||||
if require_onionshare:
|
||||
required_check = "onionshare_cli"
|
||||
if required_check not in check_map:
|
||||
errors.append(f"doctor missing required check: {required_check}")
|
||||
elif not check_map[required_check].get("ok"):
|
||||
errors.append(f"doctor check failed: {required_check} ({check_map[required_check].get('detail')})")
|
||||
|
||||
if require_tor:
|
||||
for required_check in ("tor_binary", "tor_socks_reachable"):
|
||||
if required_check not in check_map:
|
||||
errors.append(f"doctor missing Tor check: {required_check}")
|
||||
elif not check_map[required_check].get("ok"):
|
||||
errors.append(f"doctor Tor check failed: {required_check} ({check_map[required_check].get('detail')})")
|
||||
|
||||
if require_autostart_artifacts:
|
||||
if not autostart.get("artifacts"):
|
||||
errors.append("autostart status returned no artifacts")
|
||||
missing_artifacts = [artifact for artifact in autostart.get("artifacts", []) if not Path(artifact).exists()]
|
||||
if missing_artifacts:
|
||||
errors.append("autostart artifacts missing: " + ", ".join(missing_artifacts))
|
||||
|
||||
if errors:
|
||||
print("Platform smoke validation failed:", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f"- {error}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Platform smoke OK: {platform_status['label']} [{platform_status['tier']}]")
|
||||
print(f"Hosting model: {platform_status.get('hosting_model', '')}")
|
||||
failing_checks = [check["name"] for check in doctor.get("checks", []) if not check.get("ok")]
|
||||
if failing_checks:
|
||||
print("Doctor warnings: " + ", ".join(failing_checks))
|
||||
else:
|
||||
print("Doctor warnings: none")
|
||||
print("Autostart message: " + autostart.get("message", ""))
|
||||
PY
|
||||
|
||||
echo "Artifacts written to $TMP_DIR"
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
ARCH="${ARCH_OVERRIDE:-$(uname -m)}"
|
||||
if [[ "$ARCH" == "x86_64" ]]; then
|
||||
DEB_ARCH="amd64"
|
||||
else
|
||||
DEB_ARCH="$ARCH"
|
||||
fi
|
||||
|
||||
DEB_PATH="${1:-voxvera/resources/bin/voxvera_$(python3 - <<'PY'
|
||||
from voxvera import __version__
|
||||
print(__version__)
|
||||
PY
|
||||
)_${DEB_ARCH}.deb}"
|
||||
APPIMAGE_PATH="${2:-voxvera/resources/bin/VoxVera-${ARCH}.AppImage}"
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$tmpdir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
json_get() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
python3 - "$file" "$key" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path = sys.argv[2].split(".")
|
||||
data = json.load(open(sys.argv[1], "r", encoding="utf-8"))
|
||||
for part in path:
|
||||
data = data[part]
|
||||
if isinstance(data, list):
|
||||
print("\n".join(str(item) for item in data))
|
||||
else:
|
||||
print(data)
|
||||
PY
|
||||
}
|
||||
|
||||
require_non_empty_json_field() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value
|
||||
value="$(json_get "$file" "$key")"
|
||||
if [[ -z "${value// }" ]]; then
|
||||
echo "Expected non-empty JSON field '$key' in $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Validating Debian artifact: $DEB_PATH"
|
||||
if [[ ! -f "$DEB_PATH" ]]; then
|
||||
echo "Debian package not found: $DEB_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$tmpdir/deb-root" "$tmpdir/deb-data"
|
||||
dpkg-deb -x "$DEB_PATH" "$tmpdir/deb-root"
|
||||
|
||||
if [[ ! -x "$tmpdir/deb-root/usr/bin/voxvera" ]]; then
|
||||
echo "Packaged voxvera binary missing from .deb payload" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VOXVERA_DIR="$tmpdir/deb-data" \
|
||||
"$tmpdir/deb-root/usr/bin/voxvera" \
|
||||
--config "$tmpdir/deb-data/config.json" \
|
||||
init --non-interactive
|
||||
|
||||
if [[ ! -f "$tmpdir/deb-data/config.json" ]]; then
|
||||
echo ".deb validation failed: config.json was not created" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$tmpdir/deb-data/host/voxvera/index.html" ]]; then
|
||||
echo ".deb validation failed: built site missing index.html" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Validating AppImage artifact: $APPIMAGE_PATH"
|
||||
if [[ ! -f "$APPIMAGE_PATH" ]]; then
|
||||
echo "AppImage not found: $APPIMAGE_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x "$APPIMAGE_PATH"
|
||||
"$APPIMAGE_PATH" platform-status --json > "$tmpdir/platform-status.json"
|
||||
require_non_empty_json_field "$tmpdir/platform-status.json" "tier_description"
|
||||
require_non_empty_json_field "$tmpdir/platform-status.json" "hosting_model"
|
||||
|
||||
VOXVERA_DIR="$tmpdir/appimage-data" \
|
||||
"$APPIMAGE_PATH" \
|
||||
--config "$tmpdir/appimage-data/config.json" \
|
||||
init --non-interactive
|
||||
|
||||
if [[ ! -f "$tmpdir/appimage-data/config.json" ]]; then
|
||||
echo "AppImage validation failed: config.json was not created" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$tmpdir/appimage-data/host/voxvera/index.html" ]]; then
|
||||
echo "AppImage validation failed: built site missing index.html" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Release artifact validation passed."
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
"title": "TOP SECRET",
|
||||
"subtitle": "DO ~~NOT~~ DISTRIBUTE",
|
||||
"headline": "OPERATION VOX VERA",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message. \n\nUse memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting. \n\nPrivacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.\n\nJoin us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.\n",
|
||||
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
|
||||
"url_message": "Get VoxVera and start spreading your message.",
|
||||
"url": "https://github.com/PR0M3TH3AN/VoxVera",
|
||||
"tear_off_link": "https://voxvera.org/",
|
||||
"binary_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
"tear_off_link": "http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/",
|
||||
"footer_message": "0110010 0101011 0110010 0111101 0110100"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
+93
-51
@@ -40,12 +40,13 @@
|
||||
|
||||
[dir="rtl"] .qr-code-body {
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[dir="rtl"] .qr-code-url {
|
||||
text-align: right;
|
||||
margin-right: 0;
|
||||
margin-left: 10px;
|
||||
padding-right: 4px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
[dir="rtl"] .message {
|
||||
@@ -94,6 +95,7 @@
|
||||
|
||||
.print-button, .download-button, .lang-select {
|
||||
padding: 10px 20px;
|
||||
min-height: 42px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
background-color: #000;
|
||||
@@ -101,6 +103,12 @@
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
line-height: 1.2;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.lang-select {
|
||||
@@ -127,6 +135,7 @@
|
||||
.tear-off {
|
||||
display: flex;
|
||||
height: 1in; /* Fixed height for consistency */
|
||||
gap: 12px;
|
||||
padding: 5px;
|
||||
border: 1px dashed #000;
|
||||
font-size: 10px;
|
||||
@@ -139,9 +148,11 @@
|
||||
.tear-off-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-all;
|
||||
padding-top: 1px;
|
||||
word-wrap: normal;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: normal;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
.tear-off a {
|
||||
@@ -157,7 +168,6 @@
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 0px solid #000;
|
||||
margin-left: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -169,20 +179,21 @@
|
||||
|
||||
.content {
|
||||
width: calc(8.5in - 3.75in);
|
||||
padding: 20px;
|
||||
padding: 15px 20px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
page-break-inside: avoid;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
padding: 0;
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
letter-spacing: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -192,7 +203,10 @@
|
||||
|
||||
.distribute {
|
||||
font-size: 18px;
|
||||
line-height: 1.2;
|
||||
letter-spacing: 6px;
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -204,8 +218,8 @@
|
||||
}
|
||||
|
||||
.message {
|
||||
height: 6in; /* Fixed height to prevent footer push-out */
|
||||
margin-top: 10px;
|
||||
flex: 1;
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
@@ -236,8 +250,9 @@
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
@@ -253,19 +268,21 @@
|
||||
.qr-code-url {
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
hyphens: auto;
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
max-width: 60%;
|
||||
word-wrap: normal;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: normal;
|
||||
hyphens: none;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid #000;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -294,6 +311,14 @@
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.container.no-tear-offs .left-tear-offs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.container.no-tear-offs .content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body, html {
|
||||
width: 8.5in;
|
||||
@@ -326,10 +351,20 @@
|
||||
|
||||
.button-bar {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
padding: 20px 0 30px;
|
||||
}
|
||||
|
||||
#download-link {
|
||||
display: inline-flex;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#download-link .download-button {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Dropdown styles */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
@@ -378,7 +413,7 @@
|
||||
</style>
|
||||
<script>
|
||||
// VoxVera Multi-Language Engine
|
||||
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f"}, "landing": {"title": "\u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629", "subtitle": "~~\u0644\u0627~~ \u0644\u0644\u0646\u0634\u0631", "headline": "\u0639\u0645\u0644\u064a\u0629 VOX VERA", "content": "\u062a\u062d\u0631\u0631 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629 \u0645\u0639 VoxVera. \u0623\u062f\u0627\u0629 \u062a\u0633\u0648\u064a\u0642 \u0639\u0635\u0627\u0628\u0627\u062a \u0645\u062c\u0647\u0648\u0644\u0629 \u0648\u0646\u0634\u0631 \u0631\u0633\u0627\u0626\u0644. \u0633\u0648\u0627\u0621 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0623\u0648 \u0641\u064a \u0627\u0644\u0639\u0627\u0644\u0645 \u0627\u0644\u0645\u0627\u062f\u064a\u060c \u064a\u0645\u0643\u0646\u0647\u0627 \u062a\u0645\u0643\u064a\u0646\u0643 \u0645\u0646 \u0646\u0634\u0631 \u0623\u0641\u0643\u0627\u0631\u0643 \u0628\u062c\u0631\u0623\u0629\u060c \u0645\u062d\u0645\u064a\u0629 \u0628\u0625\u062e\u0641\u0627\u0621 \u0627\u0644\u0647\u0648\u064a\u0629 \u0627\u0644\u0643\u0627\u0645\u0644 (\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0633\u062a\u0636\u064a\u0641 \u0639\u0628\u0631 Tor). \u0642\u0645 \u0628\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0643\u0648\u062f\u060c \u0648\u0635\u0645\u0645 \u0645\u0648\u0642\u0639 \u0645\u0646\u0634\u0648\u0631\u060c \u0648\u0627\u0633\u062a\u0636\u064a\u0641\u0647 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0642\u0645 \u0628\u062a\u0636\u062e\u064a\u0645 \u0631\u0633\u0627\u0644\u062a\u0643.\n\n\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0645\u064a\u0645\u064a\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0623\u0641\u0643\u0627\u0631\u0643 \u0641\u064a \u0645\u062f\u0631\u0633\u062a\u0643\u060c \u0623\u0648 \u0645\u0643\u0627\u0646 \u0639\u0645\u0644\u0643\u060c \u0623\u0648 \u0645\u062c\u062a\u0645\u0639\u0627\u062a\u0643 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a\u060c \u0623\u0648 \u062d\u062a\u0649 \u0639\u0644\u0649 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0639\u0627\u0644\u0645. \u062a\u0636\u0645\u0646 VoxVera \u062a\u0631\u062f\u062f \u0635\u062f\u0649 \u0631\u0633\u0627\u0644\u062a\u0643 \u0628\u0639\u064a\u062f\u0627\u064b \u0648\u0639\u0644\u0649 \u0646\u0637\u0627\u0642 \u0648\u0627\u0633\u0639\u060c \u0645\u0639 \u0623\u0642\u0633\u0627\u0645 \u0642\u0627\u0628\u0644\u0629 \u0644\u0644\u0646\u0632\u0639 \u062a\u062a\u0645\u064a\u0632 \u0628\u0631\u0648\u0627\u0628\u0637 \u0641\u0631\u064a\u062f\u0629 \u0648\u0623\u0643\u0648\u0627\u062f QR \u0644\u0633\u0647\u0648\u0644\u0629 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0637\u0628\u0627\u0639\u0629.\n\n\u064a\u0645\u0643\u0646 \u0627\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629. \u064a\u0645\u0643\u0646 \u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u0645\u0646\u0634\u0648\u0631\u0627\u062a \u0639\u0628\u0631 \u0634\u0628\u0643\u0629 Tor\u060c \u0645\u0645\u0627 \u064a\u062d\u0645\u064a \u0627\u0644\u0645\u0636\u064a\u0641 \u0648\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645\u064a\u0646 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629.\n\n\u0627\u0646\u0636\u0645 \u0625\u0644\u064a\u0646\u0627 \u0641\u064a \u062b\u0648\u0631\u0629 \u062a\u0642\u062f\u0631 \u0627\u0644\u062d\u0642\u064a\u0642\u0629 \u0648\u0627\u0644\u0634\u0641\u0627\u0641\u064a\u0629. \u0645\u0639\u0627\u064b\u060c \u064a\u0645\u0643\u0646\u0646\u0627 \u0628\u0646\u0627\u0621 \u0634\u0628\u0643\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0637\u0646\u064a\u0646 \u0627\u0644\u0645\u0633\u062a\u0646\u064a\u0631\u064a\u0646 \u0627\u0644\u0630\u064a\u0646 \u0644\u0627 \u064a\u062e\u0634\u0648\u0646 \u0627\u0644\u062a\u062d\u062f\u062b.", "url_message": "\u0627\u062d\u0635\u0644 \u0639\u0644\u0649 VoxVera \u0648\u0627\u0628\u062f\u0623 \u0628\u0646\u0634\u0631 \u0631\u0633\u0627\u0644\u062a\u0643.", "name": "Vox Vera Printable Flyers"}}, "de": {"meta": {"language_name": "Deutsch", "flag": "\ud83c\udde9\ud83c\uddea"}, "web": {"teLine truncated
|
||||
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f", "download_attachment": "\u062a\u0646\u0632\u064a\u0644 \u0627\u0644\u0645\u0631\u0641\u0642", "tor_browser_download_url": "https://www.torproject.org/ar/download/"}, "landing": {"title": "\u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629", "subtitle": "~~\u0644\u0627~~ \u0644\u0644\u0646\u0634\u0631", "headline": "\u0639\u0645\u0644\u064a\u0629 VOX VERA", "content": "\u062a\u062d\u0631\u0631 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629 \u0645\u0639 VoxVera. \u0623\u062f\u0627\u0629 \u062a\u0633\u0648\u064a\u0642 \u0639\u0635\u0627\u0628\u0627\u062a \u0645\u062c\u0647\u0648\u0644\u0629 \u0648\u0646\u0634\u0631 \u0631\u0633\u0627\u0626\u0644. \u0633\u0648\u0627\u0621 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0623\u0648 \u0641\u064a \u0627\u0644\u0639\u0627\u0644\u0645 \u0627\u0644\u0645\u0627\u062f\u064a\u060c \u064a\u0645\u0643\u0646\u0647\u0627 \u062a\u0645\u0643\u064a\u0646\u0643 \u0645\u0646 \u0646\u0634\u0631 \u0623\u0641\u0643\u0627\u0631\u0643 \u0628\u062c\u0631\u0623\u0629\u060c \u0645\u062d\u0645\u064a\u0629 \u0628\u0625\u062e\u0641\u0627\u0621 \u0627\u0644\u0647\u0648\u064a\u0629 \u0627\u0644\u0643\u0627\u0645\u0644 (\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0633\u062a\u0636\u064a\u0641 \u0639\u0628\u0631 Tor). \u0642\u0645 \u0628\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0643\u0648\u062f\u060c \u0648\u0635\u0645\u0645 \u0645\u0648\u0642\u0639 \u0645\u0646\u0634\u0648\u0631\u060c \u0648\u0627\u0633\u062a\u0636\u064a\u0641\u0647 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0642\u0645 \u0628\u062a\u0636\u062e\u064a\u0645 \u0631\u0633\u0627\u0644\u062a\u0643.<br><br>\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0645\u064a\u0645\u064a\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0623\u0641\u0643\u0627\u0631\u0643 \u0641\u064a \u0645\u062f\u0631\u0633\u062a\u0643\u060c \u0623\u0648 \u0645\u0643\u0627\u0646 \u0639\u0645\u0644\u0643\u060c \u0623\u0648 \u0645\u062c\u062a\u0645\u0639\u0627\u062a\u0643 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a\u060c \u0623\u0648 \u062d\u062a\u0649 \u0639\u0644\u0649 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0639\u0627\u0644\u0645. \u062a\u0636\u0645\u0646 VoxVera \u062a\u0631\u062f\u062f \u0635\u062f\u0649 \u0631\u0633\u0627\u0644\u062a\u0643 \u0628\u0639\u064a\u062f\u0627\u064b \u0648\u0639\u0644\u0649 \u0646\u0637\u0627\u0642 \u0648\u0627\u0633\u0639\u060c \u0645\u0639 \u0623\u0642\u0633\u0627\u0645 \u0642\u0627\u0628\u0644\u0629 \u0644\u0644\u0646\u0632\u0639 \u062a\u062a\u0645\u064a\u0632 \u0628\u0631\u0648\u0627\u0628\u0637 \u0641\u0631\u064a\u062f\u0629 \u0648\u0623\u0643\u0648\u0627\u062f QR \u0644\u0633\u0647\u0648\u0644\u0629 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0637\u0628\u0627\u0639\u0629.<br><br>\u064a\u0645\u0643\u0646 \u0627\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629. \u064a\u0645\u0643\u0646 \u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u0645\u0646\u0634\u0648\u0631\u0627\u062a \u0639\u0628\u0631 \u0634\u0628\u0643\u0629 Tor\u060c \u0645\u0645\u0627 \u064a\u062d\u0645\u064a \u0627\u0644\u0645\u0636\u064a\u0641 \u0648\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645\u064a\u0646 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629.<br><br>\u0627\u0646\u0636\u0645 \u0625\u0644\u064a\u0646\u0627 \u0641\u064a \u062b\u0648\u0631\u0629 \u062a\u0642\u062f\u0631 \u0627\u0644\u062d\u0642\u064a\u0642\u0629 \u0648\u0627\u0644\u0634\u0641\u0627\u0641\u064a\u0629. \u0645\u0639\u0627\u064b\u060c \u064a\u0645\u0643\u0646\u0646\u0627 \u0628\u0646\u0627\u0621 \u0634\u0628\u0643\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0637\u0646\u064a\u0646 \u0627\u0644\u0645\u0633\u062a\u0646\u064a\u0631\u064a\u0646 \u0627\u0644\u0630\u064a\u0646 \u0644\u0627 \u064a\u062e\u0634\u0648\u0646 \u0627\u0644\u062a\u062d\u062f\u062b.", "url_message": "\u0627\u062d\u0635\u0644 \u0639\u0644\u0649 VoxVera \u0648\u0627\u0628\u062f\u0623 \u0628\u0646\u0634\Line truncated
|
||||
|
||||
function t(path, lang) {
|
||||
const parts = path.split('.');
|
||||
@@ -413,6 +448,19 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('[data-i18n-href]').forEach(el => {
|
||||
const path = el.getAttribute('data-i18n-href');
|
||||
const href = t(path, lang);
|
||||
if (href) {
|
||||
el.href = href;
|
||||
}
|
||||
});
|
||||
const torBrowserHref = t('web.tor_browser_download_url', lang);
|
||||
if (torBrowserHref && torBrowserHref !== 'web.tor_browser_download_url') {
|
||||
document.querySelectorAll('.tor-browser-link').forEach(el => {
|
||||
el.href = torBrowserHref;
|
||||
});
|
||||
}
|
||||
document.getElementById('lang-selector').value = lang;
|
||||
document.documentElement.lang = lang;
|
||||
}
|
||||
@@ -437,13 +485,13 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="container ">
|
||||
<div class="left-tear-offs">
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -453,9 +501,9 @@
|
||||
<!-- Repeat for other tear-offs -->
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -464,9 +512,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -475,9 +523,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -486,9 +534,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -497,9 +545,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -508,9 +556,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -519,9 +567,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -530,9 +578,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -541,9 +589,9 @@
|
||||
</div>
|
||||
<div class="tear-off">
|
||||
<div class="tear-off-text">
|
||||
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
|
||||
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
|
||||
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
|
||||
<span data-i18n="web.tear_off_footer">click the button below to print</span>
|
||||
</div>
|
||||
<div class="qr-code">
|
||||
@@ -557,18 +605,12 @@
|
||||
<h1 id="operation-title-2" data-i18n="landing.headline">OPERATION VOX VERA</h1>
|
||||
<hr>
|
||||
<div class="message">
|
||||
<div class="text-container" id="main-content" contenteditable="false" data-i18n="landing.content">Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.
|
||||
|
||||
Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.
|
||||
|
||||
Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.
|
||||
|
||||
Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.
|
||||
</div>
|
||||
<div class="text-container" id="main-content" contenteditable="false" data-i18n="landing.content">Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.</div>
|
||||
<div class="qr-code-body">
|
||||
|
||||
<div class="qr-code-url">
|
||||
<span class="url-message" data-i18n="landing.url_message">Get VoxVera and start spreading your message.</span><br><br>
|
||||
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
|
||||
<a id="qr-link-2" href="https://github.com/PR0M3TH3AN/VoxVera" target="_blank">https://github.com/PR0M3TH3AN/VoxVera</a>
|
||||
</div>
|
||||
<img src="qrcode-content.png" alt="QR Code">
|
||||
@@ -577,7 +619,7 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
<hr>
|
||||
<div class="footer">
|
||||
<p class="credit"><span data-i18n="web.built_with">built with</span> <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
|
||||
<p class="binary" id="binary-message">0110010 0101011 0110010 0111101 0110100</p>
|
||||
<p class="binary" id="footer-message">0110010 0101011 0110010 0111101 0110100</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -589,4 +631,4 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 568 B |
@@ -0,0 +1,156 @@
|
||||
{
|
||||
"generated_at": "2026-03-19",
|
||||
"default_supported_target": "linux_cli_systemd",
|
||||
"support_tiers": {
|
||||
"supported": "Validated for persistent hidden-service hosting with documented recovery behavior.",
|
||||
"beta": "Feature-complete enough for broader testing, but not yet validated for production-grade recovery claims.",
|
||||
"experimental": "May install or run, but is not yet validated for reliable background hidden-service hosting.",
|
||||
"unsupported": "Not currently a target for reliable hosting."
|
||||
},
|
||||
"platforms": [
|
||||
{
|
||||
"id": "linux_cli_systemd",
|
||||
"label": "Linux CLI with systemd --user",
|
||||
"tier": "supported",
|
||||
"hosting_model": "System Tor or Tor Browser plus onionshare-cli, with VoxVera-managed recurring systemd user timer recovery.",
|
||||
"distribution_surfaces": [
|
||||
"install.sh",
|
||||
"voxvera-install.sh",
|
||||
"pip/pipx",
|
||||
"linux binary"
|
||||
],
|
||||
"required_capabilities": [
|
||||
"manual_build_and_serve",
|
||||
"persistent_onion_identity",
|
||||
"background_recovery_after_boot",
|
||||
"background_recovery_after_offline_periods",
|
||||
"inspectable_logs"
|
||||
],
|
||||
"current_notes": [
|
||||
"Canonical deployment target.",
|
||||
"Systemd user timer retries start-all every few minutes."
|
||||
],
|
||||
"next_gaps": [
|
||||
"Real-machine reboot and network-loss validation evidence should be captured in CI/docs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "docker_cli",
|
||||
"label": "Docker CLI container",
|
||||
"tier": "experimental",
|
||||
"hosting_model": "Containerized VoxVera runtime with state under /flyers and periodic start-all retries.",
|
||||
"distribution_surfaces": [
|
||||
"Dockerfile",
|
||||
"ghcr image"
|
||||
],
|
||||
"required_capabilities": [
|
||||
"persistent_onion_identity",
|
||||
"restart_policy_integration",
|
||||
"validated_state_volume_layout"
|
||||
],
|
||||
"current_notes": [
|
||||
"State now routes through VOXVERA_DIR=/flyers.",
|
||||
"Not yet validated as a production-grade hidden-service host."
|
||||
],
|
||||
"next_gaps": [
|
||||
"Decide whether the canonical model is single-container or compose-based.",
|
||||
"Validate restart behavior with real container restart policies."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "macos_cli",
|
||||
"label": "macOS CLI",
|
||||
"tier": "experimental",
|
||||
"hosting_model": "CLI runtime with launchd autostart path and external Tor/OnionShare dependencies.",
|
||||
"distribution_surfaces": [
|
||||
"install.sh",
|
||||
"pip/pipx",
|
||||
"macOS binary",
|
||||
"DMG"
|
||||
],
|
||||
"required_capabilities": [
|
||||
"manual_build_and_serve",
|
||||
"launchd_recovery",
|
||||
"persistent_onion_identity"
|
||||
],
|
||||
"current_notes": [
|
||||
"Installer and autostart warnings are explicit.",
|
||||
"No validated background recovery claim."
|
||||
],
|
||||
"next_gaps": [
|
||||
"Define the canonical Tor/OnionShare discovery model.",
|
||||
"Validate launchd behavior after login, reboot, and offline periods."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "windows_cli",
|
||||
"label": "Windows CLI",
|
||||
"tier": "experimental",
|
||||
"hosting_model": "CLI runtime with Task Scheduler autostart path and external Tor/OnionShare dependencies.",
|
||||
"distribution_surfaces": [
|
||||
"install.ps1",
|
||||
"windows binary",
|
||||
"Chocolatey"
|
||||
],
|
||||
"required_capabilities": [
|
||||
"manual_build_and_serve",
|
||||
"task_scheduler_recovery",
|
||||
"persistent_onion_identity",
|
||||
"consistent_binary_naming"
|
||||
],
|
||||
"current_notes": [
|
||||
"Installer warnings are explicit.",
|
||||
"Packaging surfaces are not yet trustworthy for hosting claims."
|
||||
],
|
||||
"next_gaps": [
|
||||
"Unify binary naming and installer expectations.",
|
||||
"Define canonical Tor and OnionShare discovery on Windows.",
|
||||
"Validate restart behavior after reboot and reconnect."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "electron_gui",
|
||||
"label": "Electron GUI",
|
||||
"tier": "experimental",
|
||||
"hosting_model": "GUI frontend over the CLI runtime.",
|
||||
"distribution_surfaces": [
|
||||
"gui/electron"
|
||||
],
|
||||
"required_capabilities": [
|
||||
"shared_runtime_contract_with_cli",
|
||||
"status_visibility",
|
||||
"non-destructive_window_close_behavior"
|
||||
],
|
||||
"current_notes": [
|
||||
"GUI is documented as experimental."
|
||||
],
|
||||
"next_gaps": [
|
||||
"Remove duplicate runtime logic where possible.",
|
||||
"Ensure GUI is a controller over supported runtime surfaces, not a separate hosting model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "flatpak_appimage_homebrew_chocolatey",
|
||||
"label": "Package-only surfaces",
|
||||
"tier": "experimental",
|
||||
"hosting_model": "Artifact distribution only until runtime parity exists.",
|
||||
"distribution_surfaces": [
|
||||
"Flatpak",
|
||||
"AppImage",
|
||||
"Homebrew",
|
||||
"Chocolatey",
|
||||
"Debian package"
|
||||
],
|
||||
"required_capabilities": [
|
||||
"runtime_dependency_model",
|
||||
"support-tier-specific_release_checks"
|
||||
],
|
||||
"current_notes": [
|
||||
"All are now explicitly marked experimental."
|
||||
],
|
||||
"next_gaps": [
|
||||
"Do not promote any package surface until the underlying platform runtime is validated."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
import pkg_resources
|
||||
import setuptools
|
||||
print("pkg_resources available")
|
||||
except ImportError as e:
|
||||
print(f"Failed to import: {e}")
|
||||
|
||||
from voxvera import cli
|
||||
cli._internal_onionshare()
|
||||
@@ -0,0 +1,2 @@
|
||||
import pkg_resources
|
||||
print("Success")
|
||||
@@ -0,0 +1,2 @@
|
||||
import pkg_resources
|
||||
print("Success")
|
||||
Loaded 100 of 143 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user