Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,24 @@ This works on both Slack (via `files.getUploadURLExternal`) and Telegram (via `s

Additionally, the runtime tracks large tool outputs (>8000 characters) and attaches them as file parts in the A2A response. This ensures channel adapters receive the complete, untruncated tool output even when the LLM's text summary is truncated by output token limits. JSON tool outputs (e.g. Tavily Research/Search results) are automatically unwrapped into readable markdown before delivery.

## Container Deployment

When channels are configured in `forge.yaml`, the build pipeline automatically:

1. **Includes channel config files** — `slack-config.yaml`, `telegram-config.yaml`, etc. are copied into the Docker build context alongside `forge.yaml`
2. **Adds `--with` to the entrypoint** — The container entrypoint becomes `["forge", "run", "--host", "0.0.0.0", "--with", "slack,telegram"]`
3. **Handles auth loopback** — When [external auth](runtime.md#external-authentication) is configured, channel adapters authenticate to the A2A server using an internal token, bypassing the external auth provider

Pass channel secrets via environment variables:

```bash
docker run \
-e SLACK_APP_TOKEN=xapp-... \
-e SLACK_BOT_TOKEN=xoxb-... \
-e FORGE_AUTH_URL=https://auth.example.com/verify \
my-agent
```

## Docker Compose Integration

```bash
Expand Down
25 changes: 25 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ forge build [flags]

Uses global `--config` and `--output-dir` flags. Output is written to `.forge-output/` by default.

### Flags

| Flag | Default | Description |
|------|---------|-------------|
| `--signing-key` | | Path to Ed25519 private key for signing build output |
| `--slim` | `false` | Minimize image size (skip heavy/optional binaries) |
| `--alpine` | `false` | Prefer Alpine base image |
| `--local-bin` | | Local binary override as `name=/path/to/file` (repeatable) |

### Examples

```bash
Expand All @@ -92,6 +101,12 @@ forge build

# Build with custom config and output
forge build --config agent.yaml --output-dir ./build

# Build with a local binary override
forge build --local-bin forge=/path/to/linux/forge

# Build with Alpine base and slim image
forge build --alpine --slim
```

---
Expand Down Expand Up @@ -147,6 +162,7 @@ forge run [flags]
| `--provider` | | LLM provider: `openai`, `anthropic`, or `ollama` |
| `--env` | `.env` | Path to .env file |
| `--with` | | Comma-separated channel adapters (e.g., `slack,telegram`) |
| `--auth-url` | | External auth provider URL for token validation |
| `--cors-origins` | localhost | Comma-separated CORS allowed origins (e.g., `https://app.example.com,https://admin.example.com`). Use `*` to allow all origins |

### Examples
Expand Down Expand Up @@ -277,6 +293,9 @@ forge package [flags]
| `--builder` | | Force builder: `docker`, `podman`, or `buildah` |
| `--skip-build` | `false` | Skip re-running forge build |
| `--with-channels` | `false` | Generate docker-compose.yaml with channel adapters |
| `--slim` | `false` | Minimize image size (skip heavy/optional binaries) |
| `--alpine` | `false` | Prefer Alpine base image |
| `--local-bin` | | Local binary override as `name=/path/to/file` (repeatable) |

### Examples

Expand All @@ -292,6 +311,12 @@ forge package --platform linux/amd64 --no-cache

# Generate docker-compose with channels
forge package --with-channels

# Package with a local binary override
forge package --local-bin forge=/path/to/linux/forge

# Package with slim Alpine image
forge package --alpine --slim
```

---
Expand Down
16 changes: 14 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,18 @@ egress:
cors_origins: # CORS allowed origins for A2A server
- "https://app.example.com" # (default: localhost variants)

skills:
path: "SKILL.md"
package:
alpine: false # Prefer Alpine base image
slim: false # Minimize image size
bin_overrides: # Per-binary install overrides
forge:
local: "/path/to/linux/forge" # Host path to local binary file
jq:
apt: "jq" # APT package name
custom-tool:
url: "https://example.com/tool.tar.gz" # Direct download URL
dest: "/usr/local/bin/custom-tool" # Install destination
chmod: "0755" # File permissions

secrets:
providers: # Secret providers (order matters)
Expand Down Expand Up @@ -96,6 +106,8 @@ schedules: # Recurring scheduled tasks (optional)
| `ANTHROPIC_BASE_URL` | Override Anthropic base URL |
| `OLLAMA_BASE_URL` | Override Ollama base URL (default: `http://localhost:11434`) |
| `FORGE_CORS_ORIGINS` | Comma-separated CORS allowed origins for A2A server |
| `FORGE_AUTH_URL` | External auth provider URL for token validation |
| `FORGE_AUTH_ORG_ID` | Organization ID sent to external auth provider |
| `FORGE_PASSPHRASE` | Passphrase for encrypted secrets file |

---
Expand Down
23 changes: 23 additions & 0 deletions docs/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ forge run --host 0.0.0.0 --shutdown-timeout 30s
| `--env` | `.env` | Path to env file |
| `--enforce-guardrails` | `true` | Enforce guardrail violations as errors |
| `--no-guardrails` | `false` | Disable all guardrail enforcement |
| `--auth-url` | — | External auth provider URL for token validation |

### `forge serve` — Background Daemon

Expand Down Expand Up @@ -200,6 +201,28 @@ forge serve logs

The daemon forks `forge run` in the background with `setsid`, writes state to `.forge/serve.json`, and redirects output to `.forge/serve.log`. Passphrase prompting for encrypted secrets happens in the parent process (which has TTY access) before forking.

## External Authentication

When `--auth-url` is set (or `FORGE_AUTH_URL` env var), the runtime delegates token validation to an external auth provider. On each request, the bearer token is forwarded to the external URL for verification.

```bash
# Via CLI flag
forge run --auth-url https://auth.example.com/verify

# Via environment variable (useful in containers)
docker run -e FORGE_AUTH_URL=https://auth.example.com/verify my-agent
```

The middleware checks tokens in two layers: an internal token is accepted first (used by channel adapter loopback calls), then the external auth provider is consulted. This ensures channel adapters (Slack, Telegram) can reach the A2A server without needing a valid external token.

## KUBECONFIG Materialization

The runtime supports passing kubeconfig content directly via the `KUBECONFIG` environment variable. If `KUBECONFIG` contains inline YAML (detected by newlines or `apiVersion:` markers), the runtime automatically writes it to a file and updates `KUBECONFIG` to point to that file. This is useful for container deployments where mounting files is inconvenient:

```bash
docker run -e KUBECONFIG="$(cat ~/.kube/config)" my-agent
```

## File Output Directory

The runtime configures a `FilesDir` for tool-generated files (e.g., from `file_create`). This directory defaults to `<WorkDir>/.forge/files/` and is injected into the execution context so tools can write files that other tools can reference by path.
Expand Down
22 changes: 6 additions & 16 deletions docs/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Skills are a progressive disclosure mechanism for defining agent capabilities in

## Overview

Skills bridge the gap between high-level capability descriptions and the tool-calling system. A `SKILL.md` file in your project root defines what the agent can do, and Forge compiles these into JSON artifacts and prompt text for the container.
Skills bridge the gap between high-level capability descriptions and the tool-calling system. Each skill lives in its own subdirectory under `skills/` with a `SKILL.md` file that defines what the agent can do. Forge compiles these into JSON artifacts and prompt text for the container.

## SKILL.md Format

Skills are defined in a Markdown file (default: `SKILL.md`). The file supports optional YAML frontmatter and two body formats.
Skills are defined in Markdown files inside `skills/<skill-name>/SKILL.md`. Each file supports optional YAML frontmatter and two body formats.

```markdown
---
Expand Down Expand Up @@ -499,20 +499,10 @@ The skill compilation pipeline has three stages:

The `SkillsStage` runs as part of the build pipeline:

1. Resolves the skills file path (default: `SKILL.md` in work directory)
2. Skips silently if the file doesn't exist
3. Parses, compiles, and writes artifacts
4. Updates the `AgentSpec` with `skills_spec_version` and `forge_skills_ext_version`
5. Records generated files in the build manifest

## Configuration

In `forge.yaml`:

```yaml
skills:
path: SKILL.md # default, can be customized
```
1. Scans the `skills/` subdirectory for `SKILL.md` files in each subdirectory
2. Parses, compiles, and writes artifacts
3. Updates the `AgentSpec` with `skills_spec_version` and `forge_skills_ext_version`
4. Records generated files in the build manifest

## CLI Workflow

Expand Down
4 changes: 2 additions & 2 deletions docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ When `HOME` is overridden to `workDir`, `kubectl` and `helm` lose access to `~/.

| Env Var | Value | Purpose |
|---------|-------|---------|
| `KUBECONFIG` | `<real-home>/.kube/config` | Restores access to the real kubeconfig |
| `KUBECONFIG` | Explicit `KUBECONFIG` if set, else `<real-home>/.kube/config` | Passes through the active kubeconfig |
| `NO_PROXY` | K8s API server hostname(s) | Bypasses the egress proxy for cluster connections |

`NO_PROXY` is extracted from the kubeconfig's `clusters[].cluster.server` field. Other binaries do not receive these variables.
If `KUBECONFIG` is explicitly set in the environment (e.g., via `docker run -e KUBECONFIG=...` or after [KUBECONFIG materialization](runtime.md#kubeconfig-materialization)), that value is passed through directly. Otherwise, `cli_execute` falls back to the real `~/.kube/config`. `NO_PROXY` is extracted from the kubeconfig's `clusters[].cluster.server` field. Other binaries do not receive these variables.

## File Create

Expand Down
Loading
Loading