diff --git a/deployment/README.md b/deployment/README.md index 451c9af..3698572 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -11,7 +11,7 @@ The mantis needs a public-reachable URL. Pick the option that matches where you | **B. [Docker + Tailscale](./tailscale.md)** | Your machine | Tailscale Funnel (`*.ts.net`) | Personal mantis on a laptop / home server — works behind CGNAT | | **C. [Docker + Cloudflare Tunnel](./cloudflare.md)** | Your machine | Your own domain on Cloudflare | You already have a Cloudflare-hosted domain; want SSO via Cloudflare Access | | **E1. [Railway](./railway.md)** | Railway (long-running) | `*.up.railway.app` or custom | Set-and-forget; the worker runs natively (no cron config) | -| **E2. [Fly.io](./fly.md)** | Fly (long-running) | `*.fly.dev` or custom | Same shape as Railway; broader regional choice | +| **E2. [Fly.io](./fly.md)** | Fly app + managed or external Postgres | `*.fly.dev` or custom | One-command, idempotent launch; broader regional choice | | **E3. [Render](./render.md)** | Render (long-running) | `*.onrender.com` or custom | Same shape; free tier exists but cold-starts after 15 min idle | For any of the public options, also see **[edge-limits.md](./edge-limits.md)** for rate-limit and DDoS guidance, and **[backups.md](./backups.md)** for Postgres backup strategies. diff --git a/deployment/fly.md b/deployment/fly.md index 7f79443..a82a4f2 100644 --- a/deployment/fly.md +++ b/deployment/fly.md @@ -1,19 +1,107 @@ --- title: "Fly.io" -description: "Deploy Mantis to Fly.io with Postgres and public edge guidance." +description: "Deploy Mantis to Fly.io with one command, Postgres, and safe proxy defaults." --- +The repository includes an idempotent launcher that creates the Fly app, +connects Postgres, sets the required secrets, deploys Mantis, and prints the +first admin API key. + +## Quick launch + +Prerequisites: + +- A Fly.io account and [`flyctl`](https://fly.io/docs/flyctl/install/). +- A local clone of the Mantis repository. +- `fly auth login` completed once. + +```bash +git clone https://github.com/privacykey/mantis +cd mantis +fly auth login + +bash deploy/fly-launch.sh --app my-mantis --region iad +``` + +The launcher shows the app-machine and database choices before creating +anything. Add `--dry-run` to print the planned commands, or `--yes` to skip the +confirmation prompt: + +```bash +bash deploy/fly-launch.sh \ + --app my-mantis \ + --region iad \ + --dry-run +``` + +On a first launch, save the `mantis_live_...` admin key printed at the end. It +is shown once and works for both the dashboard login and `mantis login`. + +## Database choices + +The default is Fly Managed Postgres. It is the supported Fly database product, +but it is billed separately and can cost considerably more than the small +Mantis app machine. The launcher surfaces that before provisioning. + +| Option | Behaviour | When to use it | +|---|---|---| +| `--db mpg` | Creates and attaches Fly Managed Postgres. This is the default. | You want the supported, managed Fly option. | +| `--db external` | Imports the `DATABASE_URL` already present in your environment. | You use Neon, Supabase, or another external Postgres provider. | +| `--db unmanaged` | Creates a legacy `fly postgres` machine. | You accept operating and recovering Postgres yourself. | +| `--db none` | Skips database setup. | You will attach or configure Postgres separately before the app can start. | + +For an external database: + ```bash -cp deploy/fly.toml.example fly.toml -# edit `app =` to a unique name; edit PUBLIC_BASE_URL after first deploy -fly launch --no-deploy --copy-config -fly postgres create --name mantis-db -fly postgres attach mantis-db -fly secrets set \ - PUBLIC_BASE_URL=https://.fly.dev \ - MANTIS_API_KEY_PEPPER="$(openssl rand -base64 32)" \ - BOOTSTRAP_API_KEY=mantis_live_... -fly deploy +DATABASE_URL='postgres://...' \ + bash deploy/fly-launch.sh --app my-mantis --db external ``` -Fly concurrency protects the VM, not the public URL. For app-layer URL/rate limits, use a Cloudflare-proxied custom domain; see **[edge limits](/deployment/edge-limits#flyio)**. +## Safe re-runs and configuration checks + +Re-running the launcher is safe: it reuses an existing app and database and +refuses to rotate `MANTIS_API_KEY_PEPPER`, because rotating that value would +invalidate every API key. + +If `fly.toml` already exists, the launcher leaves it untouched and checks its +public origin before deploying: + +- `PUBLIC_BASE_URL` must be a quoted absolute `https://` URL. +- A `*.fly.dev` URL must match the app passed with `--app`. +- An intentional custom HTTPS domain is accepted. +- Missing `TRUST_PROXY_HEADERS` remains a warning so existing deployments are + not blocked, but it should be fixed before relying on hit IP attribution. + +The generated configuration enables trusted proxy headers and pins attribution +to Fly's authoritative `X-Forwarded-For` value. Without those settings, +production deployments record a null client IP. If a custom domain is proxied +through Cloudflare, follow the comments in `deploy/fly.toml.example` and use +`cf-connecting-ip` instead. + +## Later deployments + +To redeploy from your machine, either re-run the launcher with the same options +or deploy the existing configuration directly: + +```bash +fly deploy --app my-mantis --config fly.toml +``` + +The repository also includes an opt-in +[`fly-deploy.yml`](https://github.com/privacykey/mantis/blob/main/.github/workflows/fly-deploy.yml) +workflow for deploys from `main`. It stays inert until you configure the +app-scoped `FLY_API_TOKEN`, `FLY_APP`, and `FLY_DEPLOY_ENABLED=true` repository +settings described at the top of that file. + +## Verify + +```bash +curl https://my-mantis.fly.dev/api/health +mantis login --url https://my-mantis.fly.dev +mantis doctor +``` + +The health response should report `"status":"ok"` and `"db":"ok"`. + +Fly concurrency protects the VM, not the public canary URL. For app-layer +rate limits and DDoS guidance, see **[edge limits](/deployment/edge-limits#flyio)**. diff --git a/getting-started.md b/getting-started.md index 5e94654..5ca6263 100644 --- a/getting-started.md +++ b/getting-started.md @@ -19,7 +19,7 @@ The CLI talks to either of two backends, with the same command surface: | Backend | What it gives you | Where it runs | |---|---|---| -| **Stateful server** (`mantis`) | Dashboard, hit history, audit log, multiple destinations per key, key revocation, monitor/status, optional Apple Wallet passes | Your own server. Docker Compose on a VPS, or one-click via [Railway / Fly.io / Render](/deployment). **Don't run this on your laptop as your primary backend** — your laptop's not on the public internet at 3am when the canary fires. | +| **Stateful server** (`mantis`) | Dashboard, hit history, audit log, multiple destinations per key, key revocation, monitor/status, optional Apple Wallet passes | Your own server. Docker Compose on a VPS, the [one-command Fly.io launcher](/deployment/fly), or the [Railway / Render guides](/deployment). **Don't run this on your laptop as your primary backend** — your laptop's not on the public internet at 3am when the canary fires. | | **Cloudflare Worker** (`mantis-edge`) | Sub-50ms response from the CF edge, no DB to host, "anyone with the AES key can mint", URLs are self-contained encrypted blobs | Cloudflare's edge — you deploy a single Worker via `pnpm --filter @mantis/edge run deploy` from the source checkout (see [`mantis-edge/`](https://github.com/privacykey/mantis/tree/main/mantis-edge)) | You can use **both**. Most people deploy the stateful server for primary tripwires (the ones you want a timeline + multi-destination management for), and reach for the edge variant for hand-off URLs that just need to fire a webhook without a central log. diff --git a/updating.md b/updating.md index 189cd16..a948e36 100644 --- a/updating.md +++ b/updating.md @@ -14,7 +14,7 @@ Pick the row that matches how each component was installed. After bumping the se | **Server** | Docker (`docker compose up -d`) | `git pull && docker compose up -d --build` — `AUTO_MIGRATE=1` (set in `docker-compose.yml`) runs new migrations on container start. | | **Server** | Local dev (`pnpm run dev`) | `git pull && corepack enable && pnpm install && pnpm run db:migrate` — then restart `pnpm run dev`. | | **Server** | Railway / Render (Git-tracked) | `git push` to the tracked branch — both auto-redeploy. Migrations run on boot via `AUTO_MIGRATE=1`. | -| **Server** | Fly.io | `git pull && fly deploy`. | +| **Server** | Fly.io launcher | `git pull && bash deploy/fly-launch.sh --app --region ` — safely reuses the app/database and does not rotate the API-key pepper. If `fly.toml` is already configured, `git pull && fly deploy --app --config fly.toml` is the shorter equivalent. | | **CLI** | Homebrew (`brew install privacykey/tap/mantis`) | `brew update && brew upgrade mantis`. | | **CLI** | Direct tarball from [releases](https://github.com/privacykey/mantis/releases?q=cli-v) | Download the new `mantis-.tar.gz`, extract, replace the binary on your `$PATH`. | | **Edge worker** | `mantis-edge/` via wrangler | `git pull && corepack enable && pnpm install && pnpm --filter @mantis/edge run deploy` — secrets (`MANTIS_EDGE_KEY`, `MANTIS_EDGE_WEBHOOK_ALLOWLIST`) persist across deploys, no re-keying needed. |