# Planet Ops Runbook This runbook is for deployment, on-call, and maintenance engineers. End-user UI flows live in the [Planet Manual](/home/ray/dev/linkong/planet/docs/technical/en/manual.md); this document only covers shell, Docker, logs, environment variables, and troubleshooting. ## First Startup ```bash ./planet.sh start ``` Default behavior: - Starts PostgreSQL and Redis - Starts AI Provider - Starts the backend API - Starts the frontend Vite dev server - Prints Earth, console, Playground, and backend API doc URLs First startup seeds two default accounts (see `DEFAULT_LOGIN_USERS` in `backend/app/db/session.py`): | Username | Password | Role | | --- | --- | --- | | `admin` | `admin123` | `super_admin` | | `linkong` | `LK12345678` | `super_admin` | Both seed accounts are created with `email_verified = TRUE` and can log into the console immediately. Any other account must either go through the public registration flow described in the Manual, or be created via `./planet.sh createuser`. Specify custom ports: ```bash ./planet.sh start -b 8001 -f 3001 -a 8101 ``` | Flag | Meaning | | --- | --- | | `-b ` | Backend port | | `-f ` | Frontend port | | `-a ` | AI Provider port | | `--allow-lan` | Enable LAN access | | `--verbose` | Show extra command output | ## Stop and Per-Module Restart Stop everything: ```bash ./planet.sh stop ``` Stops backend, AI Provider, frontend, PostgreSQL, Redis. Per-module restart: ```bash ./planet.sh restart # full ./planet.sh restart -b # backend ./planet.sh restart -f # frontend ./planet.sh restart -a # AI Provider ./planet.sh restart -d # database ``` Per-module restart is preferred during development to avoid interrupting unrelated services. ## Destructive Reset ```bash ./planet.sh destroy ``` `destroy` returns a local development environment to a near-empty project state. It requires typing `Y` before it runs; source files and existing `.env` files are preserved. Cleanup order and boundaries: - If `planet_postgres` is running, the script first clears the `public` schema in `planet_db`. This prevents old `collected_data.is_current = true` rows from making Earth OOBE report `ready=true` if Docker volume removal later fails. - Docker cleanup targets resources whose Compose project is `planet`, plus the explicit volumes `planet_postgres_data`, `planet_redis_data`, `postgres_data`, and `redis_data`; do not delete unlabeled volumes by a broad `planet_*` pattern, because another local project could own them. - Local build state removes `.venv`, frontend `node_modules` / `dist`, Planet state/cache, and scattered Python / Vite cache directories. After the reset, run `./planet.sh init` again to recreate tables and default seed data. Old collected records are not restored, and Earth OOBE is evaluated from the backend's real collection state on the next visit. ## Health Check ```bash ./planet.sh health ``` Checks: - `planet_*` container status - Backend `/health` - AI Provider `/health` - Frontend reachability If anything reports offline, check the corresponding logs first. ## Logs Recent logs: ```bash ./planet.sh log ``` Follow: ```bash ./planet.sh log -f # frontend: ~/.local/state/planet/frontend.log ./planet.sh log -b # backend: ~/.local/state/planet/backend.log ./planet.sh log -a # AI Provider: planet_aiprovider container logs ``` ## CLI User Creation ```bash ./planet.sh createuser ``` Interactively prompts for username, password, and role; writes the user with `email_verified = TRUE` directly. Use when: - SMTP is not yet configured but an admin account is needed now - Pre-seeding internal test accounts - Public registration is unavailable for any reason and a fallback is required For ordinary user onboarding, configure SMTP at `/settings -> SMTP Email` first and let users self-register at `/register`. ## LAN / WSL Access ```bash ./planet.sh start --allow-lan ``` Useful for: - Starting in WSL, accessing from Windows browser - Demoing Earth from a phone or tablet - Other LAN machines reaching the same dev instance On Windows, the repository-root `planet.cmd` can be used as a one-click entrypoint. It requests Administrator privileges, enters the `Ubuntu` WSL distribution at `/home/linkong/planet`, runs `./planet.sh restart --allow-lan`, opens `http://localhost:3000/earth` after a successful restart, and leaves the terminal inside a WSL shell for log inspection. If the local WSL distribution name or checkout path differs, adjust the `wsl.exe -d ... --cd ...` arguments in `planet.cmd` first. On a new Windows machine, check the WSL generation first: ```powershell wsl -l -v ``` Planet development should use WSL2. WSL1 has different networking, filesystem, and process behavior, and can surface as Bun package-manager commands returning only `An unknown error occurred (Unexpected)`, unstable port release, or LAN behavior that does not match the script's assumptions. Convert the distribution if it still runs as WSL1: ```powershell wsl --set-version Ubuntu 2 ``` `--allow-lan` directly exposes the frontend, backend, and AI Provider from the development machine: frontend `3000`, backend `8000`, and AI Provider `8010`. Before startup, the script checks all three ports. If WSL/Linux cannot release a port and a Windows-side listener or stale `portproxy` rule owns it, the script requests Administrator PowerShell cleanup. When Planet runs in WSL, Windows can usually reach it through `localhost`; other LAN machines reaching the Windows LAN IP still need Windows Firewall allow rules. Diagnose in this order: ```bash # From the shell running Planet curl http://localhost:3000 curl http://localhost:8000/health curl http://localhost:8010/health ss -ltnp | grep -E ':3000|:8000|:8010' ``` If the services are running but the LAN IP still fails, first remove stale `portproxy` rules and confirm Windows Firewall allows the ports. The script checks this automatically and requests Administrator PowerShell when needed. Manual fallback commands: ```powershell netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=3000 netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=8000 netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=8010 New-NetFirewallRule -DisplayName "WSL Planet 3000" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 3000 New-NetFirewallRule -DisplayName "WSL Planet 8000" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8000 New-NetFirewallRule -DisplayName "WSL Planet 8010" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8010 ``` LAN devices should use the Windows external ports, for example `http://:3000/earth`, `http://:8000/health`, and `http://:8010/health`. ## AI Provider Environment and Builds AI Provider runtime configuration lives in two places: | Location | Best for | Notes | | --- | --- | --- | | `aiprovider/.env` | Team-shared local defaults | Read by Docker Compose as `env_file` | | `~/.zshrc` | Personal provider/model/key/proxy | `planet.sh` reads common `AI_*`, `SERVICE_*`, `PYTHON_IMAGE`, `UV_IMAGE` lines | Recommended form: ```bash export AI_PROVIDER=minimax export AI_PROVIDER_API=anthropic-messages export AI_BASE_URL=https://api.example.com/anthropic export AI_API_KEY=sk-change-me export AI_MODEL=MiniMax-M2.7 export AI_PROVIDER_SERVICE_TOKEN=change_me ``` By default `planet.sh` only statically parses simple `export KEY=value` lines from `~/.zshrc`. When complex shell expansion is required, opt in explicitly: ```bash PLANET_LOAD_ZSHRC_ENV=source ./planet.sh start -a ``` To ignore `~/.zshrc` entirely: ```bash PLANET_LOAD_ZSHRC_ENV=0 ./planet.sh start -a ``` The AI Provider image only rebuilds when code, Dockerfile, Compose config, or Python dependencies change. After changing keys or base URL, restarting the container is enough: ```bash ./planet.sh restart -a ``` Rebuild detection is based on a content fingerprint rather than only file mtimes. `planet.sh` hashes the `aiprovider/` files, `aiprovider/Dockerfile`, `pyproject.toml`, `uv.lock`, `PYTHON_IMAGE`, `UV_IMAGE`, and the dependency fingerprint into `AI_PROVIDER_BUILD_FINGERPRINT`; Docker writes it into the image label `planet.aiprovider.build-fingerprint`. If the existing `planet-aiprovider:latest` image has a matching label, the script skips rebuild and refreshes the local stamp. Older images without the label fall back to the state/cache stamp. Docker builds use `uv sync --frozen`. To make container builds reuse the host uv mirror configuration, the script resolves the first config file in this order and mounts it into the build as a BuildKit secret at `/root/.config/uv/uv.toml`: 1. The current `UV_CONFIG_FILE` 2. Repository-root `uv.toml` 3. `${XDG_CONFIG_HOME:-~/.config}/uv/uv.toml` 4. `~/.uv/uv.toml` If none exists, the script creates an empty state file for the secret so Compose does not fail on a missing file. Before Docker build it unsets `UV_DEFAULT_INDEX`, `UV_INDEX_URL`, and `UV_EXTRA_INDEX_URL`, keeping the build tied to the explicit `UV_CONFIG_FILE`. For a temporary Tsinghua mirror, place this in repository-root `uv.toml`: ```toml [[index]] name = "tsinghua" url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" default = true ``` Diagnose slow builds: | Symptom | Common cause | Fix | | --- | --- | --- | | Large `transferring context` | build context includes unrelated frontend / data files | `.dockerignore` ships only required files | | `uv sync --frozen` is slow | first build, cold cache, or missing uv mirror config | wait for the first build; later runs reuse BuildKit cache; configure `uv.toml` when needed | | Old keys still in effect after edit | container not restarted | `./planet.sh restart -a` | | Code changed but the image did not rebuild | fingerprint still matches the image label | Confirm the change is under `aiprovider/`, Dockerfile, or Python dependency inputs; delete `planet-aiprovider:latest` and retry if needed | ## SMTP Email (Required for Public Registration) Public registration and email verification depend on SMTP. Administrators configure host, port, username, password, from-address, and TLS mode at `/settings -> SMTP Email` in the console, then use the "Send Test Email" button to verify. Settings are persisted in the `system_settings.smtp` row. When SMTP is unset, `POST /api/v1/auth/register` returns `503 EMAIL_PROVIDER_NOT_CONFIGURED` and the frontend surfaces a clear error. The operational fallback is `./planet.sh createuser`. One-time codes are stored in Redis under `otp:{purpose}:{email}` with a 600-second TTL. The key is invalidated after 5 invalid attempts. Resend cooldown is 60 seconds, enforced via `otp_rate:{purpose}:{email}`. ## Troubleshooting Order ```bash ./planet.sh health # 1. service state ./planet.sh log # 2. recent logs ./planet.sh log -f # 3. per-module logs ./planet.sh log -b ./planet.sh log -a ./planet.sh restart -f # 4. restart only the affected module ./planet.sh restart -b ./planet.sh restart -a ./planet.sh restart -d # 5. database / cache issues ./planet.sh restart # 6. full restart if still broken ``` ## Development Command Conventions Backend setup and script initialization use the lockfile: ```bash uv python install 3.14 uv sync --frozen --group dev ``` `--frozen` rejects implicit `uv.lock` rewrites, which is the desired behavior on new machines, CI, and Docker builds. Dependency upgrades should explicitly update `pyproject.toml` / `uv.lock` on a development machine and commit the lockfile. Frontend must use Bun: ```bash cd frontend bun install bun run dev bun run build ``` Do not use `npm run ...`. In the WSL / Windows mixed environment Bun avoids Node/npm path inconsistencies. Validate the frontend build: ```bash source ~/.zshrc && bun run build ``` Use `bun run dev` during development; Vite HMR refreshes the browser after source saves. `bun run build` only writes the `dist` artifact and does not refresh an already-open dev page. To inspect the production bundle with automatic reload after successful builds: ```bash bun run preview:auto ``` To watch sources and rebuild continuously without starting the preview server: ```bash bun run build:watch ``` Backend dependencies are managed with uv: ```bash uv sync uv run pytest backend/tests/test_otp_service.py ``` ## Earth Boundary PMTiles Operations 1. In the console, open `Operations and Configuration -> Earth Content -> Boundary Precision` to save boundary source configuration. The local config is written to `config/earth-boundary-sources.local.json`; do not commit it. 2. Click "Build high precision boundaries", or switch the Earth toolbar settings gear to High Precision for the first build. The backend downloads the three source packages to `data/earth-boundary-sources/`, writes the source manifest, and invokes the PMTiles build script. 3. The builder requires `tippecanoe` and `pmtiles` on PATH. Missing tools return a clear API error and do not write data-source collection records. 4. A successful production build outputs `frontend/public/earth/data/boundaries/earth-boundaries-china-pov-v1.pmtiles` and its manifest. 5. After deployment, open Earth, enable "Border Lines", and inspect China's southeast coast, Taiwan, Hainan, the South China Sea, Zangnan, Kosovo, and Gaza for hover behavior and boundary policy. 6. If no high-precision manifest/PMTiles exists locally, Earth uses the bundled `frontend/public/earth/data/countries-admin0.min.geojson` fallback. If high-precision assets exist but tile requests fail, troubleshoot PMTiles range requests, manifest provider, Nginx `.pmtiles` static serving, and sha256 consistency. ## Related Docs - [planet.sh Startup Mechanism](/home/ray/dev/linkong/planet/docs/technical/en/ops-planet-sh-startup.md) - [System Service Control](/home/ray/dev/linkong/planet/docs/technical/en/backend-system-service-control.md) - [Docker + Compose + Buildx Upgrade](/home/ray/dev/linkong/planet/docs/technical/en/ops-docker-compose-buildx-upgrade.md) - [Data Collectors](/home/ray/dev/linkong/planet/docs/technical/en/backend-collectors.md)