CellNetwork page (CellPanel):
- Internet Sharing section below service toggles
- Toggle: 'Offer my internet to <cell>' (calls PUT /api/cells/<n>/exit-offer)
- Read-only indicator: whether remote cell offers internet back
- Contextual hints explaining what each party needs to do next
Peers page:
- Fetches connected cells on mount
- Edit modal: Internet Exit dropdown (route-via) showing all connected cells
with ✓ marker for cells that have offered internet
- Warning if selected cell hasn't offered internet yet
- On save, calls PUT /api/peers/<n>/route-via only when value changed
- Table badge shows 'via <cell>' for peers with active routing
api.js:
- cellLinkAPI.setExitOffer(cellName, offered)
- peerRegistryAPI.setRouteVia(peerName, viaCell)
Tests (vitest + @testing-library/react):
- 19 new frontend tests in src/__tests__/
- CellNetworkInternetSharing.test.jsx (10 tests)
- PeersRouteVia.test.jsx (9 tests)
- make test-webui target runs them via docker node:18-alpine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CellNetwork.jsx CopyButton: use execCommand fallback when clipboard API
is unavailable (HTTP non-localhost context)
- Makefile reset-admin-password: run inside cell-api container via docker exec
so bcrypt and all deps are available without host installation
- docker-compose.yml: mount ./scripts:/app/scripts:ro in cell-api so the
reset script is accessible inside the container
- scripts/reset_admin_password.py: auto-detect API module path and data dir
so the script works in both host (api/ sibling) and container (/app) layouts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runtime config files exist on disk but are now gitignored. A bare git pull
conflicts with them. Stash (including untracked) before pulling and pop after.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix make test: was pointing to non-existent api/tests/, now runs unit tests
correctly with --ignore=e2e --ignore=integration
- Remove dead phase test targets (test-phase1..4, test-all-phases) that all
referenced cd api && pytest tests/ (non-existent path)
- Add .test_admin_pass file: reset_admin_password.py now writes a persistent
test password file alongside .admin_initial_password; the API never deletes
it (unlike .admin_initial_password which is consumed on first startup)
- Update both integration/conftest.py and e2e/helpers/admin_password.py to
read .test_admin_pass before .admin_initial_password — so tests work after
make restart without needing PIC_ADMIN_PASS env var
- Add AI collaboration rules to CLAUDE.md (auto-loaded every session)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. make reset/show-admin-password: use sudo so data/api/ owned-by-root
files are writable without explicit sudo prefix
2. Peers.jsx: remove one-time password modal on peer creation — admin
already knows the password they typed; replace with a success toast
showing peer name and provisioned accounts
3. WireGuard.jsx + Peers.jsx: add credentials:'include' to every raw
fetch() call (7 calls across two files, plus fix one hardcoded
localhost:3000 URL); the port check and peer status calls were
returning 401 because they didn't send the session cookie
4. test_admin_wireguard.py: update test to match new toast flow (no modal),
add Scenario 10 test that verifies the port check badge renders on the
WireGuard page after the credentials fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
make show-admin-password — prints the admin password from the initial
setup file if the API hasn't consumed it yet; otherwise prompts to reset
make reset-admin-password — generates a strong random password, updates
auth_users.json directly, writes it back to the setup file, and prints
it prominently so it's easy to copy
Also enhances reset_admin_password.py with --show, --generate flags and
a clear banner output, and adds both targets to make help.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- All test targets now use python3 -m pytest (not bare pytest)
- test-e2e-deps uses sudo pip3 --break-system-packages and
sudo python3 -m playwright install (Debian externally-managed env)
- test-e2e-wg uses sudo -E python3 -m pytest (preserves PATH/env)
- reset-test-admin-pass uses make ifndef guard instead of shell ?: expansion
- Remove stale -m markers from test targets (filters were redundant)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sprint 1 — Security & correctness:
- Restore all 10 commented-out is_local_request() checks (vault, containers, images, volumes)
- Fix XFF spoofing: only trust the LAST X-Forwarded-For entry (Caddy's append), not all
- Require prefix length in wireguard.address (was accepting bare IPs like 10.0.0.1)
- Validate service_access list in add_peer (valid: calendar/files/mail/webdav)
- Fix dhcp/reservations POST/DELETE: unpack mac/ip/hostname from body (was passing dict as positional arg)
- Fix network/test POST: remove spurious data arg (test_connectivity takes no args)
- Fix remove_peer: clear iptables rules and regenerate DNS ACLs on deletion (was leaving stale rules)
- Fix CoreDNS reload: SIGHUP → SIGUSR1 (SIGHUP kills the process; SIGUSR1 triggers reload plugin)
- Remove local.{domain} block from Corefile template (local.zone doesn't exist, caused log spam)
- Fix routing_manager._remove_nat_rule: targeted -D instead of flushing entire POSTROUTING chain
Sprint 2 — State consistency:
- Atomic config writes in config_manager, ip_utils, firewall_manager, network_manager
(write to .tmp → fsync → os.replace, prevents truncated files on kill)
- backup_config: now also backs up Caddyfile, Corefile, .env, DNS zone files
- restore_config: restores all of the above so config stays consistent after restore
Sprint 3 — Dead code / documentation:
- Remove CellManager instantiation from app startup (was never called, double-instantiated all managers)
- Document routing_manager scope (targets host, not cell-wireguard; methods not called by any active route)
Sprint 4 — Test infrastructure:
- Add tests/conftest.py with shared tmp_dir, tmp_config_dir, tmp_data_dir, flask_client fixtures
- Add tests/test_config_validation.py: 400 paths for ip_range, port, wireguard.address validation
- Add tests/test_ip_utils_caddyfile.py: 14 tests for write_caddyfile (was completely untested)
- Expand test_app_misc.py: 7 new is_local_request tests covering XFF spoofing and cell-network IPs
- Add --cov-fail-under=70 to make test-coverage
- Add pre-commit hook that runs pytest before every commit
414 tests pass (was 372).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously 'n' still ran --rmi all, removing Docker images. Now:
- n (default): docker-compose down only — containers gone, images and
data/config untouched; 'make start' brings everything back immediately
- y: full wipe — containers, images, config/ and data/ all removed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After make uninstall (which wipes config/ and data/), running make update
directly failed with "Couldn't find env file: config/mail/mailserver.env"
because docker-compose needs the generated config files to exist.
make update now checks for the sentinel config file and calls make setup
first if it's missing, so uninstall → update works as a valid flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows a warning then asks: y = full wipe, n/Enter = stop+remove images
only (keep config/data), anything else = cancel. Updates help + README.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- update: git pull + rebuild + restart
- reinstall: full wipe (config/data) + setup + start
- uninstall: stop, remove images, wipe config/data
- logs-<svc>: follow logs for any single service
- shell-<svc>: exec into any container (bash with sh fallback)
- backup: use sudo tar to read container-owned files
- help: restructured with all commands documented
- README: updated Quick Start + added Management Commands reference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scripts/check_deps.sh now checks and installs all prerequisites:
git, curl, openssl, python3, python3-cryptography, docker, docker-compose.
Runs apt-get update only once if anything needs installing.
Also adds current user to docker group if missing.
Makefile calls it with sudo so it has the rights to install packages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sudo -n skips apt silently if no sudo rights (SSH non-interactive).
Falls back to python3 -m pip install --user which never needs root,
then ensurepip if pip module is missing. Friendly error if all fail.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add check-deps target: auto-installs python3, pip3, cryptography via
apt (with pip fallback) before running setup_cell.py
- Add sudo chown in setup to reclaim config/data dirs if containers have
taken ownership (e.g. re-running setup after make start)
- Pass PUID/PGID=$(id -u/g) to docker-compose so linuxserver/wireguard
chowns its config dir to the host user instead of hardcoded UID 911
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docker compose up --build ensures the API and webui images are always built
from the current source on first install or after code changes, so users
don't need a separate make build-api step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows running make setup on hosts without wg binary or Python cryptography
library by passing pre-generated keys from another machine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DC variable auto-detects available command at build time.
Also fixes backup and dev targets that incorrectly used $(DC).yml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py: ConfigManager now uses CONFIG_DIR env var for config file path
instead of hardcoded './config/cell_config.json' — config was being read
from the image's working directory, making all settings writes ephemeral
(lost on container restart)
- wireguard_manager: generate_config uses configured address/port instead of
hardcoded 10.0.0.1 in DNAT rules and Address field
- scripts/setup_cell.py: full setup script — generates WireGuard keys (wg
binary or Python cryptography fallback), writes wg0.conf and cell_config.json
with correct _identity key; CELL_NAME / VPN_ADDRESS / WG_PORT env vars
- Makefile: setup target passes env vars through; build-api / build-webui targets
- README: replace install.sh references with make setup && make start
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>