# Personal Internet Cell - Makefile # Provides easy commands for managing the cell .PHONY: help start stop restart status logs clean setup check-deps init-peers \ update reinstall uninstall \ build build-api build-webui \ start-dns start-api start-wg start-webui \ backup restore \ test test-all test-unit test-coverage test-api test-cli \ test-phase1 test-phase2 test-phase3 test-phase4 test-all-phases \ test-integration test-integration-readonly \ test-e2e-deps test-e2e-api test-e2e-ui test-e2e-wg test-e2e \ reset-test-admin-pass \ show-admin-password reset-admin-password \ show-routes add-peer list-peers # Detect docker compose command (v2 plugin preferred, fallback to v1 standalone) DC := $(shell docker compose version >/dev/null 2>&1 && echo "docker compose" || echo "docker-compose") # Default target help: @echo "Personal Internet Cell - Management Commands" @echo "" @echo "First install:" @echo " check-deps - Install all required system packages (python3, docker, etc.)" @echo " setup - Generate keys, write configs, create data dirs" @echo " Env vars: CELL_NAME=mycell CELL_DOMAIN=cell VPN_ADDRESS=10.0.0.1/24 WG_PORT=51820" @echo " init-peers - Reset peer list to empty" @echo "" @echo "Lifecycle:" @echo " start - Start all services" @echo " stop - Stop all services" @echo " restart - Restart all services" @echo " status - Show container status + API health" @echo " logs - Follow logs from all services" @echo " logs- - Follow logs for one service (e.g. make logs-api)" @echo " shell- - Open shell in a container (e.g. make shell-api)" @echo "" @echo "Updates & reinstall:" @echo " update - git pull + rebuild + restart (deploy latest code)" @echo " reinstall - Full wipe and fresh install from current git checkout" @echo " uninstall - Stop + remove containers; prompts whether to also delete data" @echo "" @echo "Build:" @echo " build - Rebuild API image" @echo " build-api - Rebuild API image (no cache)" @echo " build-webui - Rebuild Web UI image (no cache)" @echo "" @echo "Individual services:" @echo " start-dns - Start DNS only" @echo " start-api - Start API only" @echo " start-wg - Start WireGuard only" @echo "" @echo "Maintenance:" @echo " backup - Backup config + data to backups/" @echo " restore - List available backups" @echo " clean - Remove containers and volumes (keeps config/data dirs)" @echo " show-admin-password - Print the admin password (reads setup file or prompts to reset)" @echo " reset-admin-password - Generate a new random admin password and print it" @echo "" @echo "Tests:" @echo " test - Run all tests" @echo " test-coverage - Run tests with HTML coverage report" @echo " test-integration - Full integration tests (needs running stack)" @echo " test-integration-readonly - Read-only integration tests (safe to run anytime)" @echo "" @echo "Peers:" @echo " list-peers - List configured WireGuard peers" @echo " show-routes - Show WireGuard routing table" # ── Dependencies & setup ────────────────────────────────────────────────────── check-deps: @sudo sh scripts/check_deps.sh setup: check-deps @echo "Setting up Personal Internet Cell..." @sudo chown -R $$(id -u):$$(id -g) config/ data/ 2>/dev/null || true CELL_NAME=$(or $(CELL_NAME),mycell) \ CELL_DOMAIN=$(or $(CELL_DOMAIN),cell) \ VPN_ADDRESS=$(or $(VPN_ADDRESS),10.0.0.1/24) \ WG_PORT=$(or $(WG_PORT),51820) \ WG_PRIVATE_KEY="$(WG_PRIVATE_KEY)" \ WG_PUBLIC_KEY="$(WG_PUBLIC_KEY)" \ python3 scripts/setup_cell.py init-peers: @echo "Initializing peer configuration..." @echo '[]' > data/api/peers.json @echo "Peer configuration initialized." # ── Lifecycle ───────────────────────────────────────────────────────────────── start: @echo "Starting Personal Internet Cell..." PUID=$$(id -u) PGID=$$(id -g) $(DC) up -d --build @echo "Services started. Check status with 'make status'" stop: @echo "Stopping Personal Internet Cell..." PUID=$$(id -u) PGID=$$(id -g) $(DC) down @echo "Services stopped." restart: @echo "Restarting Personal Internet Cell..." PUID=$$(id -u) PGID=$$(id -g) $(DC) restart @echo "Services restarted." status: @echo "Personal Internet Cell Status:" @echo "================================" $(DC) ps @echo "" @echo "API Status:" @curl -s http://localhost:3000/health || echo "API not responding" logs: $(DC) logs -f logs-%: $(DC) logs -f $* shell-%: docker exec -it cell-$* /bin/bash 2>/dev/null || docker exec -it cell-$* /bin/sh # ── Updates & reinstall ─────────────────────────────────────────────────────── update: @echo "Pulling latest code..." git pull @if [ ! -f config/mail/mailserver.env ]; then \ echo "Config missing — running setup first..."; \ $(MAKE) setup; \ fi @echo "Rebuilding and restarting services..." PUID=$$(id -u) PGID=$$(id -g) $(DC) up -d --build @echo "Update complete. Run 'make status' to verify." reinstall: @echo "Reinstalling Personal Internet Cell from scratch..." PUID=$$(id -u) PGID=$$(id -g) $(DC) down -v 2>/dev/null || true @sudo rm -rf config/ data/ @$(MAKE) setup @$(MAKE) start @echo "Reinstall complete." uninstall: @echo "" @echo "This will stop and remove all containers." @echo "" @printf "Also delete config/ and data/? This cannot be undone. [y/N/cancel]: "; \ read ans; \ case "$$ans" in \ y|Y) \ echo "Stopping containers and removing images..."; \ PUID=$$(id -u) PGID=$$(id -g) $(DC) down -v --rmi all 2>/dev/null || true; \ echo "Deleting config/ and data/..."; \ sudo rm -rf config/ data/; \ echo "Uninstall complete. Git repo and scripts remain."; \ ;; \ n|N|"") \ echo "Stopping and removing containers (keeping images and data)..."; \ PUID=$$(id -u) PGID=$$(id -g) $(DC) down 2>/dev/null || true; \ echo "Done. Images, config/ and data/ are untouched. Run 'make start' to bring it back up."; \ ;; \ *) \ echo "Cancelled."; \ ;; \ esac # ── Build ───────────────────────────────────────────────────────────────────── build: @echo "Building API service..." $(DC) build api build-api: @echo "Rebuilding API (no cache)..." $(DC) build --no-cache api $(DC) up -d api build-webui: @echo "Rebuilding Web UI (no cache)..." $(DC) build --no-cache webui $(DC) up -d webui # ── Individual services ─────────────────────────────────────────────────────── start-dns: $(DC) up -d dns start-api: $(DC) up -d api start-wg: $(DC) up -d wireguard start-webui: $(DC) up -d webui # ── Maintenance ─────────────────────────────────────────────────────────────── clean: @echo "Removing containers and volumes..." $(DC) down -v docker system prune -f @echo "Done. config/ and data/ are untouched." backup: @echo "Creating backup..." @mkdir -p backups @sudo tar -czf backups/cell-backup-$(shell date +%Y%m%d-%H%M%S).tar.gz \ config/ data/ docker-compose.yml Makefile README.md @sudo chown $$(id -u):$$(id -g) backups/cell-backup-*.tar.gz @echo "Backup created in backups/." restore: @echo "Available backups:" @ls -lh backups/cell-backup-*.tar.gz 2>/dev/null || echo "No backups found." @echo "" @echo "To restore: tar -xzf backups/cell-backup-YYYYMMDD-HHMMSS.tar.gz" # ── Tests ───────────────────────────────────────────────────────────────────── test: @echo "Running all tests..." pytest tests/ api/tests/ test-all: python3 api/tests/run_tests.py test-unit: pytest tests/ test-coverage: pytest tests/ api/tests/ --cov=api --cov-report=html --cov-report=term-missing --cov-fail-under=70 -v test-integration: @echo "Running full integration tests (requires running PIC stack)..." PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/integration/ -v test-integration-readonly: @echo "Running read-only integration tests (no peer creation)..." PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/integration/test_live_api.py tests/integration/test_webui.py -v test-api: cd api && python3 -m pytest tests/test_api_endpoints.py -v test-cli: cd api && python3 -m pytest tests/test_cli_tool.py -v # ── E2E tests ───────────────────────────────────────────────────────────────── # Run `make test-e2e-deps` once to install dependencies, then use the other targets. # WG tests require wg-quick and run under sudo (passwordless sudo assumed on this host). test-e2e-deps: sudo pip3 install --break-system-packages -r tests/e2e/requirements.txt sudo python3 -m playwright install --with-deps chromium test-e2e-api: @PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/e2e/api -v test-e2e-ui: @PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/e2e/ui -v test-e2e-wg: @PIC_HOST=$${PIC_HOST:-localhost} sudo -E python3 -m pytest tests/e2e/wg -v -p no:xdist test-e2e: test-e2e-api test-e2e-ui test-e2e-wg reset-test-admin-pass: ifndef PIC_TEST_ADMIN_PASS $(error Usage: make reset-test-admin-pass PIC_TEST_ADMIN_PASS=newpassword) endif python3 scripts/reset_admin_password.py "$(PIC_TEST_ADMIN_PASS)" # ── Admin password management ────────────────────────────────────────────────── show-admin-password: @python3 scripts/reset_admin_password.py --show reset-admin-password: @python3 scripts/reset_admin_password.py --generate test-phase1: cd api && python3 -m pytest tests/test_network_manager.py tests/test_phase1_endpoints.py -v test-phase2: cd api && python3 -m pytest tests/test_wireguard_manager.py tests/test_phase2_endpoints.py -v test-phase3: cd api && python3 -m pytest tests/test_phase3_managers.py tests/test_phase3_endpoints.py -v test-phase4: cd api && python3 -m pytest tests/test_phase4_routing.py tests/test_phase4_endpoints.py -v test-all-phases: cd api && python3 -m pytest tests/ -v # ── Network / peers ─────────────────────────────────────────────────────────── show-routes: @docker exec cell-wireguard wg show 2>/dev/null || echo "WireGuard not running" list-peers: @curl -s http://localhost:3000/api/peers | python3 -m json.tool || echo "API not responding" add-peer: @if [ -n "$(PEER_NAME)" ] && [ -n "$(PEER_IP)" ] && [ -n "$(PEER_KEY)" ]; then \ curl -X POST http://localhost:3000/api/peers \ -H "Content-Type: application/json" \ -d '{"name":"$(PEER_NAME)","ip":"$(PEER_IP)","public_key":"$(PEER_KEY)"}'; \ else \ echo "Usage: make add-peer PEER_NAME=name PEER_IP=10.0.0.x PEER_KEY="; \ fi # ── Dev ─────────────────────────────────────────────────────────────────────── dev: $(DC) -f docker-compose.yml -f docker-compose.dev.yml up -d