Policy Enforcement Guide
Overview
This guide explains how APort policy enforcement works, what users need to set in their passports, and how the GitHub Actions showcase the full scope of policy features.
How Policy Enforcement Works
1. Policy Definition (policies/code.repository.merge.v1/policy.json)
{
"enforcement": {
"allowed_repos_enforced": true,
"allowed_base_branches_enforced": true,
"path_allowlist_enforced": true,
"size_limits_enforced": true,
"review_requirements_enforced": false,
"github_actor_enforced": true,
"github_app_enforced": true
}
}
2. Enforcement Logic (functions/api/verify/policy/[pack_id].ts)
The enforcement is OPTIONAL and GRACEFUL:
- If a limit is NOT set in passport ā NO CHECK PERFORMED ā
- If a limit IS set in passport ā CHECK IS PERFORMED ā
- If enforcement rule is
falseā CHECK IS SKIPPED ā
Review counts and required labels are handled by GitHub branch protection or
repository rules today. APort hosted verification enforces passport-scoped
repository, branch, path, size, actor/app/workflow allowlist, and evidence
checks, then records a signed decision.
3. User Passport Requirements
Users do NOT need to set all options. The system is designed to be flexible:
Minimal Passport (works fine):
{
"agent_id": "agt_123",
"limits": {
"max_prs_per_day": 10,
"max_merges_per_day": 5
}
}
Result: Only PR count limits are enforced. Repository/branch/path restrictions are ignored.
Full Passport (all restrictions):
{
"agent_id": "agt_123",
"limits": {
"max_prs_per_day": 10,
"max_merges_per_day": 5,
"max_pr_size_kb": 1000,
"allowed_repos": ["owner/repo1", "owner/repo2"],
"allowed_base_branches": ["main", "develop"],
"allowed_paths": ["src/", "docs/"]
},
"integrations": {
"github": {
"allowed_actors": ["my-bot[bot]", "acme-ci"],
"allowed_apps": ["my-github-app"]
}
}
}
Result: All APort-supported restrictions are enforced. Review counts and
required labels still belong in GitHub branch protection or repository rules.
TypedLimits Interface
The TypedLimits interface now includes all policy enforcement fields:
export interface TypedLimits {
// Numeric limits
max_prs_per_day?: number;
max_merges_per_day?: number;
max_pr_size_kb?: number;
// Array limits (for allowlists)
allowed_repos?: string[];
allowed_base_branches?: string[];
allowed_paths?: string[];
// Other existing limits...
refund_amount_max_per_tx?: number;
max_export_rows?: number;
allow_pii?: boolean;
// ... etc
}
GitHub Action Context Mapping
The GitHub Action now maps comprehensive context to enable full policy enforcement:
| GitHub Context | APort Context | Policy Enforcement |
|---|---|---|
github.repository
|
repo
|
Repository allowlist |
github.event.pull_request.base.ref
|
base_branch
|
Base branch allowlist |
github.actor
|
github_actor
|
GitHub actor allowlist |
github.app
|
github_app
|
GitHub app allowlist |
| Computed diff stats |
files_changed, lines_added
|
Change statistics |
| PR size calculation |
pr_size_kb
|
Size limits |
| Changed file paths |
file_paths
|
Path allowlist |
| PR labels |
labels
|
Evidence context only |
| Review count |
reviews
|
Evidence context only |
Policy Enforcement Examples
Example 1: Repository Allowlist
// Passport
{
"limits": {
"allowed_repos": ["owner/repo1", "owner/repo2"]
}
}
// Context
{
"repo": "owner/repo3" // ā Not in allowlist
}
// Result: BLOCKED - "Agent does not have access to repository owner/repo3"
Example 2: Path Allowlist
// Passport
{
"limits": {
"allowed_paths": ["src/", "docs/"]
}
}
// Context
{
"file_paths": ["src/main.js", "config/secrets.json"] // ā secrets.json not allowed
}
// Result: BLOCKED - "Agent not authorized to access paths: config/secrets.json"
Example 3: Size Limits
// Passport
{
"limits": {
"max_pr_size_kb": 1000
}
}
// Context
{
"pr_size_kb": 1500 // ā Exceeds limit
}
// Result: BLOCKED - "PR size 1500KB exceeds limit of 1000KB"
Example 4: No Restrictions (Minimal Passport)
// Passport
{
"limits": {
"max_prs_per_day": 10
// No other limits set
}
}
// Context
{
"repo": "any/repo",
"base_branch": "any-branch",
"file_paths": ["any/file.js"]
}
// Result: ALLOWED - Only PR count is enforced, other restrictions ignored
Passport Path Overrides for Command Validation
The system.command.execute.v1 policy includes built-in security patterns that block access to sensitive paths (e.g. /etc/, /proc/, hidden files). Passport owners can override path-sensitivity heuristics using allowed_paths:
{
"limits": {
"allowed_paths": ["/root/", "/home/agent/"],
"allowed_commands": ["*"]
}
}
When a command references a path in allowed_paths, overridable rules are skipped (e.g. "Access to sensitive system directories"). Catastrophic protections ā fork bombs, rm -rf /, reverse shells, nc/netcat, find -exec rm ā can never be overridden by passport config.
Overridable rules include: sensitive system directories, sensitive hidden files, credential files, secrets files, network exfiltration heuristics, permission manipulation, password manager databases, log files, and clipboard access.
Key Benefits
- Flexible: Users can set as many or as few restrictions as needed
- Type Safe: All limits are properly typed in TypeScript
- Comprehensive: GitHub Action maps all relevant context
- Graceful: Missing limits don't cause errors, they're simply ignored
- Extensible: Easy to add new enforcement rules
- Overridable: Path-sensitivity heuristics can be customized per passport without weakening catastrophic protections
Testing
The system has been tested with real API calls and works correctly:
- ā Policy verification endpoint responds properly
- ā Context mapping works for all enforcement rules
- ā Type safety is maintained
- ā Graceful handling of missing limits
- ā Comprehensive error messages
Conclusion
The policy enforcement system is now complete and showcases the full scope of the code.repository.merge.v1 policy. Users can create minimal passports for basic functionality or comprehensive passports for full security, and the system will enforce only the restrictions they've configured.