mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-08 22:21:45 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
155d0a5a28
22 changed files with 254 additions and 476 deletions
|
|
@ -1,12 +0,0 @@
|
|||
# syntax=docker/dockerfile:1.9
|
||||
#
|
||||
# Thin deploy image for PaaS providers that build from a repo Dockerfile
|
||||
# (Railway, Render, Fly.io source mode). Pulls the published multi-arch
|
||||
# image from GHCR instead of rebuilding fabro from source on every
|
||||
# deploy, which keeps cold starts short and avoids needing the release
|
||||
# workflow's pre-built binary context on the PaaS runner.
|
||||
#
|
||||
# The upstream image already configures entrypoint, CMD (binds
|
||||
# 0.0.0.0:${PORT:-32276}), volumes, and the unprivileged fabro user.
|
||||
|
||||
FROM ghcr.io/fabro-sh/fabro:nightly
|
||||
13
README.md
13
README.md
|
|
@ -144,15 +144,18 @@ For headless or scripted environments, `fabro install` runs the same setup as a
|
|||
|
||||
---
|
||||
|
||||
## Self-host the Fabro server
|
||||
## Running Fabro
|
||||
|
||||
Running Fabro as an HTTP server with the web UI lets a team share one instance. The repository ships a `Dockerfile` that serves the API (with the embedded web UI) on `$PORT` (default `32276`) and persists state to `/storage`.
|
||||
Fabro runs as a server. You choose where it runs:
|
||||
|
||||
[](https://railway.com/new/template?template=https%3A%2F%2Fgithub.com%2Ffabro-sh%2Ffabro) [](https://render.com/deploy?repo=https://github.com/fabro-sh/fabro)
|
||||
- **On your laptop** — install the CLI (above) and run `fabro server start`. Workflows pause when your laptop sleeps.
|
||||
- **On a host (self-hosted)** — deploy the Docker image with `docker compose` or any cloud container service (ECS, Cloud Run, Kubernetes). See [Self-host with Docker](https://docs.fabro.sh/administration/self-host-docker).
|
||||
|
||||
Click a button to provision a Fabro service from this repository. On Railway, attach a Volume at `/storage` so your runs and checkpoints survive redeploys; on Render, the `render.yaml` blueprint provisions a 1 GB disk at `/storage` automatically. The [Railway](https://docs.fabro.sh/administration/deploy-railway) and [Render](https://docs.fabro.sh/administration/deploy-render) deploy guides walk through env vars, accessing the dev token, and pointing the CLI at your deployment.
|
||||
One-click managed alternative for the same Docker image:
|
||||
|
||||
Prefer to run Fabro elsewhere? See [Running the Fabro Server](https://docs.fabro.sh/administration/deploy-server) for generic Docker guidance and the companion guides for [Fly.io](https://docs.fabro.sh/administration/deploy-fly-io) and [DigitalOcean](https://docs.fabro.sh/administration/deploy-digital-ocean).
|
||||
[](https://railway.com/deploy/UcEy5m?referralCode=E5TucU&utm_medium=integration&utm_source=template&utm_campaign=generic)
|
||||
|
||||
See the [deployment overview](https://docs.fabro.sh/administration/deployment) for the full picture.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ services:
|
|||
volumes:
|
||||
- fabro-storage:/storage
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
|
||||
volumes:
|
||||
fabro-storage:
|
||||
|
|
|
|||
|
|
@ -1,137 +0,0 @@
|
|||
---
|
||||
title: "DigitalOcean"
|
||||
description: "Deploy Fabro to a DigitalOcean Droplet with docker compose and Caddy for automatic TLS"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
DigitalOcean is a good fit for self-hosting Fabro on a Droplet — a plain Linux VPS running `docker compose`. The repo ships everything you need:
|
||||
|
||||
- `docker-compose.yaml` pulls the pre-built image from GHCR and declares a named volume for `/storage`
|
||||
- `docker-compose.prod.yaml` adds a [Caddy](https://caddyserver.com) sidecar that terminates TLS and auto-provisions Let's Encrypt certificates
|
||||
- `docker/Caddyfile` reverse-proxies HTTPS traffic to the Fabro server
|
||||
|
||||
<Note>
|
||||
**Why not App Platform?** DigitalOcean App Platform has no persistent volumes — it is designed for stateless workloads that offload state to managed Postgres or Spaces. Fabro writes runs, checkpoints, sessions, and JWT keys to `/storage`, so a Droplet (or any VPS) is the right fit. If you prefer Kubernetes, DOKS works too — that path is not documented here.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A DigitalOcean account and (optionally) the [`doctl`](https://docs.digitalocean.com/reference/doctl/how-to/install/) CLI
|
||||
- A domain name with DNS you can edit (required for Caddy to issue a Let's Encrypt cert)
|
||||
- LLM provider API keys (Anthropic, OpenAI, etc.) and any secrets you want set via `.env`
|
||||
|
||||
## 1. Create a Droplet
|
||||
|
||||
Pick the **Docker on Ubuntu** image from the DigitalOcean Marketplace — it ships with Docker Engine and the Compose plugin preinstalled, so there's no manual Docker install step.
|
||||
|
||||
From the control panel, or with `doctl`:
|
||||
|
||||
```bash
|
||||
doctl compute droplet create fabro \
|
||||
--image docker-20-04 \
|
||||
--size s-2vcpu-2gb \
|
||||
--region nyc3 \
|
||||
--ssh-keys <your-ssh-key-fingerprint>
|
||||
```
|
||||
|
||||
`s-2vcpu-2gb` is a reasonable starting size for light usage; grow later as your workflow load increases. Any region works — pick the one closest to you.
|
||||
|
||||
## 2. Point DNS at the Droplet
|
||||
|
||||
Caddy needs the domain to resolve to the Droplet's public IP **before** you start the stack, or the first cert issuance will fail. Create an `A` record for your chosen hostname (e.g. `fabro.example.com`) pointing at the Droplet's IPv4 address. Wait for propagation before continuing.
|
||||
|
||||
## 3. Configure and launch
|
||||
|
||||
SSH into the Droplet and pull the repo:
|
||||
|
||||
```bash
|
||||
ssh root@<droplet-ip>
|
||||
git clone https://github.com/fabro-sh/fabro
|
||||
cd fabro
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Edit `.env`. At minimum:
|
||||
|
||||
```
|
||||
FABRO_DOMAIN=fabro.example.com
|
||||
ANTHROPIC_API_KEY=...
|
||||
SESSION_SECRET=...
|
||||
```
|
||||
|
||||
The [Server Configuration](/administration/server-configuration) reference has the full list of variables; the minimum useful set is:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `FABRO_DOMAIN` | Public hostname Caddy serves. Must resolve to this Droplet for Let's Encrypt to issue a cert. |
|
||||
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GEMINI_API_KEY` / ... | At least one LLM provider key for the models you'll run |
|
||||
| `FABRO_DEV_TOKEN` | Optional — pre-set the dev token instead of reading the one written to `/storage` on first boot |
|
||||
| `SESSION_SECRET` | 64-character hex string; required when the web UI is enabled |
|
||||
| `GITHUB_APP_CLIENT_SECRET`, `GITHUB_APP_WEBHOOK_SECRET`, `GITHUB_APP_PRIVATE_KEY` | Only if you enable GitHub OAuth or the GitHub App integration |
|
||||
|
||||
Bring up the stack:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d
|
||||
```
|
||||
|
||||
This starts two containers: `fabro` (the server, state in the `fabro-storage` named volume) and `caddy` (listening on ports 80/443, cert state in the `caddy_data` named volume). Caddy requests a Let's Encrypt cert on first boot; watch progress with `docker compose logs -f caddy`.
|
||||
|
||||
## Accessing your Fabro server
|
||||
|
||||
Once `https://<FABRO_DOMAIN>/health` returns `ok`, two things to grab:
|
||||
|
||||
1. **The dev token** — on first boot, Fabro writes one to `/var/fabro/dev-token` and logs it:
|
||||
|
||||
```bash
|
||||
docker compose exec fabro cat /var/fabro/dev-token
|
||||
```
|
||||
|
||||
2. **Point your local CLI at the server** — add the URL to `~/.fabro/settings.toml`:
|
||||
|
||||
```toml title="~/.fabro/settings.toml"
|
||||
[cli.target]
|
||||
type = "http"
|
||||
url = "https://fabro.example.com/api/v1"
|
||||
```
|
||||
|
||||
Then commands like `fabro model list --server <url>` will hit your Droplet.
|
||||
|
||||
See [Running the Fabro Server](/administration/deploy-server) for the full auth and CLI-pointing story.
|
||||
|
||||
## Updates
|
||||
|
||||
To pull the latest nightly image and restart:
|
||||
|
||||
```bash
|
||||
cd /root/fabro
|
||||
git pull
|
||||
docker compose -f docker-compose.yaml -f docker-compose.prod.yaml pull
|
||||
docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d
|
||||
```
|
||||
|
||||
The `fabro-storage` and `caddy_data` named volumes survive `pull` and `up`, so runs, checkpoints, and TLS certs persist.
|
||||
|
||||
To pin a specific version instead of `:nightly`, edit `docker-compose.yaml` and change `image: ghcr.io/fabro-sh/fabro:nightly` to the tag you want.
|
||||
|
||||
## Caveats
|
||||
|
||||
- **DNS must resolve before first `up`.** Caddy will retry Let's Encrypt failures, but an obviously bad DNS config will lock you into the staging CA's low rate limits. Verify with `dig +short fabro.example.com` before starting.
|
||||
- **Firewall.** The Docker Marketplace image opens 22, 80, and 443 by default — good. Keep port 32276 **closed** on the public interface; Caddy fronts the Fabro server on the private Docker network. Use `ufw status` to verify.
|
||||
- **State lives in named volumes.** `fabro-storage` and `caddy_data` are the load-bearing pieces. Back them up (for example, via `docker run --rm -v fabro_fabro-storage:/src -v $(pwd):/dst alpine tar czf /dst/backup.tgz -C /src .`) before destructive operations.
|
||||
- **Single-host deploy.** This setup assumes one Droplet owns the data. For HA you'd need a different architecture (external block storage, managed DB, etc.) — Fabro's server currently assumes a single writer on `/storage`.
|
||||
- **Architecture.** The compose file pins `platform: linux/amd64`; the `:nightly` tag is multi-arch but the arm64 variant is not currently usable. Use x86_64 Droplets.
|
||||
|
||||
## Next steps
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Running the Fabro Server" icon="server" href="/administration/deploy-server">
|
||||
Auth, dev tokens, submitting runs, and pointing the CLI at your deployment.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
Full `settings.toml` reference — reverse-proxy TLS, auth methods, concurrency, and more.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
---
|
||||
title: "Fly.io"
|
||||
description: "Deploy Fabro to Fly.io from the prebuilt GHCR image, with a Fly Volume for state"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
[Fly.io](https://fly.io) can host the Fabro server by pulling the pre-built image published to GHCR. The repo ships a `fly.toml` that points Fly directly at the image — no build step on Fly's builders, no `Dockerfile` evaluation — and declares a Volume mount at `/storage` so your runs, checkpoints, and sessions survive redeploys.
|
||||
|
||||
Fly.io is CLI-first; there is no one-click deploy button. The workflow below uses [`flyctl`](https://fly.io/docs/flyctl/install/).
|
||||
|
||||
## First-deploy checklist
|
||||
|
||||
### 1. Adopt `fly.toml` in a new app
|
||||
|
||||
```bash
|
||||
git clone https://github.com/fabro-sh/fabro
|
||||
cd fabro
|
||||
fly launch --copy-config --no-deploy
|
||||
```
|
||||
|
||||
`fly launch --copy-config` keeps the repo's `fly.toml` instead of generating a new one; `--no-deploy` lets you finish wiring secrets and volumes before the first deploy. You'll be prompted for an **app name** (must be globally unique on Fly) and a **primary region** — update `fly.toml` in place if you change the defaults.
|
||||
|
||||
### 2. Create the persistent Volume
|
||||
|
||||
Fabro writes all persistent state — run history, checkpoints, sessions, the default token, and JWT keys — under `/storage`. Fly containers have ephemeral filesystems, so without a Volume that directory is wiped on every redeploy. `fly.toml` declares the mount but **cannot create the volume itself** — provision it with `flyctl`:
|
||||
|
||||
```bash
|
||||
fly volumes create storage --size 1 --region <your-primary-region>
|
||||
```
|
||||
|
||||
Grow later with `fly volumes extend` if needed. The volume name (`storage`) must match `[[mounts]].source` in `fly.toml`.
|
||||
|
||||
### 3. Set required environment variables
|
||||
|
||||
Fly stores env vars as encrypted secrets:
|
||||
|
||||
```bash
|
||||
fly secrets set \
|
||||
ANTHROPIC_API_KEY=... \
|
||||
SESSION_SECRET=...
|
||||
```
|
||||
|
||||
The [Server Configuration](/administration/server-configuration) reference has the full list; the minimum useful set:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GEMINI_API_KEY` / ... | At least one LLM provider key for the models you'll run |
|
||||
| `FABRO_DEV_TOKEN` | Optional — pre-set the dev token instead of reading the one written to `/storage` on first boot |
|
||||
| `SESSION_SECRET` | 64-character hex string; required when the web UI is enabled |
|
||||
| `GITHUB_APP_CLIENT_SECRET`, `GITHUB_APP_WEBHOOK_SECRET`, `GITHUB_APP_PRIVATE_KEY` | Only if you enable GitHub OAuth or the GitHub App integration |
|
||||
|
||||
### 4. Deploy
|
||||
|
||||
```bash
|
||||
fly deploy
|
||||
```
|
||||
|
||||
Fly pulls `ghcr.io/fabro-sh/fabro:nightly`, attaches the volume, and starts the Machine. The health check on `/health` must pass before traffic is routed.
|
||||
|
||||
## Accessing your Fabro server
|
||||
|
||||
Once the deploy is healthy, Fly exposes a `<app>.fly.dev` URL (or your custom domain). Two things to grab:
|
||||
|
||||
1. **The dev token** — on first boot, Fabro writes one to `/var/fabro/dev-token` and logs it. Read it via the Machine's shell:
|
||||
|
||||
```bash
|
||||
fly ssh console -C "cat /var/fabro/dev-token"
|
||||
```
|
||||
|
||||
Or tail the startup logs with `fly logs`.
|
||||
|
||||
2. **Point your local CLI at the server** — add the Fly URL to `~/.fabro/settings.toml`:
|
||||
|
||||
```toml title="~/.fabro/settings.toml"
|
||||
[cli.target]
|
||||
type = "http"
|
||||
url = "https://<your-app>.fly.dev/api/v1"
|
||||
```
|
||||
|
||||
Then commands like `fabro model list --server <url>` will hit your Fly instance.
|
||||
|
||||
See [Running the Fabro Server](/administration/deploy-server) for the full auth and CLI-pointing story.
|
||||
|
||||
## Redeploys and updates
|
||||
|
||||
`fly deploy` re-pulls the GHCR image on every run. `fly.toml` references the `:nightly` tag by default, so redeploying picks up the latest nightly automatically. To pin a specific version, edit `fly.toml` to reference `ghcr.io/fabro-sh/fabro:<version>` and redeploy. The `/storage` Volume survives redeploys, so runs and checkpoints persist.
|
||||
|
||||
## Caveats
|
||||
|
||||
- **`$PORT` is not injected.** Unlike Railway and Render, Fly does not set a `PORT` environment variable. The Fabro image binds to `$PORT` if set, otherwise `32276` — `fly.toml` pins `internal_port = 32276` so the default works. If you change `internal_port`, also `fly secrets set PORT=<n>` to match.
|
||||
- **Volume is load-bearing and not replicated.** Fly's docs recommend at least two Volumes per app for redundancy, but Fabro's server is single-replica by design — one Machine owns `/storage`. Treat this like a traditional VPS: hardware failure means restoring from [Fly's volume snapshots](https://fly.io/docs/volumes/snapshots/) or a backup you manage.
|
||||
- **Single Machine.** Don't `fly scale count` above 1 — a second Machine can't mount the same Volume, and the server assumes a single writer.
|
||||
- **Architecture.** Fly Machines run x86_64 (amd64) by default. The `:nightly` tag is multi-arch, but the arm64 variant is not currently usable — stay on amd64.
|
||||
- **Autostop is disabled.** `fly.toml` sets `auto_stop_machines = "off"` so the Machine stays up for the run queue. Leaving autostop enabled would pause Fabro when there's no HTTP traffic, stalling any in-flight runs.
|
||||
|
||||
## Next steps
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Running the Fabro Server" icon="server" href="/administration/deploy-server">
|
||||
Auth, dev tokens, submitting runs, and pointing the CLI at your deployment.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
Full `settings.toml` reference — TLS, auth methods, concurrency, and more.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Railway"
|
||||
description: "Deploy Fabro to Railway from the Dockerfile, with a persistent Volume for state"
|
||||
description: "Deploy Fabro to Railway from the prebuilt GHCR image, with a persistent Volume for state"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
|
|
@ -11,9 +11,9 @@ description: "Deploy Fabro to Railway from the Dockerfile, with a persistent Vol
|
|||
|
||||
## One-click deploy
|
||||
|
||||
[](https://railway.com/new/template?template=https%3A%2F%2Fgithub.com%2Ffabro-sh%2Ffabro)
|
||||
[](https://railway.com/deploy/UcEy5m?referralCode=E5TucU&utm_medium=integration&utm_source=template&utm_campaign=generic)
|
||||
|
||||
The button launches Railway's new-project flow pointed at this repo. `railway.toml` instructs Railway to build `Dockerfile.deploy`, which is a thin wrapper that pulls `ghcr.io/fabro-sh/fabro:nightly` — no Rust compilation on Railway's builder, so deploys complete in seconds.
|
||||
The button launches Railway's template flow, which deploys the pre-built `ghcr.io/fabro-sh/fabro:nightly` image directly from GHCR — no Rust compilation on Railway's builder, so deploys complete in seconds.
|
||||
|
||||
## First-deploy checklist
|
||||
|
||||
|
|
@ -59,11 +59,11 @@ Once the deploy is healthy, Railway exposes a `*.up.railway.app` URL (or your cu
|
|||
|
||||
Then commands like `fabro model list --server <url>` will hit your Railway instance.
|
||||
|
||||
See [Running the Fabro Server](/administration/deploy-server) for the full auth and CLI-pointing story.
|
||||
See [Server Operations](/reference/server-operations) for the full auth and CLI-pointing story.
|
||||
|
||||
## Redeploys and updates
|
||||
|
||||
Railway re-pulls the GHCR image on every deploy. `Dockerfile.deploy` references the `:nightly` tag by default, so redeploying picks up the latest nightly automatically. To pin a specific version, edit `Dockerfile.deploy` to reference `ghcr.io/fabro-sh/fabro:<version>` and redeploy. The `/storage` Volume survives redeploys, so runs and checkpoints persist.
|
||||
Railway re-pulls the GHCR image on every deploy. The template uses the `:nightly` tag by default, so redeploying picks up the latest nightly automatically. To pin a specific version, change the image in **Service → Settings → Source** to `ghcr.io/fabro-sh/fabro:<version>` and redeploy. The `/storage` Volume survives redeploys, so runs and checkpoints persist.
|
||||
|
||||
## Caveats
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ Railway re-pulls the GHCR image on every deploy. `Dockerfile.deploy` references
|
|||
## Next steps
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Running the Fabro Server" icon="server" href="/administration/deploy-server">
|
||||
<Card title="Server Operations" icon="server" href="/reference/server-operations">
|
||||
Auth, dev tokens, submitting runs, and pointing the CLI at your deployment.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
---
|
||||
title: "Render"
|
||||
description: "Deploy Fabro to Render via the render.yaml blueprint, with a persistent disk for state"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
[Render](https://render.com) can host the Fabro server by pulling the pre-built image published to GHCR. The repo ships a `render.yaml` blueprint that provisions the service and a persistent disk for `/storage` in one click, so your runs, checkpoints, and sessions survive redeploys.
|
||||
|
||||
## One-click deploy
|
||||
|
||||
[](https://render.com/deploy?repo=https://github.com/fabro-sh/fabro)
|
||||
|
||||
The button opens Render's blueprint flow pointed at this repo. `render.yaml` instructs Render to build `Dockerfile.deploy`, which is a thin wrapper that pulls `ghcr.io/fabro-sh/fabro:nightly` — no Rust compilation on Render's builder, so deploys complete in seconds.
|
||||
|
||||
The blueprint declares:
|
||||
|
||||
- A `web` service (Docker runtime) on Render's `starter` plan
|
||||
- A 1 GB persistent disk mounted at `/storage`
|
||||
- A health check on `/health`
|
||||
|
||||
## First-deploy checklist
|
||||
|
||||
The blueprint covers the infrastructure; a few pieces still need to be wired up after the first deploy.
|
||||
|
||||
### 1. Confirm the disk is attached
|
||||
|
||||
Fabro writes all persistent state — run history, checkpoints, sessions, the default token, and JWT keys — under `/storage`. The blueprint provisions a 1 GB disk at that path automatically. Verify in **Service → Disks** that `fabro-storage` is present and mounted at `/storage`. Grow the size later from the same page if needed.
|
||||
|
||||
<Note>
|
||||
Render persistent disks require a paid plan (`starter` or higher). The free tier does not support disks, so the blueprint selects `starter` by default.
|
||||
</Note>
|
||||
|
||||
### 2. Confirm the target port
|
||||
|
||||
Render sets `$PORT` automatically and expects the container to bind to it. The Fabro image honors `$PORT` and falls back to `32276`, so Render's default HTTP routing works without any manual port configuration.
|
||||
|
||||
### 3. Set required environment variables
|
||||
|
||||
Add variables in **Service → Environment** as needed. The [Server Configuration](/administration/server-configuration) reference has the full list; the minimum useful set:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GEMINI_API_KEY` / ... | At least one LLM provider key for the models you'll run |
|
||||
| `FABRO_DEV_TOKEN` | Optional — pre-set the dev token instead of reading the one written to `/storage` on first boot |
|
||||
| `SESSION_SECRET` | 64-character hex string; required when the web UI is enabled |
|
||||
| `GITHUB_APP_CLIENT_SECRET`, `GITHUB_APP_WEBHOOK_SECRET`, `GITHUB_APP_PRIVATE_KEY` | Only if you enable GitHub OAuth or the GitHub App integration |
|
||||
|
||||
No `.env` file is auto-loaded inside the container; everything comes from Render's environment.
|
||||
|
||||
## Accessing your Fabro server
|
||||
|
||||
Once the deploy is healthy, Render exposes a `*.onrender.com` URL (or your custom domain). Two things to grab:
|
||||
|
||||
1. **The dev token** — on first boot, Fabro writes one to `/var/fabro/dev-token` and logs it. Find it in **Service → Logs** in the Render dashboard, or open **Service → Shell** and run `cat /var/fabro/dev-token`.
|
||||
2. **Point your local CLI at the server** — add the Render URL to `~/.fabro/settings.toml`:
|
||||
|
||||
```toml title="~/.fabro/settings.toml"
|
||||
[cli.target]
|
||||
type = "http"
|
||||
url = "https://<your-service>.onrender.com/api/v1"
|
||||
```
|
||||
|
||||
Then commands like `fabro model list --server <url>` will hit your Render instance.
|
||||
|
||||
See [Running the Fabro Server](/administration/deploy-server) for the full auth and CLI-pointing story.
|
||||
|
||||
## Redeploys and updates
|
||||
|
||||
Render re-pulls the GHCR image on every deploy. `Dockerfile.deploy` references the `:nightly` tag by default, so redeploying picks up the latest nightly automatically. To pin a specific version, edit `Dockerfile.deploy` to reference `ghcr.io/fabro-sh/fabro:<version>` and redeploy. The `/storage` disk survives redeploys, so runs and checkpoints persist.
|
||||
|
||||
## Caveats
|
||||
|
||||
- **Disk is load-bearing.** `/storage` holds the dev token and JWT signing keys in addition to runs and checkpoints. Do not detach or resize it destructively once the service is in use.
|
||||
- **Single replica.** Fabro's server currently assumes one process owns `/storage`. Don't scale the service to multiple instances.
|
||||
- **Architecture.** Render runs x86_64 (amd64) containers. The `:nightly` tag is multi-arch, but the arm64 variant is not currently usable — stay on amd64, which is Render's default.
|
||||
|
||||
## Next steps
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Running the Fabro Server" icon="server" href="/administration/deploy-server">
|
||||
Auth, dev tokens, submitting runs, and pointing the CLI at your deployment.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
Full `settings.toml` reference — reverse-proxy TLS, auth methods, concurrency, and more.
|
||||
</Card>
|
||||
</Columns>
|
||||
42
docs/public/administration/deployment.mdx
Normal file
42
docs/public/administration/deployment.mdx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: "Deployment"
|
||||
description: "Choose where the Fabro server runs: on your laptop or self-hosted on a server"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
Fabro runs as a server. The CLI authenticates and communicates with that server, whether the server is running on your laptop or on a remote host. Choose where the server runs based on how you use Fabro.
|
||||
|
||||
## Two deployment modes
|
||||
|
||||
| | Local | Self-hosted |
|
||||
|---|---|---|
|
||||
| **Where the server runs** | Your laptop | A host you operate (Docker container) |
|
||||
| **How to start it** | `fabro server start` | `docker compose up -d` |
|
||||
| **Best for** | Solo use, getting started, learning | Teams, production, 24/7 workflows |
|
||||
| **Trade-off** | Workflows pause when your laptop sleeps or shuts off | You operate the host |
|
||||
|
||||
Both modes use the same image, the same workflow engine, and the same CLI. The only difference is where the server process lives.
|
||||
|
||||
## Local
|
||||
|
||||
Run `fabro server start` on your machine. State persists under `~/.fabro/`. The CLI talks to it over a Unix socket by default. This is the right mode for solo use and getting started — no deployment required.
|
||||
|
||||
When your laptop sleeps or shuts off, in-flight workflows pause until the laptop wakes again. For workflows that need to run 24/7 or for teams sharing a single instance, self-host.
|
||||
|
||||
See [Server Operations](/reference/server-operations) for starting the server, the install wizard, authentication, and pointing the CLI at it.
|
||||
|
||||
## Self-hosted
|
||||
|
||||
For team use, production workflows, or running 24/7, self-host the server as a Docker container. The recommended approach is `docker compose` for a single host, or any cloud container service (ECS, Cloud Run, Kubernetes) using the same image with the same requirements.
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Self-host with Docker" icon="docker" href="/administration/self-host-docker">
|
||||
Compose-first walkthrough. Same image works on ECS, Cloud Run, and Kubernetes.
|
||||
</Card>
|
||||
<Card title="Deploy to Railway" icon="train" href="/administration/deploy-railway">
|
||||
One-click managed shortcut for the same Docker image.
|
||||
</Card>
|
||||
</Columns>
|
||||
143
docs/public/administration/self-host-docker.mdx
Normal file
143
docs/public/administration/self-host-docker.mdx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
title: "Self-host with Docker"
|
||||
description: "Run the Fabro server as a Docker container with docker compose, ECS, or any cloud container service"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
The supported deployment artifact is the official Fabro image at `ghcr.io/fabro-sh/fabro`. Everything else — `docker compose`, ECS, Cloud Run, Kubernetes, Railway — is just running this image somewhere with the right requirements.
|
||||
|
||||
## Requirements
|
||||
|
||||
| Requirement | Value |
|
||||
|---|---|
|
||||
| **Image** | `ghcr.io/fabro-sh/fabro:nightly` (multi-arch; pin a version for production) |
|
||||
| **Persistent volume** | Mount at `/storage`. Stores run history, checkpoints, sessions, the dev token, and JWT keys. |
|
||||
| **Port** | The container binds to `$PORT` (default `32276`). Expose it. |
|
||||
| **LLM provider key** | At least one of `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, ... |
|
||||
| **Replicas** | One. The server expects exclusive ownership of `/storage`. |
|
||||
|
||||
## Quickstart with docker compose
|
||||
|
||||
The repo ships a `docker-compose.yaml` at the root. Clone the repo (or copy the file), create a `.env` with at least one provider key, and start it:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/fabro-sh/fabro.git
|
||||
cd fabro
|
||||
cp .env.example .env
|
||||
# edit .env and set at least ANTHROPIC_API_KEY (or another provider key)
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The compose file:
|
||||
|
||||
- Pulls `ghcr.io/fabro-sh/fabro:nightly`
|
||||
- Creates a named volume `fabro-storage` mounted at `/storage`
|
||||
- Mounts `/var/run/docker.sock` so Fabro can spawn sandbox containers on the host daemon
|
||||
- Exposes port `32276`
|
||||
- Loads environment from `.env` if present
|
||||
|
||||
<Warning>
|
||||
Mounting `/var/run/docker.sock` gives the container host-root-equivalent access. Only use the bundled compose service in trusted, single-tenant deployments. See [Sandboxing](/administration/sandboxing) for the threat model.
|
||||
</Warning>
|
||||
|
||||
After the container is healthy, finish setup in your browser following the [install wizard](/reference/server-operations#first-run-web-install-wizard).
|
||||
|
||||
### Adding a reverse proxy with TLS
|
||||
|
||||
For a production deployment exposed to the internet, layer the `docker-compose.prod.yaml` overlay on top. It adds a [Caddy](https://caddyserver.com) reverse proxy that terminates TLS (auto-provisioning a Let's Encrypt certificate) and forwards to Fabro:
|
||||
|
||||
```bash
|
||||
# In .env
|
||||
FABRO_DOMAIN=fabro.example.com
|
||||
|
||||
# Then bring the stack up
|
||||
docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d
|
||||
```
|
||||
|
||||
Leave `FABRO_DOMAIN` unset to serve plain HTTP on `localhost`.
|
||||
|
||||
## Required environment variables
|
||||
|
||||
At minimum, set one LLM provider key in `.env`:
|
||||
|
||||
```bash title=".env"
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
|
||||
For the web UI you also need a session secret:
|
||||
|
||||
```bash
|
||||
SESSION_SECRET=<64-character hex string>
|
||||
```
|
||||
|
||||
Generate one with `openssl rand -hex 32`.
|
||||
|
||||
Optional:
|
||||
|
||||
| Variable | Purpose |
|
||||
|---|---|
|
||||
| `FABRO_DEV_TOKEN` | Pre-set the dev token instead of reading the one written to `/storage` on first boot |
|
||||
| `GITHUB_APP_CLIENT_SECRET`, `GITHUB_APP_WEBHOOK_SECRET`, `GITHUB_APP_PRIVATE_KEY` | Only if you enable GitHub OAuth or the GitHub App integration |
|
||||
| `FABRO_DOMAIN` | Public hostname when using the Caddy reverse-proxy overlay |
|
||||
|
||||
See [Server Configuration](/administration/server-configuration) for the full settings reference, and [`.env.example`](https://github.com/fabro-sh/fabro/blob/main/.env.example) for the complete list.
|
||||
|
||||
## Cloud container services
|
||||
|
||||
The same image works on any container orchestrator that supports the requirements above. Common patterns:
|
||||
|
||||
- **AWS ECS / Fargate** — Task definition referencing `ghcr.io/fabro-sh/fabro:nightly`, EFS volume mounted at `/storage`, port `32276` published, environment variables for keys.
|
||||
- **Google Cloud Run** — Cloud Run with a backed volume mount at `/storage`. Pin minimum instances to 1; scale-to-zero interrupts running workflows.
|
||||
- **Kubernetes** — One-replica `StatefulSet` (not Deployment) with a `PersistentVolumeClaim` mounted at `/storage`. Expose via Service + Ingress.
|
||||
|
||||
In all cases: single replica, persistent `/storage`, expose `$PORT`, set provider keys.
|
||||
|
||||
## Pinning a version
|
||||
|
||||
`docker-compose.yaml` uses `:nightly` by default, so `docker compose pull && docker compose up -d` picks up the latest nightly. To pin a specific version, change the `image:` line to `ghcr.io/fabro-sh/fabro:<version>`.
|
||||
|
||||
Release artifacts ship with [SLSA Build Provenance](/reference/verifying-releases) attestations you can verify with `gh attestation verify`.
|
||||
|
||||
## Pointing the CLI at your server
|
||||
|
||||
Once the container is running, install the CLI on your local machine and point it at the server:
|
||||
|
||||
```toml title="~/.fabro/settings.toml"
|
||||
[cli.target]
|
||||
type = "http"
|
||||
url = "https://fabro.example.com/api/v1"
|
||||
```
|
||||
|
||||
For dev-token auth, save the token in the CLI auth store:
|
||||
|
||||
```bash
|
||||
fabro auth login --server https://fabro.example.com/api/v1 --dev-token fabro_dev_...
|
||||
```
|
||||
|
||||
See [Server Operations](/reference/server-operations#pointing-the-cli-at-a-server) for the full CLI-target options.
|
||||
|
||||
## Caveats
|
||||
|
||||
- **Volume is load-bearing.** Without a persistent volume at `/storage`, redeploys silently wipe all state — including the dev token and JWT signing keys.
|
||||
- **Single replica.** The server expects exclusive ownership of `/storage`. Don't scale to multiple replicas.
|
||||
- **Architecture.** The `:nightly` tag is multi-arch. The amd64 variant is the most heavily tested.
|
||||
|
||||
## Next steps
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Server Operations" icon="server" href="/reference/server-operations">
|
||||
Install wizard, web UI, authentication, demo mode, and pointing the CLI at the server.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
Full settings.toml reference — auth, reverse-proxy TLS, run defaults, and more.
|
||||
</Card>
|
||||
<Card title="Deploy to Railway" icon="train" href="/administration/deploy-railway">
|
||||
One-click managed shortcut for the same Docker image.
|
||||
</Card>
|
||||
<Card title="Sandboxing" icon="shield" href="/administration/sandboxing">
|
||||
The Docker sandbox provider's security model and trust assumptions.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
|
@ -33,7 +33,7 @@ It checks:
|
|||
|
||||
**Port already in use** — Change the port with `fabro server start --port 3001` or stop the conflicting process.
|
||||
|
||||
**SSE streams disconnecting** — If using a reverse proxy, ensure buffering is disabled and the connection timeout is long enough for workflow runs. See the [DigitalOcean reverse-proxy example](/administration/deploy-digital-ocean).
|
||||
**SSE streams disconnecting** — If using a reverse proxy, ensure buffering is disabled and the connection timeout is long enough for workflow runs.
|
||||
|
||||
**Run config validation errors** — Use `fabro preflight` to validate without executing:
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Fabro has two interfaces, both backed by the same workflow engine:
|
|||
- **Direct CLI runs** (`fabro run`) — Run a single workflow synchronously in your terminal. Best for local development, one-off runs, and CI/CD.
|
||||
- **Server interface** (`fabro server start`) — Start an HTTP API server with a web UI, concurrent run scheduling, and team access. Best for production use and running at scale.
|
||||
|
||||
Both interfaces parse the same Graphviz files, use the same execution engine, and support the same sandbox providers. See [Running the Fabro Server](/administration/deploy-server) for a detailed comparison and setup guide, or [Architecture](/reference/architecture) for internals.
|
||||
Both interfaces parse the same Graphviz files, use the same execution engine, and support the same sandbox providers. See [Deployment](/administration/deployment) for where the server runs and [Server Operations](/reference/server-operations) for operating it, or [Architecture](/reference/architecture) for internals.
|
||||
|
||||
## Author time
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@
|
|||
"reference/run-directory",
|
||||
"reference/sdk",
|
||||
"reference/architecture",
|
||||
"reference/server-operations",
|
||||
"reference/verifying-releases",
|
||||
"administration/server-configuration",
|
||||
"administration/troubleshooting",
|
||||
|
|
@ -156,11 +157,9 @@
|
|||
"group": "Deployment",
|
||||
"icon": "server",
|
||||
"pages": [
|
||||
"administration/deploy-server",
|
||||
"administration/deploy-railway",
|
||||
"administration/deploy-render",
|
||||
"administration/deploy-fly-io",
|
||||
"administration/deploy-digital-ocean"
|
||||
"administration/deployment",
|
||||
"administration/self-host-docker",
|
||||
"administration/deploy-railway"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ description: "Get up and running with Fabro"
|
|||
---
|
||||
|
||||
<Note>
|
||||
Fabro has two modes:
|
||||
|
||||
- **Standalone mode** — Run workflows directly from the CLI. This is what the quick start covers below.
|
||||
- **Server mode** — An API server with a web UI for launching and managing workflow runs at scale. See [Server Mode](/administration/deploy-server) for details.
|
||||
Fabro runs as a server. This quickstart runs everything locally on your laptop — no deployment required. To self-host the server for a team or 24/7 workflows, see [Deployment](/administration/deployment).
|
||||
</Note>
|
||||
|
||||
## Supported platforms
|
||||
|
|
@ -54,7 +51,7 @@ Release binaries and the multi-arch Docker image ship with [SLSA Build Provenanc
|
|||
</Tip>
|
||||
|
||||
<Note>
|
||||
Setting up server mode instead? Run `fabro server start` to finish setup in a browser-based wizard. See [Running the Fabro Server](/administration/deploy-server) for the full flow.
|
||||
Self-hosting the server? See [Self-host with Docker](/administration/self-host-docker) and [Server Operations](/reference/server-operations) for the install wizard, auth, and CLI-pointing.
|
||||
</Note>
|
||||
|
||||
## Initialize your project
|
||||
|
|
|
|||
|
|
@ -89,4 +89,4 @@ The UI provides:
|
|||
|
||||
## Comparison
|
||||
|
||||
See [Server Mode](/administration/deploy-server#standalone-vs-server-mode) for a full feature comparison between standalone and server mode.
|
||||
See [Deployment](/administration/deployment) for where the Fabro server runs and the trade-offs between local and self-hosted modes.
|
||||
|
|
|
|||
|
|
@ -1,28 +1,13 @@
|
|||
---
|
||||
title: "Running the Fabro Server"
|
||||
description: "Run Fabro as an API server with a web UI, concurrent runs, and team access"
|
||||
title: "Server Operations"
|
||||
description: "Operate the Fabro server: starting, install wizard, auth, web UI, and pointing the CLI at it"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The server interface is in private early access. Contact [bryan@qlty.sh](mailto:bryan@qlty.sh) if you're interested in trying it.
|
||||
</Warning>
|
||||
|
||||
Fabro has two interfaces to the same workflow engine. You can run a workflow directly in the CLI with `fabro run`, or start the HTTP server with `fabro server start` to queue runs, stream events, and serve the web UI.
|
||||
|
||||
Both interfaces use the same workflow engine, the same Graphviz files, and the same sandbox providers. The difference is how you interact with them.
|
||||
|
||||
## Direct CLI Runs vs. Server Interface
|
||||
|
||||
| | Direct CLI runs | Server interface |
|
||||
|---|---|---|
|
||||
| **Command** | `fabro run workflow.fabro` | `fabro server start` |
|
||||
| **Best for** | Local development, one-off runs, CI/CD | Production, team use, running at scale |
|
||||
| **Execution** | Synchronous, one run per process | Asynchronous, queued with configurable concurrency |
|
||||
| **Human-in-the-loop** | Terminal prompts | Web UI or HTTP endpoints |
|
||||
| **Events** | Printed to stderr | Streamed via SSE |
|
||||
| **Persistence** | Checkpoint files only | Persistent run store + checkpoint files |
|
||||
| **Web UI** | Not available | Full React interface |
|
||||
| **Authentication** | None | Dev token and/or GitHub OAuth |
|
||||
This page covers operating the Fabro server once it's running, whether locally on your laptop or self-hosted in a container. For where to run it, see [Deployment](/administration/deployment).
|
||||
|
||||
## Starting the server
|
||||
|
||||
|
|
@ -68,7 +53,7 @@ See [Server Configuration](/administration/server-configuration) for the full `s
|
|||
|
||||
## Submitting runs
|
||||
|
||||
In the server interface, workflows are submitted via the REST API and executed in the background. The exact request body is documented in the API reference:
|
||||
Workflows are submitted via the REST API and executed in the background. The exact request body is documented in the API reference:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/v1/runs
|
||||
|
|
@ -110,7 +95,7 @@ The API streams run events via [Server-Sent Events (SSE)](/api-reference/runs/st
|
|||
|
||||
## Human-in-the-loop
|
||||
|
||||
In the server interface, human-in-the-loop questions are served over HTTP instead of terminal prompts. The engine blocks the current stage until an answer is submitted, then continues execution. See the [list questions](/api-reference/human-in-the-loop/list-run-questions) and [submit answer](/api-reference/human-in-the-loop/submit-run-answer) API reference pages.
|
||||
Human-in-the-loop questions are served over HTTP. The engine blocks the current stage until an answer is submitted, then continues execution. See the [list questions](/api-reference/human-in-the-loop/list-run-questions) and [submit answer](/api-reference/human-in-the-loop/submit-run-answer) API reference pages.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
|
@ -162,16 +147,16 @@ See [User Configuration](/reference/user-configuration#cli-target-section) for t
|
|||
## Next steps
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Deployment" icon="server" href="/administration/deployment">
|
||||
Choose where the server runs: laptop or self-hosted Docker container.
|
||||
</Card>
|
||||
<Card title="Server Configuration" icon="gear" href="/administration/server-configuration">
|
||||
Full settings.toml reference — authentication, reverse-proxy TLS, run defaults, and more.
|
||||
</Card>
|
||||
<Card title="Deploy to Railway" icon="train" href="/administration/deploy-railway">
|
||||
Step-by-step guide for deploying Fabro on Railway.
|
||||
</Card>
|
||||
<Card title="API Reference" icon="code" href="/api-reference/overview">
|
||||
REST API for submitting runs, streaming events, and managing resources.
|
||||
</Card>
|
||||
<Card title="How Fabro Works" icon="lightbulb" href="/core-concepts/how-fabro-works">
|
||||
The workflow engine that powers both interfaces.
|
||||
The workflow engine and architecture.
|
||||
</Card>
|
||||
</Columns>
|
||||
35
fly.toml
35
fly.toml
|
|
@ -1,35 +0,0 @@
|
|||
# Fly.io deployment config for the Fabro server.
|
||||
#
|
||||
# Pulls the multi-arch image published to GHCR instead of building from
|
||||
# source on every deploy. Persists state to a Fly Volume mounted at
|
||||
# /storage. The Fabro binary binds to $PORT or 32276, so matching
|
||||
# internal_port to 32276 avoids needing to wire PORT as a secret.
|
||||
#
|
||||
# First-time setup:
|
||||
# fly launch --copy-config --no-deploy # adopts this file; sets app name/region
|
||||
# fly volumes create storage --size 1 # required — fly.toml cannot create volumes
|
||||
# fly secrets set ANTHROPIC_API_KEY=... SESSION_SECRET=...
|
||||
# fly deploy
|
||||
|
||||
app = "fabro"
|
||||
primary_region = "ord"
|
||||
|
||||
[build]
|
||||
image = "ghcr.io/fabro-sh/fabro:nightly"
|
||||
|
||||
[[mounts]]
|
||||
source = "storage"
|
||||
destination = "/storage"
|
||||
|
||||
[http_service]
|
||||
internal_port = 32276
|
||||
force_https = true
|
||||
auto_stop_machines = "off"
|
||||
auto_start_machines = false
|
||||
|
||||
[[http_service.checks]]
|
||||
path = "/health"
|
||||
method = "GET"
|
||||
interval = "30s"
|
||||
timeout = "5s"
|
||||
grace_period = "10s"
|
||||
|
|
@ -36,9 +36,10 @@ pub use error::{Error, Result};
|
|||
pub use local::LocalSandbox;
|
||||
pub use read_guard::ReadBeforeWriteSandbox;
|
||||
pub use sandbox::{
|
||||
CommandOutputCallback, DirEntry, ExecResult, ExecStreamingResult, GitRunInfo, GitSetupIntent,
|
||||
GrepOptions, Sandbox, SandboxEvent, SandboxEventCallback, format_lines_numbered,
|
||||
git_push_via_exec, setup_git_via_exec, shell_quote,
|
||||
CommandOutputCallback, DEFAULT_EXEC_OUTPUT_TAIL_BYTES, DirEntry, ExecResult,
|
||||
ExecStreamingResult, GitRunInfo, GitSetupIntent, GrepOptions, Sandbox, SandboxEvent,
|
||||
SandboxEventCallback, format_lines_numbered, git_push_via_exec, redacted_output_tail,
|
||||
setup_git_via_exec, shell_quote,
|
||||
};
|
||||
pub use sandbox_provider::SandboxProvider;
|
||||
pub use sandbox_record::SandboxRecord;
|
||||
|
|
|
|||
|
|
@ -451,15 +451,7 @@ impl ExecResult {
|
|||
&self,
|
||||
max_bytes_per_stream: usize,
|
||||
) -> Option<fabro_types::ExecOutputTail> {
|
||||
let (stdout, stdout_truncated) = redacted_tail(&self.stdout, max_bytes_per_stream);
|
||||
let (stderr, stderr_truncated) = redacted_tail(&self.stderr, max_bytes_per_stream);
|
||||
let tail = fabro_types::ExecOutputTail {
|
||||
stdout,
|
||||
stderr,
|
||||
stdout_truncated,
|
||||
stderr_truncated,
|
||||
};
|
||||
(!tail.is_empty()).then_some(tail)
|
||||
redacted_output_tail(&self.stdout, &self.stderr, max_bytes_per_stream)
|
||||
}
|
||||
|
||||
pub fn default_redacted_output_tail(&self) -> Option<fabro_types::ExecOutputTail> {
|
||||
|
|
@ -488,6 +480,26 @@ impl ExecResult {
|
|||
}
|
||||
}
|
||||
|
||||
/// Build a redacted `ExecOutputTail` from raw stdout/stderr without
|
||||
/// fabricating a synthetic `ExecResult`. Pass `""` for either stream that
|
||||
/// isn't relevant. Returns `None` when both streams are empty.
|
||||
#[must_use]
|
||||
pub fn redacted_output_tail(
|
||||
stdout: &str,
|
||||
stderr: &str,
|
||||
max_bytes_per_stream: usize,
|
||||
) -> Option<fabro_types::ExecOutputTail> {
|
||||
let (stdout, stdout_truncated) = redacted_tail(stdout, max_bytes_per_stream);
|
||||
let (stderr, stderr_truncated) = redacted_tail(stderr, max_bytes_per_stream);
|
||||
let tail = fabro_types::ExecOutputTail {
|
||||
stdout,
|
||||
stderr,
|
||||
stdout_truncated,
|
||||
stderr_truncated,
|
||||
};
|
||||
(!tail.is_empty()).then_some(tail)
|
||||
}
|
||||
|
||||
fn redacted_tail(text: &str, max_bytes: usize) -> (Option<String>, bool) {
|
||||
if text.is_empty() || max_bytes == 0 {
|
||||
return (None, !text.is_empty());
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ fn security_doc_does_not_require_jwt_keys_for_the_current_web_flow() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn deploy_server_doc_links_to_the_cli_target_section_slug() {
|
||||
let deploy_server = read_doc("docs/public/administration/deploy-server.mdx");
|
||||
fn server_operations_doc_links_to_the_cli_target_section_slug() {
|
||||
let server_operations = read_doc("docs/public/reference/server-operations.mdx");
|
||||
assert!(
|
||||
deploy_server.contains("/reference/user-configuration#cli-target-section"),
|
||||
"deploy-server doc should link to the Mintlify slug for the [cli.target] section"
|
||||
server_operations.contains("/reference/user-configuration#cli-target-section"),
|
||||
"server-operations doc should link to the Mintlify slug for the [cli.target] section"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -326,21 +326,14 @@ fn parse_fast_import_mark(stdout: &str) -> Result<String, SandboxMetadataError>
|
|||
"git fast-import did not report imported commit mark (stdout_bytes={})",
|
||||
stdout.len()
|
||||
),
|
||||
exec_output_tail: stdout_output_tail(stdout),
|
||||
exec_output_tail: fabro_sandbox::redacted_output_tail(
|
||||
stdout,
|
||||
"",
|
||||
fabro_sandbox::DEFAULT_EXEC_OUTPUT_TAIL_BYTES,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn stdout_output_tail(stdout: &str) -> Option<fabro_types::ExecOutputTail> {
|
||||
fabro_sandbox::ExecResult {
|
||||
stdout: stdout.to_string(),
|
||||
stderr: String::new(),
|
||||
exit_code: Some(0),
|
||||
termination: fabro_types::CommandTermination::Exited,
|
||||
duration_ms: 0,
|
||||
}
|
||||
.default_redacted_output_tail()
|
||||
}
|
||||
|
||||
fn fast_import_ident(author: &GitAuthor) -> String {
|
||||
let name = author
|
||||
.name
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
[build]
|
||||
builder = "dockerfile"
|
||||
dockerfilePath = "Dockerfile.deploy"
|
||||
|
||||
[deploy]
|
||||
healthcheckPath = "/health"
|
||||
restartPolicyType = "ALWAYS"
|
||||
restartPolicyMaxRetries = 5
|
||||
11
render.yaml
11
render.yaml
|
|
@ -1,11 +0,0 @@
|
|||
services:
|
||||
- type: web
|
||||
name: fabro
|
||||
runtime: docker
|
||||
dockerfilePath: ./Dockerfile.deploy
|
||||
plan: starter
|
||||
healthCheckPath: /health
|
||||
disk:
|
||||
name: fabro-storage
|
||||
mountPath: /storage
|
||||
sizeGB: 1
|
||||
Loading…
Add table
Reference in a new issue