Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
abd3a3d
Add unified enclave MCP compiler support
lpcox Aug 6, 2026
ac4ef20
Merge branch 'main' into lpcox-compile-enclave-mcp
pelikhan Aug 6, 2026
a4cd10d
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
2d573f6
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
1b3bb88
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
d734781
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
5f5e2fd
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
b310c31
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
e736696
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
a89db00
Merge remote-tracking branch 'origin/main' into lpcox-compile-enclave…
Copilot Aug 7, 2026
32865fe
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
9550613
Merge main into lpcox-compile-enclave-mcp, resolve awf_helpers.go con…
Copilot Aug 7, 2026
b55c1f9
Merge main into lpcox-compile-enclave-mcp, resolve awf_env.go conflicts
Copilot Aug 7, 2026
d9d5c26
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
0dbd761
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 7, 2026
c9612d3
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 8, 2026
bf3b439
Merge branch 'main' into lpcox-compile-enclave-mcp
github-actions[bot] Aug 8, 2026
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
12 changes: 12 additions & 0 deletions docs/public/schemas/mcp-gateway-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@
},
"default": ["*"]
},
"connectTimeout": {
"type": "integer",
"description": "Per-transport timeout in seconds while connecting to an HTTP MCP upstream.",
"minimum": 1,
"default": 30
},
"toolTimeout": {
"type": "integer",
"description": "Per-server timeout in seconds for a tool invocation.",
"minimum": 1,
"default": 60
},
"env": {
"type": "object",
"description": "Environment variables to pass through for variable resolution. Values may contain variable expressions using '${VARIABLE_NAME}' syntax, which will be resolved from the process environment.",
Expand Down
33 changes: 33 additions & 0 deletions docs/src/content/docs/reference/enclaves.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: Private repository enclaves
description: Configure unified AWF script and agent enclaves through the trusted MCP gateway.
---

The top-level `enclaves` field enables finite-disclosure access to approved private repositories. The compiler registers only the enabled `enclave_run_script` and `enclave_run_agent` tools on the `awf-enclave` MCP route.

Enclaves require AWF network isolation. Configure `sandbox.agent.sudo: false` (or the `docker-sbx` runtime) so the compiler launches mcpg in bridge mode and AWF can attach it to the isolated topology.

```yaml
enclaves:
enabled: true
private-repos:
- repo: octo-org/private-service
sensitivity: confidential
executors:
script:
enabled: true
timeout: 45
agent:
enabled: true
model: gpt-5
timeout: 180

sandbox:
agent:
id: awf
sudo: false
```

The generated gateway upstream uses a fresh masked capability for each workflow run. That capability is passed only to mcpg and AWF and is excluded from the primary agent environment. The gateway allows 120 seconds for the AWF-owned HTTP upstream to become available and sets its tool timeout to the longest enabled executor timeout plus 30 seconds.

This compiler contract depends on the unified enclave implementation from `github/gh-aw-firewall#6992`. Until that change is available in an AWF release, pinning an older AWF version will not provide the enclave server.
115 changes: 115 additions & 0 deletions pkg/parser/schemas/main_workflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2791,6 +2791,121 @@
}
]
},
"enclaves": {
"type": "object",
"description": "Unified AWF-owned private-repository script and agent enclaves. Enabled executors are exposed only through the compiler-launched MCP gateway.",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"default": false
},
"private-repos": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["repo", "sensitivity"],
"properties": {
"repo": {
"type": "string",
"maxLength": 140,
"pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,38}/[A-Za-z0-9._-]{1,100}$"
},
"sensitivity": {
"type": "string",
"enum": ["public", "internal", "confidential", "sealed"]
}
}
}
},
"executors": {
"type": "object",
"additionalProperties": false,
"properties": {
"script": {
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": { "type": "boolean", "default": false },
"runtime": { "type": "string", "enum": ["docker", "gvisor", "sbx"], "default": "docker" },
"image": { "type": "string", "minLength": 1, "maxLength": 500 },
"network": { "const": "none", "default": "none" },
"interpreter": { "const": "python3", "default": "python3" },
"timeout": { "type": "integer", "minimum": 1, "maximum": 540, "default": 30 },
"memory-limit": { "type": "string", "pattern": "^[1-9][0-9]*[bkmgBKMG]$", "default": "512m" },
"cpu-limit": { "type": "string", "pattern": "^(?:[0-9]{1,2})(?:\\.[0-9]{1,3})?$", "default": "1" },
"pids-limit": { "type": "integer", "minimum": 1, "maximum": 4096, "default": 128 },
"tmpfs-limit": { "type": "string", "pattern": "^[1-9][0-9]*[bkmgBKMG]$", "default": "64m" },
"max-output-bytes": { "type": "integer", "minimum": 1, "maximum": 8192, "default": 8192 },
"max-script-bytes": { "type": "integer", "minimum": 1, "maximum": 65536, "default": 65536 },
"max-invocations": { "type": "integer", "minimum": 1, "maximum": 10000, "default": 32 }
}
},
"agent": {
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": { "type": "boolean", "default": false },
"runtime": { "type": "string", "enum": ["docker", "gvisor", "sbx"], "default": "docker" },
"image": { "type": "string", "minLength": 1, "maxLength": 500 },
"network": { "const": "api-proxy-only", "default": "api-proxy-only" },
"engine": { "type": "string", "enum": ["copilot", "claude", "codex", "gemini"], "default": "copilot" },
"profile": { "type": "string", "enum": ["openai", "anthropic"], "default": "openai" },
"model": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,199}$" },
"timeout": { "type": "integer", "minimum": 1, "maximum": 540, "default": 120 },
"memory-limit": { "type": "string", "pattern": "^[1-9][0-9]*[bkmgBKMG]$", "default": "512m" },
"cpu-limit": { "type": "string", "pattern": "^(?:[0-9]{1,2})(?:\\.[0-9]{1,3})?$", "default": "1" },
"pids-limit": { "type": "integer", "minimum": 1, "maximum": 4096, "default": 128 },
"tmpfs-limit": { "type": "string", "pattern": "^[1-9][0-9]*[bkmgBKMG]$", "default": "64m" },
"max-output-bytes": { "type": "integer", "minimum": 1, "maximum": 8192, "default": 8192 },
"max-task-bytes": { "type": "integer", "minimum": 1, "maximum": 65536, "default": 4096 },
"max-invocations": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 8 },
"max-model-requests": { "type": "integer", "minimum": 1, "maximum": 64, "default": 8 },
"max-model-tokens": { "type": "integer", "minimum": 1, "maximum": 32768, "default": 1024 }
},
"if": {
"properties": { "enabled": { "const": true } },
"required": ["enabled"]
},
"then": { "required": ["model"] }
}
}
}
},
"if": {
"properties": { "enabled": { "const": true } },
"required": ["enabled"]
},
"then": {
"required": ["private-repos", "executors"],
"properties": {
"executors": {
"anyOf": [
{
"required": ["script"],
"properties": {
"script": {
"required": ["enabled"],
"properties": { "enabled": { "const": true } }
}
}
},
{
"required": ["agent"],
"properties": {
"agent": {
"required": ["enabled"],
"properties": { "enabled": { "const": true } }
}
}
}
]
}
}
}
},
"runner": {
"type": "object",
"description": "Runner topology configuration. Tells gh-aw and AWF what kind of runner environment the workflow targets, so they can activate topology-specific behaviors automatically (split-filesystem handling, network isolation, sysroot images, tool cache redirection). The runner.topology key is the single stable contract between gh-aw and AWF for runner environment detection.",
Expand Down
6 changes: 6 additions & 0 deletions pkg/workflow/awf_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ type AWFConfigFile struct {
// cross-repository private data access. Omitted when not configured.
BoundedQueries *AWFBoundedQueriesConfig `json:"boundedQueries,omitempty"`

// Enclaves configures the unified AWF-owned script and agent enclave subsystem.
Enclaves map[string]any `json:"enclaves,omitempty"`

// Container contains container execution configuration.
Container *AWFContainerConfig `json:"container,omitempty"`

Expand Down Expand Up @@ -471,6 +474,9 @@ func BuildAWFConfigJSON(config AWFCommandConfig) (string, error) {
awfConfig := AWFConfigFile{
Schema: buildAWFConfigSchemaURL(firewallConfig),
}
if config.WorkflowData != nil {
awfConfig.Enclaves = buildAWFEnclavesConfig(config.WorkflowData.Enclaves)
}

// ── Runner section ──────────────────────────────────────────────────────
if topology := getRunnerTopology(config.WorkflowData); topology != "" {
Expand Down
7 changes: 7 additions & 0 deletions pkg/workflow/awf_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ func ComputeAWFExcludeEnvVarNames(workflowData *WorkflowData, coreSecretVarNames
// The runner-owned gateway forwards them only for HTTP MCP github-oidc authentication.
addUnique("ACTIONS_ID_TOKEN_REQUEST_URL")
addUnique("ACTIONS_ID_TOKEN_REQUEST_TOKEN")
if enclavesEnabled(workflowData) {
addUnique(enclaveMCPCapabilityEnv)
addUnique(enclaveMCPGatewayContainerEnv)
addUnique(enclaveMCPGatewayEndpointEnv)
addUnique(enclaveMCPGatewayIdentityEnv)
addUnique(enclaveMCPReadinessTimeoutEnv)
}

// Explicitly excluded env vars from the frontmatter excluded-env field.
// These are always excluded regardless of their value content.
Expand Down
2 changes: 2 additions & 0 deletions pkg/workflow/codex_mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an
if hasMCPScripts {
renderer.RenderMCPScriptsMCP(&mcpConfigContent, workflowData.MCPScripts, workflowData)
}
case enclaveMCPServerName:
writeEnclaveMCPTOML(&mcpConfigContent, workflowData)
default:
// Handle custom MCP tools using shared helper (with adapter for isLast parameter)
HandleCustomMCPToolInSwitch(&mcpConfigContent, toolName, expandedTools, false, func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/workflow/compiler_validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ func (c *Compiler) validateCoreToolConfiguration(workflowData *WorkflowData, mar
{logMessage: "Validating OTLP workload identity configuration", validateFn: func() error { return validateOTLPWorkloadIdentity(workflowData) }},
{logMessage: "Validating default AI credits pricing values", validateFn: func() error { return validateDefaultAiCreditsPricing(workflowData) }},
{logMessage: "Validating tools.github.bounded-queries configuration", validateFn: func() error { return validateBoundedQueriesConfig(workflowData) }},
{logMessage: "Validating enclaves configuration", validateFn: func() error { return validateEnclavesConfig(workflowData) }},
}
// This validation is intentionally outside the table below because strict mode
// turns the same validation result into either an error or a warning.
Expand Down
Loading
Loading