diff --git a/examples/README.md b/examples/README.md index e990f1b..6bfdabb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -30,7 +30,7 @@ Use this as a **discoverability** pass for the **[ROADMAP.md](../ROADMAP.md)** s |------|---------| | [quickstart/](quickstart/) | Minimal workspace used by `flightdeck-quickstart-verify`. | | [ci/](ci/README.md) | Policy gate script, sample policy YAML, GitHub Actions job snippets. | -| [deploy/](deploy/README.md) | Dockerfile and compose for `flightdeck serve`. | +| [deploy/](deploy/README.md) | Dockerfile and compose for `flightdeck serve`; optional **Railway** (`railway.toml`). | | [integration/](integration/README.md) | Sample event emitter for HTTP ingest. | | [integration/adoption/](integration/adoption/README.md) | OpenAI, Anthropic, LangChain, Agents SDK, CrewAI-style totals, Temporal labels → `RunEvent`. | | [fleet/](fleet/README.md) | Multi-workspace naming, optional catalog path, approval workflow notes. | diff --git a/examples/deploy/Dockerfile b/examples/deploy/Dockerfile index 837c767..2e904c3 100644 --- a/examples/deploy/Dockerfile +++ b/examples/deploy/Dockerfile @@ -11,7 +11,8 @@ RUN chmod +x /entrypoint.sh EXPOSE 8765 +# Respect PORT at runtime (e.g. Railway); default matches local Compose. HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ - CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8765/health').read()" + CMD python -c "import os,urllib.request; p=os.environ.get('PORT','8765'); urllib.request.urlopen(f'http://127.0.0.1:{p}/health').read()" ENTRYPOINT ["/entrypoint.sh"] diff --git a/examples/deploy/README.md b/examples/deploy/README.md index 68d4127..9ebd407 100644 --- a/examples/deploy/README.md +++ b/examples/deploy/README.md @@ -10,7 +10,7 @@ Build (from this directory): docker build -t flightdeck-serve:local . ``` -The image installs **`flightdeck-ai`** from PyPI and runs **`flightdeck serve`** on **`0.0.0.0:8765`** inside the container. +The image installs **`flightdeck-ai`** from PyPI and runs **`flightdeck serve`** on **`0.0.0.0`** using port **`8765`** by default. On platforms that set **`PORT`** (for example **Railway**), **`entrypoint.sh`** binds to **`$PORT`** instead. **`entrypoint.sh`** creates a default **`flightdeck.yaml`** in `/workspace` on first start (`flightdeck init`) if the mounted volume is empty. @@ -40,6 +40,29 @@ Inside the Compose stack, **`exec`** into the running container with **`/workspa Set **`FLIGHTDECK_LOCAL_API_TOKEN`** in your environment before `docker compose up` (or in an `.env` file beside `docker-compose.yml`). Clients must send **`Authorization: Bearer …`** for **ledger writes**: **`POST /v1/promote*`**, **`POST /v1/rollback`**, and **`POST /v1/events`**. With no token configured, those routes accept only **loopback** callers. **`POST /v1/diff`** stays unauthenticated (read-only); still treat network placement as a trust boundary. +## Railway + +[Railway](https://railway.app/) often suits **small demos**; pricing and free allowances change — confirm **[Railway pricing](https://railway.com/pricing)** before relying on **`$0/month`** long term. + +### Deploy from this repo + +1. Create a **new project** → **Deploy from GitHub** (or **`railway init`** / **`railway link`** with the [CLI](https://docs.railway.app/guides/cli)). +2. Set the service **root directory** to **`examples/deploy`** so Railway builds **`Dockerfile`** and picks up **`railway.toml`** (config-as-code). + If the dashboard root cannot be a subdirectory, set [**`RAILWAY_DOCKERFILE_PATH`**](https://docs.railway.app/guides/dockerfiles) (service variable) to **`examples/deploy/Dockerfile`** and point **config as code** at **`examples/deploy/railway.toml`** per [config-as-code](https://docs.railway.app/guides/config-as-code). +3. **Networking:** enable **Public Networking** and **Generate Domain** (HTTPS). Railway routes traffic to the **`PORT`** your process listens on; **`entrypoint.sh`** uses **`PORT`** automatically. +4. **Variables (recommended for any public URL):** add **`FLIGHTDECK_LOCAL_API_TOKEN`** (random secret). The stock PyPI image does **not** embed that token in the browser bundle — use **read-only UI** (`VITE_FLIGHTDECK_UI_READ_ONLY=true` in a **custom image build**) or rebuild static assets with **`VITE_FLIGHTDECK_LOCAL_API_TOKEN`** so the UI can authenticate when **`read_auth`** is bearer-gated — see **`docs/web-ui.md`** and **[SECURITY.md](../../SECURITY.md)**. +5. **Persistent SQLite (optional):** add a [Railway volume](https://docs.railway.app/guides/volumes) mounted at **`/workspace`** so redeploys keep **`.flightdeck/`**. Without a volume, the ledger may reset when the container is recreated. + +CLI sketch (from **`examples/deploy`** after **`railway link`**): + +```bash +railway login +cd examples/deploy +railway variable set FLIGHTDECK_LOCAL_API_TOKEN="$(openssl rand -hex 24)" +railway up +railway domain # generate .railway.app URL if needed +``` + ## Helm (optional single-replica chart) A minimal chart lives under **`chart/flightdeck/`**. It runs one replica of **`flightdeck serve`** with an **`emptyDir`** workspace (ephemeral); for a persistent ledger, replace the volume in **`templates/deployment.yaml`** with a PVC or mount your own image init. diff --git a/examples/deploy/entrypoint.sh b/examples/deploy/entrypoint.sh index d7543bf..5c4efeb 100644 --- a/examples/deploy/entrypoint.sh +++ b/examples/deploy/entrypoint.sh @@ -4,4 +4,6 @@ cd /workspace if [ ! -f flightdeck.yaml ]; then flightdeck init fi -exec flightdeck serve --host 0.0.0.0 --port 8765 "$@" +# Railway (and some PaaS) inject PORT; local Compose defaults to 8765 via Dockerfile EXPOSE. +FD_PORT="${PORT:-8765}" +exec flightdeck serve --host 0.0.0.0 --port "$FD_PORT" "$@" diff --git a/examples/deploy/railway.toml b/examples/deploy/railway.toml new file mode 100644 index 0000000..7442f1d --- /dev/null +++ b/examples/deploy/railway.toml @@ -0,0 +1,14 @@ +# Railway config-as-code for FlightDeck reference image. +# Deploy with repo root directory set to `examples/deploy` (GitHub integration), +# or run `railway up` from this directory after `railway link`. +# +# Docs: https://docs.railway.app/guides/config-as-code + +[build] +builder = "DOCKERFILE" +dockerfilePath = "Dockerfile" + +[deploy] +healthcheckPath = "/health" +healthcheckTimeout = 300 +restartPolicyType = "ON_FAILURE"