mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-03 02:24:33 +00:00
## Summary
Extends the Slack integration to post `run.started`, `run.completed`,
and `run.failed` notifications, configured per-run or per-workflow
through `[run.notifications]` rather than server config. Interview
behavior is unchanged and keeps its own state.
### What changed and why
**`SlackService` is now started whenever Slack credentials are
present**, regardless of whether `default_channel` is set. Previously,
the service required `default_channel` to initialize, which blocked
lifecycle notifications for users who have no interview default.
`default_channel` is now `Option<String>` and is only consulted in the
`InterviewStarted` path.
**`handle_event` receives the full `EventEnvelope` and `AppState`**
instead of just the `RunEvent`. Lifecycle handling needs to read the
cached run projection (for `[run.notifications]` routes) and scan prior
events (for PR details and the `run.started` event name), both of which
require `AppState`.
**Lifecycle path in `handle_event`** (`RunStarted` / `RunCompleted` /
`RunFailed`):
1. Reads the run projection to find enabled Slack routes whose `events`
list contains the current event name.
2. For terminal events, scans prior run events to recover
`PullRequestCreated` details and the `run.started` event name.
3. Resolves each route's channel (supporting `{{ env.VAR }}`
interpolation); warns and skips on missing/empty/unresolved channels
without affecting other routes.
4. Posts once per matching route concurrently via `join_all`; post
failures are logged, never propagated.
**`fabro-slack/src/blocks.rs`** adds `run_lifecycle_blocks` and helpers
separate from the interview builders:
- `RunLifecycleKind` uses `strum::IntoStaticStr` for the title string.
- All untrusted fields go through `escape_slack_controls` +
`truncate_to_limit`.
- `compact_duration` formats milliseconds into human-readable strings
(`1.2s`, `1m 5s`, `2h 30m`, …).
- PR line includes number, optional URL link, and optional HTML-escaped
title.
**`SlackClient::with_api_base_and_http`** is added as a test constructor
so server tests can point the client at a `MockServer` without going
through the normal builder path.
### Design decisions
- Lifecycle notifications are fire-and-forget and never touch
`posted_messages` or `thread_registry`, keeping interview and
notification state fully separate.
- `default_channel` is only used for interviews; lifecycle channel
always comes from `[run.notifications.<name>.slack].channel`. This
matches the goal of not promoting per-run config into server config.
- PR title is sourced only from prior `PullRequestCreated` events — no
GitHub API call is made at notification time. If only a
`PullRequestLink` is available in the projection, number and URL are
included but title is omitted.
- Workflow label resolution follows a priority chain: workflow name →
workflow slug → graph name → `run.started` event name → raw event name.
### Plan Summary
- Make `SlackService` start without `default_channel`; gate interview
path on `default_channel` presence.
- Add `handle_lifecycle_event` that filters routes, loads prior events,
builds blocks, resolves channels, and fans out posts.
- Add `run_lifecycle_blocks` Block Kit builder with escaping,
truncation, and `compact_duration`.
- Add server integration tests covering: started/completed/failed
posting, route filtering, missing/unresolved channel skipping, PR
details from prior events, and interview/lifecycle state isolation.
- Update public docs for Slack integration and run configuration.
### Fabro Details
<details>
<summary>Ran 9 stages in 53m 38s for $22.76</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 2s | – | 0 |
| preflight_compile | 1m 58s | – | 0 |
| preflight_lint | 2m 11s | – | 0 |
| implement | 23m 3s | $14.18 | 0 |
| simplify_opus | 16m 37s | $6.36 | 0 |
| simplify_gpt | 5m 30s | $2.23 | 0 |
| verify | 3m 31s | – | 0 |
| fmt | 3s | – | 0 |
| **Total** | **53m 38s** | **$22.76** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
fmt [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> fmt [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
fmt -> exit
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bhelmkamp@users.noreply.github.com>
406 lines
17 KiB
Text
406 lines
17 KiB
Text
---
|
|
title: "Server Configuration"
|
|
description: "Server-owned settings.toml sections, CLI overrides, and environment variables"
|
|
---
|
|
|
|
## Config file
|
|
|
|
`fabro server start` reads `~/.fabro/settings.toml` by default. This is the same file schema used by the CLI.
|
|
|
|
On a same-machine setup, the CLI and server share one `settings.toml`. On a remote deployment, the server machine has its own `settings.toml`, and the client machine keeps a separate local `settings.toml` for CLI-only values such as `[cli.target]`.
|
|
|
|
<Note>
|
|
Fabro only reads `settings.toml`. Older `server.toml`, `user.toml`, and `cli.toml` filenames are no longer part of the supported config surface.
|
|
</Note>
|
|
|
|
### Which sections are server-owned
|
|
|
|
| Scope | Examples |
|
|
|---|---|
|
|
| Server-owned (runtime-only from local `settings.toml`) | `[server.listen]`, `[server.api]`, `[server.web]`, `[server.auth]`, `[server.storage]`, `[server.artifacts]`, `[server.slatedb]`, `[server.scheduler]`, `[server.logging]`, `[server.integrations]` |
|
|
| Shared run defaults (layered through `.fabro/project.toml`/`workflow.toml`) | `[run.model]`, `[run.prepare]`, `[run.environment]`, `[environments.<slug>]`, `[run.checkpoint]`, `[run.inputs]`, `[run.pull_request]`, `[run.git]`, `[run.hooks]`, `[run.agent]` |
|
|
|
|
The CLI-only `[cli.*]` sections (including `[cli.target]`) belong in the client machine's `settings.toml`. They tell CLI commands how to reach a server. The server process does not read `[cli.*]` for its own binding or routing.
|
|
|
|
Fabro does not terminate inbound TLS directly. Bind `[server.listen]` to a Unix socket or plain TCP port, and terminate HTTPS or mTLS at a reverse proxy, load balancer, or platform ingress in front of Fabro. Use `[server.api].url` and `[server.web].url` for those external HTTPS URLs.
|
|
|
|
### Full reference
|
|
|
|
```toml title="settings.toml"
|
|
_version = 1
|
|
|
|
[server.listen]
|
|
type = "tcp"
|
|
address = "0.0.0.0:3000"
|
|
|
|
[server.api]
|
|
url = "https://fabro.example.com/api/v1"
|
|
|
|
[server.web]
|
|
enabled = true
|
|
url = "https://fabro.example.com"
|
|
|
|
[server.auth]
|
|
methods = ["dev-token", "github"]
|
|
|
|
[server.auth.github]
|
|
allowed_usernames = ["alice", "bob"]
|
|
|
|
[server.integrations.github]
|
|
app_id = "123456"
|
|
client_id = "Iv1.abc123"
|
|
|
|
[server.integrations.github.webhooks]
|
|
strategy = "tailscale_funnel"
|
|
|
|
[server.storage]
|
|
root = "/var/lib/fabro"
|
|
|
|
[server.artifacts]
|
|
provider = "s3"
|
|
prefix = "artifacts"
|
|
|
|
[server.artifacts.s3]
|
|
bucket = "my-fabro-data"
|
|
region = "us-east-1"
|
|
|
|
[server.slatedb]
|
|
provider = "s3"
|
|
prefix = "slatedb"
|
|
disk_cache = true
|
|
|
|
[server.slatedb.s3]
|
|
bucket = "my-fabro-data"
|
|
region = "us-east-1"
|
|
|
|
[server.scheduler]
|
|
max_concurrent_runs = 8
|
|
|
|
[server.logging]
|
|
level = "info"
|
|
|
|
# Run defaults — applied to every run unless overridden by workflow/project config
|
|
[run.model]
|
|
name = "claude-sonnet-4-5"
|
|
provider = "anthropic"
|
|
fallbacks = ["gemini", "openai"]
|
|
|
|
[[run.prepare.steps]]
|
|
script = "npm install"
|
|
|
|
[run.environment]
|
|
id = "cloud"
|
|
|
|
[environments.cloud]
|
|
provider = "daytona"
|
|
|
|
[environments.cloud.lifecycle]
|
|
auto_stop = "60m"
|
|
|
|
[environments.cloud.labels]
|
|
team = "platform"
|
|
|
|
[run.checkpoint]
|
|
exclude_globs = ["**/node_modules/**", "**/.cache/**"]
|
|
skip_git_hooks = false
|
|
|
|
[run.inputs]
|
|
default_branch = "main"
|
|
|
|
[run.git.author]
|
|
name = "fabro-bot"
|
|
email = "fabro-bot@company.com"
|
|
```
|
|
|
|
### CLI overrides
|
|
|
|
Several `settings.toml` settings can be overridden via `fabro server start` flags:
|
|
|
|
| Flag | Default | Description |
|
|
|---|---|---|
|
|
| `--bind` | Resolved `[server.listen]`, falling back to `~/.fabro/fabro.sock` when `[server.listen]` is absent | Address to bind: `IP` or `IP:port` for TCP, or a path for Unix socket |
|
|
| `--web` | enabled | Enable the embedded web UI, browser auth routes, and web-only helper endpoints |
|
|
| `--no-web` | disabled | Disable the embedded web UI, browser auth routes, and web-only helper endpoints |
|
|
| `--foreground` | — | Run in the foreground instead of daemonizing |
|
|
| `--model` | — | Override default LLM model |
|
|
| `--provider` | — | Override default LLM provider |
|
|
| `--environment` | — | Override default environment slug |
|
|
| `--max-concurrent-runs` | `5` | Maximum concurrent run executions |
|
|
| `--config` | `~/.fabro/settings.toml` | Path to server config file |
|
|
|
|
CLI flags take precedence over `settings.toml` values. See [Run Configuration — Precedence](/execution/run-configuration#precedence) for the full resolution order.
|
|
|
|
### `[server.web]` section
|
|
|
|
Control the embedded SPA and browser-oriented routes.
|
|
|
|
| Key | Description | Default |
|
|
|---|---|---|
|
|
| `enabled` | Serve the embedded SPA, `/auth/*`, and the web-only helper endpoints under `/api/v1` | `true` |
|
|
| `url` | Required single canonical origin for the server. The browser UI and `/auth/*` routes use this origin, and it must be an absolute `http(s)` URL. | `http://localhost:3000` |
|
|
|
|
When `enabled = false`, the server still exposes the machine API and `/health`, but `/`, `/auth/*`, SPA client routes, `/api/v1/auth/me`, `/api/v1/setup/*`, and `/api/v1/demo/toggle` all return `404`.
|
|
|
|
`server.web.url` is not a secondary web host. Fabro supports a single public origin for API and web traffic. In local development that can be plain HTTP such as `http://localhost:3000` or `http://127.0.0.1:3000`. In production, operators are responsible for terminating HTTPS upstream.
|
|
|
|
### `[server.auth]` section
|
|
|
|
Configure how users authenticate with the server.
|
|
|
|
| Key | Description | Default |
|
|
|---|---|---|
|
|
| `methods` | Ordered list of enabled bootstrap methods: `dev-token`, `github` | `["dev-token"]` |
|
|
|
|
When `"dev-token"` is enabled, the API accepts `Authorization: Bearer fabro_dev_...` and the login page can authenticate with the dev token directly.
|
|
|
|
When `"github"` is enabled, browser users can sign in with GitHub OAuth and receive a session cookie.
|
|
|
|
### `[server.auth.github]` section
|
|
|
|
GitHub-specific auth policy.
|
|
|
|
| Key | Description |
|
|
|---|---|
|
|
| `allowed_usernames` | GitHub usernames allowed to complete OAuth login |
|
|
|
|
The GitHub OAuth client ID still lives under `[server.integrations.github].client_id`.
|
|
|
|
### `[server.slatedb]` section
|
|
|
|
Configure the embedded SlateDB key-value store used for run event storage.
|
|
|
|
| Key | Description | Default |
|
|
|---|---|---|
|
|
| `provider` | Object store backend: `local` or `s3` | `"local"` |
|
|
| `prefix` | Key prefix within the object store | `""` |
|
|
| `flush_interval` | How often to flush the write-ahead log | `"1ms"` |
|
|
| `disk_cache` | Enable a local disk cache for object store reads | `false` |
|
|
|
|
When `disk_cache = true`, Fabro creates a cache directory at `<storage_root>/cache/slatedb` and
|
|
configures SlateDB to cache object store bytes on local disk (16 GB max, 4 MB parts). This
|
|
significantly reduces read latency and costs for S3-backed deployments. A warning is emitted if
|
|
enabled with `provider = "local"` since the disk cache adds overhead when the object store is
|
|
already local.
|
|
|
|
```toml title="settings.toml"
|
|
[server.slatedb]
|
|
provider = "s3"
|
|
disk_cache = true
|
|
|
|
[server.slatedb.s3]
|
|
bucket = "{{ env.SLATEDB_BUCKET }}"
|
|
region = "us-east-1"
|
|
```
|
|
|
|
The browser install wizard's `Object store` step manages both `[server.slatedb]` and
|
|
`[server.artifacts]` together. `Local disk` uses the detected local object-store root, defaulting
|
|
to `<storage_root>/objects`, with fixed prefixes `slatedb` and `artifacts`. `AWS S3` writes one
|
|
shared bucket with the same fixed prefixes.
|
|
|
|
```toml title="Local disk object store"
|
|
[server.artifacts]
|
|
provider = "local"
|
|
prefix = "artifacts"
|
|
|
|
[server.artifacts.local]
|
|
root = "/var/lib/fabro/objects"
|
|
|
|
[server.slatedb]
|
|
provider = "local"
|
|
prefix = "slatedb"
|
|
|
|
[server.slatedb.local]
|
|
root = "/var/lib/fabro/objects"
|
|
```
|
|
|
|
The wizard only covers AWS S3 bucket/region plus one of:
|
|
|
|
- runtime credentials already supplied by the deployment environment
|
|
- manually-entered `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`
|
|
|
|
Advanced S3-compatible settings such as custom `endpoint` or `path_style` remain a manual
|
|
configuration path. If you need MinIO, R2, or another S3-compatible backend, configure
|
|
`[server.slatedb]` and `[server.artifacts]` directly in `settings.toml`. The runtime still
|
|
honors those hand-edited values even though the browser wizard does not manage them.
|
|
|
|
### Run defaults
|
|
|
|
The `[run.*]` sections in `settings.toml` act as defaults for every run.
|
|
|
|
On a same-machine setup, `settings.toml` is the shared machine-default layer under `workflow.toml` and `.fabro/project.toml`.
|
|
|
|
On a remote setup, the client bundles workflow, project, and user config into the run manifest. The server then layers those bundled client configs over its own local defaults for run-shaped fields. Server-owned values like `[server.storage]`, `[server.api]`, `[server.web]`, and `[server.scheduler]` always come from the server machine's own `settings.toml` or `fabro server start` flags.
|
|
|
|
Merge rules follow the normative matrix: TOML `[run.inputs]` tables replace wholesale, CLI `-I` / `--input` values replayed from run manifests merge per key at highest precedence, environment `env` and `labels` merge by key, environment `volumes` replace as a whole list, `[run.prepare.steps]` replaces whole-list, and `[[run.hooks]]` merge by optional `id`. Most other fields use "higher-precedence wins" field-wise merging.
|
|
|
|
### `[server.logging]` section
|
|
|
|
Configure the server log level and destination.
|
|
|
|
| Key | Description | Default |
|
|
|---|---|---|
|
|
| `level` | Log level: `error`, `warn`, `info`, `debug`, `trace` | `"info"` |
|
|
| `destination` | Where server logs are written: `file` (rotated daily under `<storage>/logs/`) or `stdout` | Daemon: `"file"`; foreground: `"stdout"` |
|
|
|
|
Level precedence: `FABRO_LOG` env var > `--debug` flag > `[server.logging].level` > `"info"`.
|
|
|
|
Destination precedence: `FABRO_LOG_DESTINATION` env var > `[server.logging].destination` > command-mode default. `stdout` is incompatible with daemon mode — use `fabro server start --foreground` (which is what container images do). Default install-generated `settings.toml` omits `destination` so foreground mode streams logs to stdout without extra config.
|
|
|
|
The CLI has its own `[cli.logging]` section.
|
|
|
|
### `[run.git.author]` section
|
|
|
|
Customize the git author identity used for checkpoint commits. When not set, defaults to `fabro` / `fabro@local`.
|
|
|
|
| Key | Description | Default |
|
|
|---|---|---|
|
|
| `name` | Git author name | `"fabro"` |
|
|
| `email` | Git author email | `"fabro@local"` |
|
|
|
|
### `[server.integrations.github]` section
|
|
|
|
Configure GitHub integration auth. `strategy = "token"` is the default and uses a stored `GITHUB_TOKEN` from the vault (with `GH_TOKEN` as a fallback). `strategy = "app"` enables the GitHub App flow and browser OAuth; webhook delivery is configured separately under `[server.integrations.github.webhooks]`.
|
|
|
|
```toml title="settings.toml"
|
|
[server.integrations.github]
|
|
strategy = "token"
|
|
```
|
|
|
|
For GitHub App mode, set `strategy = "app"` and include `app_id`, `client_id`, and `slug`. Webhook delivery is configured under `[server.integrations.github.webhooks]`:
|
|
|
|
```toml title="settings.toml"
|
|
[server.integrations.github]
|
|
strategy = "app"
|
|
app_id = "123456"
|
|
client_id = "Iv1.abc123"
|
|
slug = "fabro-app"
|
|
|
|
[server.api]
|
|
url = "https://fabro-api.example.com"
|
|
|
|
[server.integrations.github.webhooks]
|
|
strategy = "server_url"
|
|
```
|
|
|
|
Fabro always serves the GitHub webhook handler at `POST /api/v1/webhooks/github` when `GITHUB_APP_WEBHOOK_SECRET` is configured. The `strategy` field controls how Fabro exposes that route and whether it mutates the GitHub App webhook URL on startup:
|
|
|
|
- `strategy = "server_url"`: recommended for production or any deployment with a stable public API URL. Fabro sets the GitHub App webhook URL to `<server.api.url>/api/v1/webhooks/github` on startup. Requires `server.api.url` and `GITHUB_APP_WEBHOOK_SECRET`.
|
|
- `strategy = "tailscale_funnel"`: opt-in for Tailscale-hosted machines without a stable public URL. Fabro runs `tailscale funnel <server-port>`, exposes the main server on that Funnel URL, and best-effort updates the GitHub App webhook URL to `<funnel-url>/api/v1/webhooks/github`. Requires a TCP listener and `GITHUB_APP_WEBHOOK_SECRET`.
|
|
- `strategy` unset: Fabro still accepts signed webhook deliveries on `/api/v1/webhooks/github` when the secret is present, but it does not run `tailscale funnel` and does not update the GitHub App webhook URL.
|
|
|
|
Incoming webhooks are authenticated only by GitHub's `X-Hub-Signature-256` HMAC signature, not by Fabro's bearer/session auth.
|
|
|
|
### `[run.checkpoint]` section
|
|
|
|
Configure checkpoint behavior for all runs.
|
|
|
|
| Key | Description |
|
|
|---|---|
|
|
| `exclude_globs` | Glob patterns for files to exclude from checkpoint commits (for example, `["**/node_modules/**"]`) |
|
|
| `skip_git_hooks` | When `true`, Fabro-managed run-branch checkpoint commits bypass local Git commit hooks. Defaults to `false`. Does not affect Fabro workflow `[[run.hooks]]` or metadata-branch snapshots. |
|
|
|
|
`exclude_globs` replaces across layers — the highest-precedence layer wins wholesale. `skip_git_hooks` uses normal override semantics. See [Run Configuration — Checkpoint](/execution/run-configuration#runcheckpoint) for per-run configuration.
|
|
|
|
## Secrets and environment variables
|
|
|
|
Fabro splits secrets into two scopes:
|
|
|
|
- Server runtime secrets live in `<data_dir>/server.env` and resolve with precedence `process env -> server.env`.
|
|
- Workflow-visible secrets live in `<data_dir>/vaults/default/secrets.json` (the vault). Anything stored in the vault may be used by workflows.
|
|
|
|
For the auth model above, the main server runtime secrets are:
|
|
|
|
- `SESSION_SECRET` when the web UI is enabled
|
|
- `FABRO_DEV_TOKEN` when `"dev-token"` auth is enabled
|
|
- `GITHUB_APP_CLIENT_SECRET` when `"github"` auth is enabled
|
|
- `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` when the install wizard or a manual config uses
|
|
static S3 object-store credentials
|
|
|
|
Fabro no longer auto-loads `.env` files. Provider API keys are required for the models you want to use; everything else is optional.
|
|
|
|
### LLM provider keys
|
|
|
|
Fabro's built-in provider access resolves these from the process environment first, then an exact-name vault token or OAuth entry.
|
|
|
|
| Variable | Provider |
|
|
|---|---|
|
|
| `ANTHROPIC_API_KEY` | Anthropic (Claude) |
|
|
| `OPENAI_API_KEY` | OpenAI (GPT) |
|
|
| `GEMINI_API_KEY` or `GOOGLE_API_KEY` | Google (Gemini) |
|
|
| `KIMI_API_KEY` | Kimi |
|
|
| `ZAI_API_KEY` | Zai (GLM) |
|
|
| `MINIMAX_API_KEY` | Minimax |
|
|
| `INCEPTION_API_KEY` | Inception (Mercury) |
|
|
|
|
### Sandbox and tools
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `DAYTONA_API_KEY` | Daytona cloud sandbox API key |
|
|
| `BRAVE_SEARCH_API_KEY` | Brave Search API key (for the `web_search` tool) |
|
|
|
|
### Server authentication
|
|
|
|
Fabro resolves these from `process env -> server.env`.
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `SESSION_SECRET` | Session encryption secret (64-character hex string) |
|
|
|
|
### Object store runtime secrets (optional)
|
|
|
|
Fabro resolves these from `process env -> server.env`.
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `AWS_ACCESS_KEY_ID` | Static AWS access key ID for S3-backed `[server.slatedb]` / `[server.artifacts]` |
|
|
| `AWS_SECRET_ACCESS_KEY` | Matching static AWS secret access key |
|
|
|
|
The browser install wizard can write these into `server.env` for the AWS S3 manual-credential
|
|
path. It does not support manual STS/session-token input; use runtime credentials instead for ECS,
|
|
EC2 instance profiles, IRSA, or web-identity flows.
|
|
|
|
For the narrowest production policy, scope access to one bucket and the `slatedb/` and
|
|
`artifacts/` prefixes with `s3:ListBucket` plus `s3:GetObject`, `s3:PutObject`, and
|
|
`s3:DeleteObject`. Prefer a dedicated IAM user or role for Fabro instead of reusing broad AWS
|
|
credentials.
|
|
|
|
If `POST /install/finish` fails after mutating `server.env`, the error response may include
|
|
`leftover_env_keys` or `removed_env_keys`. For AWS object-store keys, treat `removed_env_keys` as
|
|
informational: the prior managed key path was already cleared, so complete the retry before
|
|
restart. If `leftover_env_keys` includes `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY`, retry the
|
|
install or remove the managed object-store lines from `server.env` before abandoning the host.
|
|
Rotation is not required solely because finish failed after an atomic `0600` rewrite, but it
|
|
remains the fallback if you no longer trust the host boundary.
|
|
|
|
### GitHub integration (optional)
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `GITHUB_TOKEN` | GitHub personal access token, stored by `fabro install` when `strategy = "token"`. Also accepts `GH_TOKEN` as a fallback. |
|
|
|
|
### GitHub App extras (optional)
|
|
|
|
Fabro resolves these from `process env -> server.env`.
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `GITHUB_APP_CLIENT_SECRET` | GitHub App client secret |
|
|
| `GITHUB_APP_WEBHOOK_SECRET` | GitHub App webhook secret |
|
|
| `GITHUB_APP_PRIVATE_KEY` | GitHub App private key (base64-encoded) |
|
|
|
|
### Slack integration (optional)
|
|
|
|
Slack credentials are server-level secrets. They enable one Slack connection that is shared by human interview prompts and run lifecycle notifications. `server.integrations.slack.default_channel` is optional and is used only as the default destination for interview prompts; lifecycle notifications use `[run.notifications.<name>.slack].channel` in run or workflow configuration.
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `FABRO_SLACK_APP_TOKEN` | Slack App-level token |
|
|
| `FABRO_SLACK_BOT_TOKEN` | Slack Bot token |
|
|
|
|
### Logging
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `FABRO_LOG` | `info` | Log level: `error`, `warn`, `info`, `debug` |
|
|
| `FABRO_LOG_DESTINATION` | Command-mode default | Server log destination: `file` or `stdout` |
|