Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"ajv": "^8.18.0",
"chalk": "^4.1.2",
"commander": "^12.0.0",
"execa": "^5.1.1",
Expand All @@ -64,7 +65,6 @@
"@types/node": "^25.6.0",
"@typescript-eslint/eslint-plugin": "^8.58.2",
"@typescript-eslint/parser": "^8.58.2",
"ajv": "^8.18.0",
"babel-jest": "^30.2.0",
"esbuild": "^0.25.0",
"eslint": "^10.2.1",
Expand Down
5 changes: 5 additions & 0 deletions scripts/generate-schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -402,4 +402,9 @@ if (printOnly) {
const outPath = join(docsDir, 'awf-config.schema.json');
writeFileSync(outPath, output);
console.log(`Schema written to ${outPath}`);

// Also write to src/ for runtime bundling (used by schema-validator.ts)
const srcPath = join(projectRoot, 'src', 'awf-config-schema.json');
writeFileSync(srcPath, output);
console.log(`Schema written to ${srcPath}`);
}
333 changes: 333 additions & 0 deletions src/awf-config-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/github/gh-aw-firewall/main/docs/awf-config.schema.json",
"title": "AWF Configuration",
"description": "JSON/YAML configuration for awf CLI. CLI flags override config file values. See https://github.com/github/gh-aw-firewall for documentation.",
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": {
"type": "string",
"description": "JSON Schema URL for IDE validation and autocomplete."
},
"network": {
"type": "object",
"description": "Network egress configuration.",
"additionalProperties": false,
"properties": {
"allowDomains": {
"type": "array",
"items": {
"type": "string"
},
"description": "Domains that the agent is allowed to reach. Both the bare domain and all subdomains are permitted (e.g. \"github.com\" also allows \"api.github.com\")."
},
"blockDomains": {
"type": "array",
"items": {
"type": "string"
},
"description": "Domains that are explicitly blocked, overriding allowDomains."
},
"dnsServers": {
"type": "array",
"items": {
"type": "string"
},
"description": "DNS servers to use inside the container. Defaults to Google DNS (8.8.8.8, 8.8.4.4). Accepts IPv4 and IPv6 addresses."
},
"upstreamProxy": {
"type": "string",
"description": "Upstream HTTP proxy URL (e.g. \"http://proxy.corp.example.com:8080\"). When set, the AWF Squid proxy forwards traffic through this proxy."
}
}
},
"apiProxy": {
"type": "object",
"description": "API proxy sidecar configuration. The sidecar injects real API credentials so the agent never has direct access to them.",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the API proxy sidecar container."
},
"enableOpenCode": {
"type": "boolean",
"description": "Enable the OpenCode API proxy endpoint (port 10004)."
},
"anthropicAutoCache": {
"type": "boolean",
"description": "Automatically apply Anthropic prompt-cache optimizations on /v1/messages requests."
},
"anthropicCacheTailTtl": {
"type": "string",
"enum": [
"5m",
"1h"
],
"description": "TTL for Anthropic cache tail optimization. Only applies when anthropicAutoCache is enabled. Allowed values: \"5m\" or \"1h\"."
},
"targets": {
"type": "object",
"description": "Override upstream API endpoints for each provider.",
"additionalProperties": false,
"properties": {
"openai": {
"$ref": "#/$defs/providerTarget",
"description": "OpenAI API target override."
},
"anthropic": {
"$ref": "#/$defs/providerTarget",
"description": "Anthropic API target override."
},
"copilot": {
"$ref": "#/$defs/providerHostOnlyTarget",
"description": "GitHub Copilot API target override (basePath not supported)."
},
"gemini": {
"$ref": "#/$defs/providerTarget",
"description": "Google Gemini API target override."
}
}
},
"models": {
"type": "object",
"description": "Model alias mapping. Keys are canonical model names; values are arrays of alternative names or patterns that should be rewritten to the canonical name.",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"security": {
"type": "object",
"description": "Security and isolation configuration.",
"additionalProperties": false,
"properties": {
"sslBump": {
"type": "boolean",
"description": "Enable SSL bumping (TLS interception) in the Squid proxy. Requires a custom CA certificate."
},
"enableDlp": {
"type": "boolean",
"description": "Enable Data Loss Prevention (DLP) inspection of outbound traffic."
},
"enableHostAccess": {
"type": "boolean",
"description": "Mount the host filesystem (read-only for system paths, read-write for the workspace). Enabled by default; set to false to run without host filesystem access."
},
"allowHostPorts": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "Host TCP ports the agent may connect to (e.g. local dev services). Accepts a single port string or an array of port strings."
},
"allowHostServicePorts": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "Named service ports on the host that the agent may connect to. Accepts a single port string or an array of port strings."
},
"difcProxy": {
"type": "object",
"description": "DIFC (Data-in-Flight Control) proxy configuration.",
"additionalProperties": false,
"properties": {
"host": {
"type": "string",
"description": "DIFC proxy host."
},
"caCert": {
"type": "string",
"description": "Path to the CA certificate for DIFC proxy TLS verification."
}
}
}
}
},
"container": {
"type": "object",
"description": "Container and Docker configuration.",
"additionalProperties": false,
"properties": {
"memoryLimit": {
"type": "string",
"description": "Docker memory limit for the agent container (e.g. \"4g\", \"512m\"). Uses Docker memory limit syntax."
},
"agentTimeout": {
"type": "integer",
"minimum": 1,
"description": "Maximum time (in minutes) the agent command is allowed to run."
},
"enableDind": {
"type": "boolean",
"description": "Enable Docker-in-Docker support inside the agent container."
},
"workDir": {
"type": "string",
"description": "Host path used as the AWF working directory for generated configs and logs. Defaults to a temporary directory."
},
"containerWorkDir": {
"type": "string",
"description": "Working directory inside the agent container."
},
"imageRegistry": {
"type": "string",
"description": "Container image registry to pull from. Defaults to \"ghcr.io/github/gh-aw-firewall\"."
},
"imageTag": {
"type": "string",
"description": "Container image tag to use. Defaults to \"latest\"."
},
"skipPull": {
"type": "boolean",
"description": "Skip pulling container images (use locally cached images)."
},
"buildLocal": {
"type": "boolean",
"description": "Build container images from source instead of pulling from the registry."
},
"agentImage": {
"type": "string",
"description": "Override the agent container image (e.g. for a GitHub Actions parity image)."
},
"tty": {
"type": "boolean",
"description": "Allocate a pseudo-TTY for the agent container."
},
"dockerHost": {
"type": "string",
"description": "Docker daemon socket or host to connect to (e.g. \"unix:///var/run/docker.sock\")."
}
}
},
"environment": {
"type": "object",
"description": "Environment variable propagation into the agent container.",
"additionalProperties": false,
"properties": {
"envFile": {
"type": "string",
"description": "Path to a .env file whose variables are injected into the agent container."
},
"envAll": {
"type": "boolean",
"description": "Forward all host environment variables into the agent container. Use with caution — may expose secrets."
},
"excludeEnv": {
"type": "array",
"items": {
"type": "string"
},
"description": "Environment variable names to exclude when envAll is true."
}
}
},
"logging": {
"type": "object",
"description": "Logging and diagnostics configuration.",
"additionalProperties": false,
"properties": {
"logLevel": {
"type": "string",
"enum": [
"debug",
"info",
"warn",
"error"
],
"description": "Log verbosity level. Defaults to \"info\"."
},
"diagnosticLogs": {
"type": "boolean",
"description": "Enable diagnostic logging (Squid access logs, iptables logs). Logs are written to the work directory."
},
"auditDir": {
"type": "string",
"description": "Directory path for audit logs."
},
"proxyLogsDir": {
"type": "string",
"description": "Directory path for Squid proxy access logs."
},
"sessionStateDir": {
"type": "string",
"description": "Directory path for agent session state (e.g. conversation history). Set to \"/tmp/gh-aw/sandbox/agent/session-state\" for Copilot agent runs."
}
}
},
"rateLimiting": {
"type": "object",
"description": "Egress rate limiting configuration.",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable egress rate limiting."
},
"requestsPerMinute": {
"type": "integer",
"minimum": 1,
"description": "Maximum number of HTTP requests per minute."
},
"requestsPerHour": {
"type": "integer",
"minimum": 1,
"description": "Maximum number of HTTP requests per hour."
},
"bytesPerMinute": {
"type": "integer",
"minimum": 1,
"description": "Maximum number of bytes transferred per minute."
}
}
}
},
"$defs": {
"providerTarget": {
"type": "object",
"description": "API provider target override.",
"additionalProperties": false,
"properties": {
"host": {
"type": "string",
"description": "Override the provider API host."
},
"basePath": {
"type": "string",
"description": "Override the provider API base path."
}
}
},
"providerHostOnlyTarget": {
"type": "object",
"description": "API provider target override (host only; basePath not supported).",
"additionalProperties": false,
"properties": {
"host": {
"type": "string",
"description": "Override the provider API host."
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/config-file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe('config-file', () => {

it('rejects invalid apiProxy.anthropicCacheTailTtl', () => {
const errors = validateAwfFileConfig({ apiProxy: { anthropicCacheTailTtl: '10m' } });
expect(errors).toContain('config.apiProxy.anthropicCacheTailTtl must be "5m" or "1h"');
expect(errors).toContain('config.apiProxy.anthropicCacheTailTtl must be one of: 5m, 1h');
});

it('rejects non-object apiProxy.targets', () => {
Expand Down
Loading
Loading