diff --git a/.claude/skills/docs/watermark b/.claude/skills/docs/watermark index 1a0244d12..6a5b8ed11 100644 --- a/.claude/skills/docs/watermark +++ b/.claude/skills/docs/watermark @@ -1 +1 @@ -31cbdb5c316e0e128d935371989afef0d4714209 +588f546b6c54006bc1e634f0510d32f6d89cf7c3 diff --git a/docs/public/execution/run-configuration.mdx b/docs/public/execution/run-configuration.mdx index 055ac4054..d499e95b7 100644 --- a/docs/public/execution/run-configuration.mdx +++ b/docs/public/execution/run-configuration.mdx @@ -75,6 +75,10 @@ dockerfile = "FROM node:20-slim\nRUN apt-get update && apt-get install -y git" API_KEY = "{{ env.MY_API_KEY }}" NODE_ENV = "production" +[run.integrations.github.permissions] +contents = "write" +pull_requests = "write" + [run.checkpoint] exclude_globs = ["**/node_modules/**", "**/.cache/**"] @@ -260,6 +264,21 @@ SERVICE_URL = "https://api.{{ env.REGION }}.example.com" Missing host variables produce a hard error pointing at the specific field and unresolved token. `run.sandbox.env` is a sticky merge-by-key map: entries from all layers combine, with higher-precedence layers overriding individual keys. +### `[run.integrations.github.permissions]` + +Request a scoped GitHub App token for workflow stages that need `GITHUB_TOKEN` inside the sandbox. Values map directly to GitHub App permission names and access levels. + +```toml title="run.toml" +[run.integrations.github.permissions] +contents = "write" +pull_requests = "write" +issues = "read" +``` + +Only requested permissions are included. The upper bound is the permission set granted to the installed GitHub App, and Fabro logs a warning and continues without `GITHUB_TOKEN` if the app is not configured or is not installed on the repository. + +This table follows the normal settings precedence order. A higher-precedence layer can set `permissions = {}` to clear inherited permissions and run without a GitHub token. + ### `[run.checkpoint]` Configure how git checkpoint commits behave. diff --git a/docs/public/integrations/github.mdx b/docs/public/integrations/github.mdx index 9dab66ab5..7ccdff726 100644 --- a/docs/public/integrations/github.mdx +++ b/docs/public/integrations/github.mdx @@ -167,7 +167,7 @@ Choose **GitHub CLI** in `fabro install` to use the default local-user flow. The 2. Stores the token as `GITHUB_TOKEN` 3. Writes `strategy = "token"` under `[server.integrations.github]` -After install, Fabro reads `GITHUB_TOKEN` from the vault or environment (with `GH_TOKEN` as a fallback). Token updates after install are the user's responsibility. +After install, Fabro reads `GITHUB_TOKEN` from the vault or environment (with `GH_TOKEN` as a fallback). Token updates after install are the user's responsibility. Short-lived GitHub installation tokens (`ghs_*`) are rejected as static tokens; use a PAT for `token` mode, or use GitHub App mode so Fabro can refresh installation tokens for you. In this mode, Fabro disables the embedded web UI and browser auth routes. Machine API routes and `/health` continue to work. @@ -208,7 +208,7 @@ For public repositories, the clone works without credentials. The token is still ### GITHUB_TOKEN injection -When any settings layer declares `[run.integrations.github.permissions]`, Fabro mints a scoped Installation Access Token at startup and injects it into the sandbox as the `GITHUB_TOKEN` environment variable. Agents running inside the sandbox can use this token for GitHub API calls, cloning additional private repos, or pushing to branches. +When any settings layer declares `[run.integrations.github.permissions]`, Fabro prepares a scoped GitHub App token source and exposes it as the `GITHUB_TOKEN` environment variable in sandbox command and agent execution. Agents running inside the sandbox can use this token for GitHub API calls, cloning additional private repos, or pushing to branches. ```toml title="workflow.toml" [run.integrations.github.permissions] @@ -218,6 +218,8 @@ pull_requests = "write" Only the listed permissions are requested — the token is scoped to the minimum access needed. If the GitHub App isn't configured or the repository lacks an installation, the run logs a warning and continues without the token. +Installation Access Tokens are short-lived, so Fabro refreshes them when they are close to expiry. Command stages and API-mode agent stages resolve `GITHUB_TOKEN` before use, which keeps long workflows working across token rollover. CLI-mode agent stages receive their token at launch time; for very long CLI-agent stages, run GitHub operations through command stages or API-mode agents if mid-stage token refresh matters. + The permissions table follows the standard layer-merge order (workflow > project > user > defaults). Set defaults at `[run.integrations.github.permissions]` in `~/.fabro/settings.toml` so every run inherits a baseline; tighten or override per-workflow as needed. A higher layer that defines `permissions = {}` clears the inherited map (no token requested). #### Security model diff --git a/docs/public/reference/cli.mdx b/docs/public/reference/cli.mdx index dc1d2393f..165f165bc 100644 --- a/docs/public/reference/cli.mdx +++ b/docs/public/reference/cli.mdx @@ -462,7 +462,7 @@ fabro install github [OPTIONS] | Option | Description | | --- | --- | -| `--owner ` | GitHub App owner: 'personal' or 'org:' (app only, requires --non-interactive) | +| `--owner ` | GitHub App owner: `personal` or `org:` (app only, requires --non-interactive) | | `--strategy ` | GitHub authentication strategy (requires --non-interactive)
Values: `token`, `app` | ### `fabro logs` @@ -856,8 +856,8 @@ fabro sandbox cp [OPTIONS] | Name | Description | | --- | --- | -| `SRC` | Source: : or local path | -| `DST` | Destination: : or local path | +| `SRC` | Source: `:` or local path | +| `DST` | Destination: `:` or local path | #### Options diff --git a/docs/public/reference/user-configuration.mdx b/docs/public/reference/user-configuration.mdx index 6e977c141..ac867c155 100644 --- a/docs/public/reference/user-configuration.mdx +++ b/docs/public/reference/user-configuration.mdx @@ -34,7 +34,7 @@ Files that omit `_version` are treated as version `1`. The legacy top-level `ver | Scope | Examples | |---|---| | CLI-only | `[cli.target]`, `[cli.auth]`, `[cli.exec]`, `[cli.output]`, `[cli.updates]`, `[cli.logging]` | -| Shared run defaults | `[run.model]`, `[run.sandbox]`, `[run.checkpoint]`, `[run.inputs]`, `[run.prepare]`, `[run.pull_request]`, `[run.hooks]`, `[run.agent.mcps]` | +| Shared run defaults | `[run.model]`, `[run.sandbox]`, `[run.checkpoint]`, `[run.inputs]`, `[run.prepare]`, `[run.pull_request]`, `[run.integrations.github.permissions]`, `[run.hooks]`, `[run.agent.mcps]` | | Server-only | `[server.listen]`, `[server.api]`, `[server.web]`, `[server.auth]`, `[server.storage]`, `[server.artifacts]`, `[server.slatedb]`, `[server.scheduler]`, `[server.logging]`, `[server.integrations]` | `[cli.*]` and `[server.*]` stanzas are owner-specific: they are only consumed from `~/.fabro/settings.toml` (plus process-local flags and env overrides). The same stanzas in `.fabro/project.toml` or `workflow.toml` remain schema-valid but runtime-inert. @@ -93,6 +93,10 @@ email = "fabro-bot@company.com" [run.pull_request] enabled = true +[run.integrations.github.permissions] +contents = "write" +pull_requests = "write" + [run.agent.mcps.filesystem] type = "stdio" command = ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/workspace"]