Forge is designed with security as a foundational principle, not an afterthought. This document describes the complete security architecture — from network-level egress controls to encrypted secrets, build signing, execution sandboxing, and runtime guardrails.
Forge's security is organized in layers, each addressing a different threat surface:
┌──────────────────────────────────────────────────────────────┐
│ Skill Guardrails │
│ (deny commands/output/prompts/responses per skill) │
├──────────────────────────────────────────────────────────────┤
│ Global Guardrails │
│ (content filtering, PII, jailbreak) │
├──────────────────────────────────────────────────────────────┤
│ Egress Enforcement │
│ (EgressEnforcer + EgressProxy + SafeDialer + NetworkPolicy) │
├──────────────────────────────────────────────────────────────┤
│ Execution Sandboxing │
│ (env isolation, binary allowlists, arg validation, │
│ file:// blocking, shell denylist) │
├──────────────────────────────────────────────────────────────┤
│ Secrets Management │
│ (AES-256-GCM, Argon2id, per-agent isolation) │
├──────────────────────────────────────────────────────────────┤
│ Build Integrity │
│ (Ed25519 signing, SHA-256 checksums) │
├──────────────────────────────────────────────────────────────┤
│ Network Posture │
│ (outbound-only connections, no public listeners) │
└──────────────────────────────────────────────────────────────┘
- Network Posture
- Egress Enforcement
- Execution Sandboxing
- Secrets Management
- Build Integrity
- Guardrails
- Audit Logging
- Container Security
- Related Documentation
Forge agents are designed to never expose inbound listeners to the public internet:
- No public tunnels — Forge does not create ngrok, Cloudflare, or similar tunnels
- No inbound webhooks — Channels use outbound-only connections
- Slack: Socket Mode (outbound WebSocket via
apps.connections.open) - Telegram: Long-polling via
getUpdates; webhook mode binds to127.0.0.1only
- Slack: Socket Mode (outbound WebSocket via
- Local-only HTTP server — The A2A dev server binds to
localhostby default - Rate limiting — Per-IP token bucket rate limiting on the A2A server (read: 60 req/min, write: 10 req/min) with automatic 429 responses and
Retry-Afterheaders - Request size limits — A2A server enforces
MaxHeaderBytes(1 MiB) and request body limits (2 MiB viahttp.MaxBytesReader) to prevent denial-of-service via oversized payloads - CORS restriction — The A2A server restricts
Access-Control-Allow-Originto localhost by default; configurable via--cors-originsflag,FORGE_CORS_ORIGINSenv var, orcors_originsinforge.yaml - Security response headers — All A2A responses include
X-Content-Type-Options: nosniff,Referrer-Policy: no-referrer,X-Frame-Options: DENY, andContent-Security-Policy: default-src 'none' - No hidden listeners — Every network binding is explicit and logged
This means a running Forge agent has zero inbound attack surface by default.
Forge restricts outbound network access at multiple levels:
All egress paths reject non-standard IP formats (octal, hex, packed decimal, leading zeros) that could bypass allowlist checks. IPv6 transition addresses (NAT64, 6to4, Teredo) embedding private IPv4 addresses are also blocked.
The EgressEnforcer is a Go http.RoundTripper backed by a SafeTransport that validates resolved IPs post-DNS. Every outbound HTTP request from in-process tools (http_request, web_search, LLM API calls) is checked against IP validation, domain allowlist, and post-resolution CIDR blocking.
Skill scripts and cli_execute subprocesses bypass Go-level enforcement. A local EgressProxy on 127.0.0.1:<random-port> validates domains and resolved IPs for subprocess HTTP traffic via HTTP_PROXY/HTTPS_PROXY env var injection.
HTTP clients used by http_request and webhook_call tools strip Authorization, Cookie, and Proxy-Authorization headers when a redirect crosses origin boundaries (different scheme, host, or port).
In containerized deployments, generated Kubernetes NetworkPolicy manifests enforce egress at the pod level, restricting traffic to allowed domains on ports 80/443.
| Mode | Behavior |
|---|---|
deny-all |
All non-localhost outbound traffic blocked |
allowlist |
Only explicitly allowed domains (exact + wildcard) |
dev-open |
All traffic allowed (development only) |
Allowed domains are resolved from three sources:
- Explicit domains — Listed in
forge.yamlunderegress.allowed_domains - Tool domains — Automatically inferred from registered tool names (e.g.,
web_search→api.tavily.com) - Capability bundles — Pre-defined domain sets for common services (e.g.,
slack→slack.com,hooks.slack.com,api.slack.com)
Localhost (127.0.0.1, ::1, localhost) is always allowed in all modes.
For full details on egress enforcement, see Egress Security.
Forge agents execute external code through two sandboxed executors, both designed to minimize the attack surface of subprocess execution.
Skill scripts run via SkillCommandExecutor (forge-cli/tools/exec.go):
| Control | Detail |
|---|---|
| Environment isolation | Only PATH, HOME, and explicitly declared env vars are passed through |
| Egress proxy injection | HTTP_PROXY/HTTPS_PROXY env vars route subprocess HTTP through the egress proxy |
| OAuth token resolution | When OPENAI_API_KEY is set to the sentinel __oauth__, the executor resolves OAuth credentials and injects the access token and OPENAI_BASE_URL |
| Model passthrough | The configured LLM model name is injected as REVIEW_MODEL so skill scripts use the correct model |
| Configurable timeout | Per-skill timeout_hint in YAML frontmatter (default: 120s) |
| No shell | Runs bash <script> <json-input>, not through a shell interpreter |
| Scoped env vars | Only env vars declared in the skill's requires.env section are passed |
The cli_execute tool (forge-cli/tools/cli_execute.go) provides 13 security layers:
| # | Layer | Detail |
|---|---|---|
| 1 | Shell denylist | Shell interpreters (bash, sh, zsh, etc.) filtered at construction and blocked at execution |
| 2 | Binary allowlist | Only pre-approved binaries can execute |
| 3 | Binary resolution | Binaries are resolved to absolute paths via exec.LookPath at startup |
| 4 | Argument validation | Rejects arguments containing $(, backticks, newlines, or file:// URLs |
| 5 | File protocol blocking | Blocks file:// URLs (case-insensitive) to prevent filesystem traversal |
| 6 | Path confinement | Path arguments inside $HOME but outside workDir are blocked |
| 7 | Timeout | Configurable per-command timeout (default: 120s) |
| 8 | No shell | Uses exec.CommandContext directly — no shell expansion |
| 9 | Working directory | cmd.Dir set to workDir for relative path resolution |
| 10 | Environment isolation | Only PATH, HOME, LANG, explicit passthrough vars, proxy vars, and GH_CONFIG_DIR (auto-set only for gh binary when HOME is overridden) |
| 11 | Output limits | Configurable max output size (default: 1MB) to prevent memory exhaustion |
| 12 | Skill guardrails | Skill-declared deny_commands and deny_output patterns via hooks |
| 13 | Custom tool entrypoint validation | Custom tool entrypoints are validated against path traversal, symlink escape, absolute paths, and non-regular files |
tools:
- name: cli_execute
config:
allowed_binaries: ["git", "curl", "jq", "python3"]
env_passthrough: ["GITHUB_TOKEN"]
timeout: 120
max_output_bytes: 1048576Forge provides AES-256-GCM encrypted secret storage with Argon2id key derivation, per-agent isolation, and a three-tier resolution hierarchy (agent-local -> global -> environment). Secrets are managed via forge secret set|get|list|delete.
At startup, the runtime detects when the same secret value is shared across different purpose categories (e.g., OPENAI_API_KEY and TELEGRAM_BOT_TOKEN having the same value). This prevents credential reuse mistakes that could escalate the impact of a single token compromise. Categories: llm, search, telegram, slack.
For full details, see Secrets Management.
Forge supports Ed25519 signing and SHA-256 checksumming of build artifacts for supply chain integrity. At runtime, forge run can verify artifacts against trusted keys before execution.
For full details, see Build Signing & Verification.
The guardrail engine checks inbound and outbound messages against policy rules including content filtering, PII detection, and jailbreak protection. Guardrails run in enforce (blocking) or warn (logging) mode.
Skills can declare domain-specific guardrails in their SKILL.md frontmatter. These guardrails operate at four hook points — blocking unauthorized commands (deny_commands), redacting sensitive output (deny_output), intercepting capability enumeration probes (deny_prompts), and replacing binary-enumerating LLM responses (deny_responses). Skill guardrails fire at runtime without requiring forge build.
For full details, see Content Guardrails.
All runtime security events are emitted as structured NDJSON to stderr with correlation IDs for end-to-end tracing.
| Event | Description |
|---|---|
session_start |
New task session begins |
session_end |
Task session completes (with final state) |
tool_exec |
Tool execution start/end (with tool name) |
egress_allowed |
Outbound request allowed (with domain, mode) |
egress_blocked |
Outbound request blocked (with domain, mode) |
llm_call |
LLM API call completed (with token count) |
guardrail_check |
Guardrail evaluation result |
{"ts":"2026-02-28T10:00:00Z","event":"session_start","correlation_id":"a1b2c3d4","task_id":"task-1"}
{"ts":"2026-02-28T10:00:01Z","event":"tool_exec","correlation_id":"a1b2c3d4","fields":{"tool":"tavily_research","phase":"start"}}
{"ts":"2026-02-28T10:00:01Z","event":"egress_allowed","correlation_id":"a1b2c3d4","fields":{"domain":"api.tavily.com","mode":"allowlist","source":"proxy"}}
{"ts":"2026-02-28T10:00:05Z","event":"tool_exec","correlation_id":"a1b2c3d4","fields":{"tool":"tavily_research","phase":"end"}}
{"ts":"2026-02-28T10:00:06Z","event":"session_end","correlation_id":"a1b2c3d4","fields":{"state":"completed"}}The source field distinguishes in-process enforcer events from subprocess proxy events.
Every forge build generates container-ready security artifacts:
| Artifact | Purpose |
|---|---|
egress_allowlist.json |
Machine-readable domain allowlist |
network-policy.yaml |
Kubernetes NetworkPolicy restricting pod egress |
Dockerfile |
Container image with minimal attack surface |
checksums.json |
SHA-256 checksums + Ed25519 signature |
When Forge detects it's running inside a container (via KUBERNETES_SERVICE_HOST or /.dockerenv):
- The local
EgressProxyis not started —NetworkPolicyhandles egress enforcement at the infrastructure level - All other security controls (guardrails, execution sandboxing, audit logging) remain active
- Secrets must use the
envprovider (encrypted files can't be decrypted without a passphrase)
forge package --prodProduction builds enforce:
- No
dev-openegress mode - No dev-only tools (
local_shell,local_file_browser) - Secret provider chain must include
env(not justencrypted-file) .dockerignoremust exist if a Dockerfile is generated
| Document | Description |
|---|---|
| Egress Security | Deep dive into egress enforcement: IP validation, SafeDialer, profiles, modes, domain matching, proxy architecture, NetworkPolicy |
| Secrets Management | Encrypted storage, per-agent secrets, passphrase handling |
| Build Signing & Verification | Key management, build signing, runtime verification |
| Content Guardrails | PII detection, jailbreak protection, custom rules |
| Architecture | System design, module layout, and data flows |
| Tools | Tool system including cli_execute security layers |
| Skills | Skill definitions and runtime execution |
| Commands | CLI reference including security-related flags |
← Channels | Back to README | Egress Security →