# Forge Policy Engine Architecture: How File System Operations Are Restricted

> Explore Forge's policy engine architecture. Learn how its three-layer defense system converts file system operations into PermissionOperations and evaluates them against Policy rules for secure I/O control.

- Repository: [Forge Code/forgecode](https://github.com/antinomyhq/forgecode)
- Tags: architecture
- Published: 2026-04-08

---

**Forge's policy engine architecture employs a three-layer defense system that converts every file system and network action into a `PermissionOperation`, evaluates it against declarative `Policy` rules, and returns `Allow`, `Deny`, or `Confirm` to gate potentially dangerous I/O behind explicit user approval or predefined constraints.**

Forge, the open-source AI coding assistant from antinomyhq/forgecode, implements a declarative policy engine architecture to prevent unauthorized file modifications. Every potentially dangerous operation—whether reading source code, writing to disk, executing shell commands, or fetching remote URLs—is intercepted and evaluated against a configurable set of security policies. This system ensures that no file system access occurs without explicit permission logic defined in the `PolicyConfig` and evaluated by the `PolicyEngine` found in `crates/forge_domain/src/policies/`.

## Three-Layer Policy Engine Architecture

The policy engine architecture operates through three distinct layers: operation description, policy definition, and engine evaluation.

### Layer 1: Operation Description with PermissionOperation

Every dangerous action is first encoded as a `PermissionOperation` in [`crates/forge_domain/src/policies/operation.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/operation.rs). This enum captures **what** the program intends to do, the target path or URL, and a human-readable message.

```rust
use forge_domain::PermissionOperation;

let op = PermissionOperation::Write {
    path: std::path::PathBuf::from("src/main.rs"),
    cwd: std::path::PathBuf::from("/repo"),
    message: "Create/overwrite src/main.rs".into(),
};

```

The four operation variants are **Read**, **Write**, **Execute**, and **Fetch**. Each carries sufficient context—including current working directory and target paths—for the engine to apply glob-based matching rules.

### Layer 2: Policy Definition and Rule Composition

Policies declare **when** an operation is permitted, denied, or requires confirmation. Defined in [`crates/forge_domain/src/policies/policy.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/policy.rs), the `Policy` enum supports simple rules and logical composition.

```rust
use forge_domain::{Policy, Permission, Rule, WriteRule};

let policy = Policy::Simple {
    permission: Permission::Allow,
    rule: Rule::Write(WriteRule {
        write: "*.rs".into(),   // glob pattern
        dir: None,
    }),
};

```

The architecture supports complex logic through **All**, **Any**, and **Not** combinators:

```rust
let combined = Policy::All {
    all: vec![
        policy.clone(),
        Policy::Simple {
            permission: Permission::Deny,
            rule: Rule::Write(WriteRule { write: "**/*.secret".into(), dir: None })
        },
    ],
};

```

Concrete rule structs (`ReadRule`, `WriteRule`, `ExecuteRule`, `Fetch`) live in [`crates/forge_domain/src/policies/rule.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/rule.rs) and implement the glob-matching logic against operation paths, command strings, or hostnames.

### Layer 3: Engine Evaluation and Decision

The `PolicyEngine` in [`crates/forge_domain/src/policies/engine.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/engine.rs) evaluates operations against a `PolicyConfig` (defined in [`crates/forge_domain/src/policies/config.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/config.rs)). The engine traverses the policy set and applies specific resolution logic:

1. Short-circuits on the first **Deny** or **Confirm** result
2. Tracks the **last Allow** encountered when multiple policies permit the operation
3. Returns **Confirm** when the policy set is empty or no rules match

```rust
use forge_domain::{PolicyEngine, PolicyConfig};

let policies = PolicyConfig::new().add_policy(policy);
let engine = PolicyEngine::new(&policies);

match engine.can_perform(&op) {
    Permission::Allow   => println!("✅ operation permitted"),
    Permission::Deny    => println!("❌ operation denied"),
    Permission::Confirm => println!("⚠️ ask user for confirmation"),
}

```

## How File System Restrictions Are Enforced

The policy engine architecture restricts I/O through pattern matching and default-deny semantics, ensuring Forge never performs unchecked operations.

### Read and Write Restrictions

File system read and write operations are gated by glob patterns defined in `ReadRule` and `WriteRule`. If an operation's path does not match any rule, the engine defaults to `Confirm`, forcing a user prompt before proceeding.

### Execute Command Sanitization

Shell commands are converted to glob patterns (e.g., `"git push*"`) and matched against `ExecuteRule` definitions. Only commands matching an explicit **Allow** rule execute automatically; others trigger the confirmation flow.

### Network Fetch Control

URL access is restricted by hostname matching (e.g., `"example.com*"`). The `Fetch` rule type in [`rule.rs`](https://github.com/antinomyhq/forgecode/blob/main/rule.rs) evaluates network requests against these patterns, denying or confirming access to unauthorized domains.

## Service Integration and Persistence

The `ForgePolicyService` in [`crates/forge_services/src/policy.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_services/src/policy.rs) provides the high-level integration layer. It loads a default policy collection from the embedded [`permissions.default.yaml`](https://github.com/antinomyhq/forgecode/blob/main/permissions.default.yaml) resource and persists user-added policies to `$HOME/.forge/policies.yml`.

When the engine returns `Confirm`, the service prompts the user via the `UserInfra` trait. If the user selects **Accept and Remember**, the service automatically appends a new allowing policy to the user's configuration file, updating the `PolicyConfig` for future operations.

```rust
use std::sync::Arc;
use forge_services::ForgePolicyService;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let infra = Arc::new(LocalFileInfra::new()?);
    let policy_service = ForgePolicyService::new(infra.clone());

    let decision = policy_service.check_operation_permission(&op).await?;
    println!("Allowed? {}", decision.allowed);
    Ok(())
}

```

## Practical Implementation Examples

### Direct Engine Usage

```rust
use forge_domain::{
    PermissionOperation, PolicyEngine, PolicyConfig,
    Permission, Policy, Rule, WriteRule,
};

let write_op = PermissionOperation::Write {
    path: std::path::PathBuf::from("src/lib.rs"),
    cwd: std::path::PathBuf::from("/repo"),
    message: "Write src/lib.rs".into(),
};

let config = PolicyConfig::new().add_policy(Policy::Simple {
    permission: Permission::Allow,
    rule: Rule::Write(WriteRule { write: "src/**/*.rs".into(), dir: None }),
});

let engine = PolicyEngine::new(&config);
assert_eq!(engine.can_perform(&write_op), Permission::Allow);

```

### Complex Policy Logic

```rust
use forge_domain::{Policy, Permission, Rule, ReadRule, WriteRule};

let restrictive_policy = Policy::All {
    all: vec![
        Policy::Simple {
            permission: Permission::Allow,
            rule: Rule::Read(ReadRule { read: "src/**/*.rs".into(), dir: None }),
        },
        Policy::Simple {
            permission: Permission::Deny,
            rule: Rule::Write(WriteRule { write: "**/*.toml".into(), dir: None }),
        },
    ],
};

```

## Summary

- Forge's policy engine architecture isolates dangerous operations through a three-layer system: `PermissionOperation` description, `Policy` definition, and `PolicyEngine` evaluation.
- The engine supports logical composition via `All`, `Any`, and `Not` combinators, allowing complex security rules in [`crates/forge_domain/src/policies/policy.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/policy.rs).
- File system restrictions rely on glob pattern matching in [`rule.rs`](https://github.com/antinomyhq/forgecode/blob/main/rule.rs), with default behavior set to `Confirm` when no policies match.
- `ForgePolicyService` manages persistence between embedded defaults ([`permissions.default.yaml`](https://github.com/antinomyhq/forgecode/blob/main/permissions.default.yaml)) and user configurations (`~/.forge/policies.yml`).
- Every I/O operation returns one of three permissions: **Allow**, **Deny**, or **Confirm**, ensuring explicit authorization before any file system modification.

## Frequently Asked Questions

### What are the three possible outcomes when Forge's policy engine evaluates an operation?

According to the source code in [`crates/forge_domain/src/policies/engine.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/engine.rs), the `PolicyEngine` returns an enum with three variants: **Allow** (permit the operation immediately), **Deny** (block the operation), or **Confirm** (suspend execution and prompt the user for explicit approval). This design ensures that potentially dangerous actions never proceed without explicit authorization when predefined rules do not apply.

### How does Forge resolve conflicts when multiple policies apply to a single operation?

The evaluation logic in [`engine.rs`](https://github.com/antinomyhq/forgecode/blob/main/engine.rs) implements specific precedence rules: the engine short-circuits on the first **Deny** or **Confirm** result it encounters. If multiple **Allow** policies match the same operation, the engine collapses them and uses the **last Allow** seen in the evaluation order. This deterministic resolution prevents ambiguous security states and ensures predictable behavior.

### Where does Forge store user-defined security policies?

User-customized policies are persisted to `$HOME/.forge/policies.yml` by the `ForgePolicyService` in [`crates/forge_services/src/policy.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_services/src/policy.rs). The service loads these at runtime alongside the embedded [`permissions.default.yaml`](https://github.com/antinomyhq/forgecode/blob/main/permissions.default.yaml), merging them into a single `PolicyConfig` that the engine evaluates against incoming operations.

### What happens if no policy rules match a requested file system operation?

When the `PolicyConfig` contains no matching rules or is completely empty, the `PolicyEngine::can_perform` method defaults to returning **Confirm**. This fail-safe mechanism, implemented in [`crates/forge_domain/src/policies/engine.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/policies/engine.rs), forces the user interface to prompt for explicit approval rather than allowing unchecked file system access, maintaining the security invariant that all I/O must be explicitly permitted.