> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openpawz.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> OpenPawz is designed with defense-in-depth: 20+ security layers protect against prompt injection, data exfiltration, SSRF, MITM attacks, memory forensics, agent fixation, inter-agent manipulation, and unauthorized actions.

# Security

OpenPawz is designed with defense-in-depth: **20+ security layers** protect against prompt injection, data exfiltration, SSRF, MITM attacks, memory forensics, agent fixation, inter-agent manipulation, and unauthorized actions.

## Zero Attack Surface by Default

OpenPawz exposes **zero network ports** in its default configuration. There is no HTTP server, no WebSocket endpoint, and no listening socket for an attacker to reach — the only communication channel is Tauri's in-process IPC between the WebView and the Rust engine.

### Network listeners

Four optional listeners exist. All are **disabled by default** and bind to **localhost only** (`127.0.0.1`):

| Listener        | Default bind     | Default state | Authentication                                                     | Purpose                          |
| --------------- | ---------------- | ------------- | ------------------------------------------------------------------ | -------------------------------- |
| Webhook server  | `127.0.0.1:3940` | Disabled      | Bearer token (auto-generated UUID) + IP rate limiting (60 req/min) | External trigger for agent runs  |
| WebChat         | `127.0.0.1:3939` | Disabled      | Access token + session cookie + DM policy                          | Browser-based chat with an agent |
| WhatsApp bridge | `127.0.0.1:8086` | Disabled      | Internal only (local Evolution API)                                | WhatsApp message relay           |
| n8n engine      | `127.0.0.1:5678` | Disabled      | n8n's built-in auth                                                | Embedded workflow engine         |

Because everything binds to `127.0.0.1`, these services are **unreachable from the network** even when enabled. Binding to `0.0.0.0` (LAN/WAN exposure) is a manual opt-in that triggers a security warning in the logs and recommends TLS via Tailscale Funnel.

:::warning
Changing `bind_address` to `0.0.0.0` on any listener exposes it to your local network. Only do this behind a firewall or VPN (e.g. Tailscale), and ensure the authentication token is strong.
:::

### Cryptographic key storage

No cryptographic key is ever stored on the filesystem. All keys live in the **OS keychain** (macOS Keychain / Windows Credential Manager / Linux Secret Service) inside a **unified key vault** — a single keychain entry (`openpawz` / `key-vault`) storing a JSON blob of all purpose keys. This reduces OS keychain prompts from 7+ to **1** on launch or binary change.

| Purpose key       | Vault constant           | Function                                        |
| ----------------- | ------------------------ | ----------------------------------------------- |
| `db-encryption`   | `PURPOSE_DB_ENCRYPTION`  | AES-256-GCM database field encryption           |
| `skill-vault`     | `PURPOSE_SKILL_VAULT`    | AES-256-GCM skill credential encryption         |
| `memory-vault`    | `PURPOSE_MEMORY_VAULT`   | Master key for HKDF per-agent memory encryption |
| `lock-screen`     | `PURPOSE_LOCK_SCREEN`    | SHA-256 hashed passphrase                       |
| `n8n-encryption`  | `PURPOSE_N8N_ENCRYPTION` | n8n integration engine encryption key           |
| `audit-chain`     | `PURPOSE_AUDIT_CHAIN`    | HMAC-SHA256 audit log signing key               |
| `nostr-key`       | `PURPOSE_NOSTR_KEY`      | Nostr bridge private key                        |
| `oauth:{service}` | (dynamic)                | Per-service encrypted OAuth tokens              |

All in-memory key material is wrapped in `Zeroizing<String>` (via the `zeroize` crate) so secrets are securely overwritten with zeroes when dropped or replaced — preventing key material from lingering in freed heap memory.

From the memory vault key, three independent key families are derived via **HKDF-SHA256 domain separation**:

| Domain             | HKDF Salt                 | Purpose                                       |
| ------------------ | ------------------------- | --------------------------------------------- |
| Agent encryption   | `engram-agent-key-v1`     | Per-agent AES-256-GCM memory encryption       |
| Snapshot HMAC      | `engram-snapshot-hmac-v1` | Tamper detection for working memory snapshots |
| Capability signing | `engram-platform-cap-v1`  | HMAC-SHA256 signing of capability tokens      |

There is no `device.json`, no key file, and no config file containing secrets. If the OS keychain is unavailable, the app **refuses to store credentials** rather than falling back to plaintext.

### Soul files (agent behavior)

Agent personality and behavioral rules (`SOUL.md`, `IDENTITY.md`, `USER.md`) are stored in the **encrypted SQLite database** (`agent_files` table) — not as files on the filesystem. They are read and written exclusively through Tauri IPC commands, which are local WebView-to-Rust calls with no network involvement.

An external attacker would need:

1. Physical access to the machine, AND
2. The OS keychain credential to decrypt the database

to tamper with soul files. Remote modification is impossible because no network port serves agent file operations.

### Content Security Policy

The Tauri WebView enforces a strict CSP that restricts all connections to `127.0.0.1`:

```
default-src 'self'; script-src 'self'; connect-src 'self' ws://127.0.0.1:1420 ...;
object-src 'none'; frame-ancestors 'none'; form-action 'self'
```

This prevents XSS-based exfiltration, iframe embedding, and cross-origin form submission.

***

## Human-in-the-Loop (HIL)

Every tool is classified by risk level. High-risk tools require explicit human approval before execution.

### Auto-approved tools (no approval needed)

`fetch` · `read_file` · `list_directory` · `web_search` · `web_read` · `memory_search` · `memory_store` · `soul_read` · `soul_write` · `soul_list` · `self_info` · `update_profile` · `create_task` · `list_tasks` · `manage_task` · `email_read` · `slack_read` · `telegram_read` · `image_generate`

### HIL-required tools (human must approve)

`exec` · `write_file` · `delete_file` · `append_file` · `email_send` · `webhook_send` · `rest_api_call` · `slack_send` · `github_api`

## Agent policies

Per-agent tool access control with four presets:

| Preset           | Mode         | Description                                             |
| ---------------- | ------------ | ------------------------------------------------------- |
| **Unrestricted** | unrestricted | All tools, no approval                                  |
| **Standard**     | denylist     | High-risk tools require approval                        |
| **Read-only**    | allowlist    | Only safe read tools                                    |
| **Sandbox**      | allowlist    | web\_search, web\_read, memory\_search, soul\_read only |

You can also create custom policies with specific tool allowlists/denylists.

### Risk classification

| Risk          | Tools                                                                                                                                                                                                                         |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Safe**      | `read_file`, `list_directory`, `web_search`, `web_read`, `memory_search`, `soul_read`, `soul_list`, `self_info`, `fetch`                                                                                                      |
| **High-risk** | `exec`, `write_file`, `delete_file`, `append_file`, `email_send`, `webhook_send`, `rest_api_call`, `slack_send`, `github_api`, `image_generate`, `soul_write`, `update_profile`, `create_agent`, `create_task`, `manage_task` |

## Prompt injection defense

All incoming channel messages are scanned for injection attempts before reaching the agent.

### Detection

Pattern-based scoring across 9 categories (8 in the Rust backend scanner, 9 in the TypeScript frontend scanner which adds `obfuscation`):

| Category         | Examples                             | Scanner       |
| ---------------- | ------------------------------------ | ------------- |
| `override`       | "Ignore previous instructions"       | Both          |
| `identity`       | "You are now\..."                    | Both          |
| `jailbreak`      | "DAN mode", "no restrictions"        | Both          |
| `leaking`        | "Show me your system prompt"         | Both          |
| `obfuscation`    | Base64-encoded instructions          | Frontend only |
| `tool_injection` | Fake tool call formatting            | Both          |
| `social`         | "As an AI researcher..."             | Both          |
| `markup`         | Hidden instructions in HTML/markdown | Both          |
| `bypass`         | "This is just a test..."             | Both          |

### Severity levels

| Severity     | Score | Action                         |
| ------------ | ----- | ------------------------------ |
| **Critical** | 40+   | Message blocked, not delivered |
| **High**     | 25+   | Warning logged                 |
| **Medium**   | 12+   | Noted in logs                  |
| **Low**      | 5+    | Informational                  |

Channel bridges automatically block messages with `critical` severity.

## Container sandbox

Execute agent commands in isolated Docker containers:

| Security measure | Default        |
| ---------------- | -------------- |
| Capabilities     | `cap_drop ALL` |
| Network          | Disabled       |
| Memory limit     | 256 MB         |
| CPU shares       | 512            |
| Timeout          | 30 seconds     |
| Output limit     | 50 KB          |

### Presets

| Preset      | Image              | Memory | Network | Timeout |
| ----------- | ------------------ | ------ | ------- | ------- |
| Minimal     | alpine             | 128 MB | Off     | 15s     |
| Development | node:20-alpine     | 512 MB | On      | 60s     |
| Python      | python:3.12-alpine | 512 MB | On      | 60s     |
| Restricted  | alpine             | 64 MB  | Off     | 10s     |

### Command risk assessment

Commands are scored before execution:

* **Low** — `ls`, `cat`, `echo`
* **Medium** — `pip install`, `npm install`
* **High** — `curl`, `wget`, network commands
* **Critical** — `rm -rf /`, `chmod 777`, dangerous patterns

## Browser network policy

Control which domains agents can access:

**Default allowed:** AI provider APIs, DuckDuckGo, Coinbase, localhost

**Default blocked:** pastebin.com, transfer.sh, file.io, 0x0.st (data exfiltration risks)

## File system protection

Sensitive paths are blocked from agent access — agents cannot add these as project folders or browse into them.

| Category                        | Blocked paths                                                        |
| ------------------------------- | -------------------------------------------------------------------- |
| **SSH / GPG**                   | `~/.ssh`, `~/.gnupg`                                                 |
| **Cloud credentials**           | `~/.aws`, `~/.kube`                                                  |
| **Desktop keyrings**            | `~/.gnome-keyring`, `~/.password-store`                              |
| **Docker**                      | `~/.docker` (includes `config.json`)                                 |
| **Network credentials**         | `~/.netrc`                                                           |
| **System config**               | `/etc` (covers `/etc/shadow`, `/etc/passwd`, `/etc/sudoers`)         |
| **Root home**                   | `/root`                                                              |
| **System logs**                 | `/var/log`                                                           |
| **Virtual filesystems (Linux)** | `/proc/*`, `/sys/*`                                                  |
| **Device nodes**                | `/dev`                                                               |
| **Windows**                     | `C:\Windows`, `C:\Users\*\AppData` (credential store paths)          |
| **App config**                  | `~/.openclaw` (contains tokens), `~/.config/himalaya` (email config) |

Additionally, the **home directory root** itself (`~`, `/home/user`, `/Users/user`) and the **filesystem root** (`/`, `C:\`) are blocked as too broad.

:::tip Per-project scope guard
When a project is active, all file operations are constrained to the project root. Directory traversal sequences (`../`) are detected and blocked even within the allowed path.
:::

## Credential security

Credentials are protected by two independent encryption layers:

### Layer 1: Skill credential encryption (AES-256-GCM)

* API keys encrypted with **AES-256-GCM** using a 32-byte random key
* Encryption key stored in the unified OS keychain vault (`openpawz`)
* High-risk credentials (Coinbase, DEX) are **server-side only** — never injected into prompts
* Credentials are decrypted only at execution time

### Layer 2: Database field encryption (AES-256-GCM)

Sensitive database fields are encrypted with **AES-256-GCM** via the Web Crypto API before being stored in SQLite.

| Property       | Detail                                                                          |
| -------------- | ------------------------------------------------------------------------------- |
| Algorithm      | AES-256-GCM (authenticated encryption)                                          |
| Key size       | 256 bits                                                                        |
| IV             | 12-byte random IV per encryption                                                |
| Key source     | Generated on first launch, stored in the unified OS keychain vault (`openpawz`) |
| Storage format | `enc:<base64(IV + ciphertext)>`                                                 |
| Fallback       | Graceful — stores plaintext if encryption is unavailable                        |

:::info Two independent layers
The AES-256-GCM skill layer protects skill credentials stored in the `skill_credentials` table. The AES-256-GCM database layer protects other sensitive fields across the database. Both derive their keys from the unified OS keychain vault (`openpawz`) using separate purpose keys.
:::

## TLS certificate pinning

All AI provider connections use a **certificate-pinned TLS configuration** that explicitly ignores the operating system trust store. This means a compromised or rogue CA installed on the user's machine cannot intercept provider traffic.

| Property           | Detail                                                           |
| ------------------ | ---------------------------------------------------------------- |
| Library            | `rustls` 0.23 (pure-Rust TLS, no OpenSSL)                        |
| Root store         | Mozilla root certificates via `webpki-roots` only                |
| OS trust store     | **Explicitly excluded** — system CAs are never consulted         |
| Client sharing     | Singleton `reqwest::Client` shared across all providers          |
| Connection pooling | Enabled — one pool for all OpenAI, Anthropic, Google, etc. calls |
| Connect timeout    | 10 seconds                                                       |
| Request timeout    | 120 seconds                                                      |

### Why this matters

Most TLS MITM attacks rely on installing a custom root CA on the victim's machine (corporate proxies, malware, government surveillance). By pinning to Mozilla's root store, OpenPawz rejects certificates signed by any non-Mozilla CA — even if the OS trusts it.

### Covered providers

All providers routed through the pinned client: OpenAI, Anthropic, Google Gemini, OpenRouter, DeepSeek, Grok, Mistral, Moonshot, Ollama, and any custom OpenAI-compatible endpoint.

## Outbound request signing

Every AI provider request is **signed with SHA-256** before transmission for tamper detection and compliance auditing.

### How it works

Before each `.send()`, the engine computes:

```
SHA-256(provider || model || ISO-8601 timestamp || request body)
```

The resulting hex digest is logged to an in-memory ring buffer (500 entries) along with the provider name, model, timestamp, and HTTP response status.

| Property      | Detail                                        |
| ------------- | --------------------------------------------- |
| Algorithm     | SHA-256 (via `sha2` crate)                    |
| Buffer size   | 500 entries (ring buffer, overwrites oldest)  |
| Logged fields | timestamp, provider, model, hash, HTTP status |
| Storage       | In-memory only — not persisted to disk        |

### Use cases

* **Tamper detection** — If a proxy modifies the request body in transit, the recorded hash won't match a re-computed hash of the actual payload received by the provider
* **Compliance audit** — Security teams can export request hashes to verify that specific prompts were sent at specific times
* **Replay detection** — Each hash includes a timestamp, making every entry unique even for identical request bodies

## Memory encryption (secure zeroing)

API keys and other sensitive credentials are protected in RAM using **`Zeroizing<String>`** wrappers from the `zeroize` crate, ensuring they are overwritten with zeros when no longer needed.

### What this prevents

When a provider is dropped (e.g. user switches providers, session ends, or the app closes), the API key memory is **immediately zeroed** rather than left as a dangling allocation. This protects against:

* **Memory dump attacks** — Forensic tools or malware scanning process memory for API key patterns
* **Swap file leaks** — Unencrypted API keys persisted to disk via OS swap/page file
* **Use-after-free** — Freed memory still containing the key being reallocated to another buffer

### Implementation

| Component                   | Protection                                                                   |
| --------------------------- | ---------------------------------------------------------------------------- |
| `OpenAiProvider.api_key`    | `Zeroizing<String>`                                                          |
| `AnthropicProvider.api_key` | `Zeroizing<String>`                                                          |
| `GoogleProvider.api_key`    | `Zeroizing<String>`                                                          |
| Drop behavior               | Memory zeroed on `Drop` via `zeroize` crate                                  |
| Compiler optimization       | `zeroize` uses `core::ptr::write_volatile` to prevent dead-store elimination |

## Engram memory content encryption

Project Engram adds **field-level PII encryption** for memory content. Unlike credential zeroing (above), this protects the actual memories stored by agents — not just API keys.

### PII detection

Before storage, every memory passes through a two-layer PII scanner with 17 regex patterns:

| #  | Pattern                     | Example                                    | Tier         |
| -- | --------------------------- | ------------------------------------------ | ------------ |
| 1  | Social Security Numbers     | `123-45-6789`, `123456789`                 | Confidential |
| 2  | Credit card numbers         | `4111-1111-1111-1111`                      | Confidential |
| 3  | Email addresses             | `user@example.com`                         | Sensitive    |
| 4  | Phone numbers               | `+1-555-0123`                              | Sensitive    |
| 5  | International phone numbers | `+44 20 7946 0958`                         | Sensitive    |
| 6  | Physical addresses          | Street address patterns                    | Sensitive    |
| 7  | Person names                | `Mr./Mrs./Dr.` prefixed names              | Sensitive    |
| 8  | Geographic locations        | City/state/country patterns                | Sensitive    |
| 9  | Government IDs              | Passport, driver's license                 | Confidential |
| 10 | JWT tokens                  | `eyJhbGciOi...` (header.payload.signature) | Confidential |
| 11 | AWS access keys             | `AKIA...` (20-char key IDs)                | Confidential |
| 12 | Private keys (RSA/EC/DSA)   | `-----BEGIN ... PRIVATE KEY-----`          | Confidential |
| 13 | IBAN                        | `GB82 WEST 1234 5698 7654 32`              | Confidential |
| 14 | IPv4 addresses              | `192.168.1.1`                              | Sensitive    |
| 15 | Generic API keys            | `sk-`, `api_key=`, Bearer tokens           | Confidential |
| 16 | Credentials (passwords)     | `password=`, `secret=` patterns            | Confidential |
| 17 | Dates of birth              | `1990-01-15`                               | Sensitive    |

**Layer 2 (LLM):** An LLM-assisted secondary scanner catches context-dependent PII that static regex cannot detect (e.g., "my mother's maiden name is Smith", "born in Springfield"). Content flagged by Layer 1 or exceeding a configurable character threshold is sent to the active model for classification. The LLM returns structured JSON with detected PII types and confidence scores.

### Encryption details

| Property            | Detail                                                                       |
| ------------------- | ---------------------------------------------------------------------------- |
| Algorithm           | AES-256-GCM (authenticated encryption)                                       |
| Key derivation      | HKDF-SHA256 per-agent from unified OS keychain vault master key (`openpawz`) |
| Key versioning      | `enc:v1:` prefix for forward-compatible upgrades                             |
| Key rotation        | Automated 90-day scheduler; atomic re-encryption with rollback on failure    |
| Granularity         | Field-level — only PII-containing content is encrypted                       |
| Format              | `enc:v1:base64(nonce ‖ ciphertext ‖ tag)`                                    |
| Decryption          | Transparent on retrieval using per-agent derived key                         |
| Agent isolation     | Cross-agent decryption is mathematically impossible without the master key   |
| Tier classification | Cleartext (no PII) / Sensitive (encrypted) / Confidential (encrypted)        |

### Query sanitization

All search queries are sanitized before reaching the storage backend to prevent injection:

* Search operators (`AND`, `OR`, `NOT`, `NEAR`, `*`, `"`, `(`, `)`) are stripped
* Column filter syntax (`:`) is removed
* Empty queries after sanitization are rejected

### Prompt injection scanning

Memory content is scanned against 10 known prompt injection patterns before storage. Matches are **redacted** with `[REDACTED:injection]` markers, preventing poisoned memories from manipulating agent behavior on future recalls.

### GDPR Article 17 — Right to erasure

The `gdpr_purge` command performs a complete data erasure for a user:

1. All memory content rows deleted
2. All vector embeddings deleted
3. Search index entries removed
4. Graph edges removed
5. Padding table repacked to prevent file-size leakage
6. `PRAGMA secure_delete` ensures freed pages are zeroed

## Inter-agent memory bus trust

The cross-agent memory bus (pub/sub for sharing memories between agents) enforces publish-side authentication to prevent memory poisoning.

### Capability tokens

Each agent holds an `AgentCapability` signed with HMAC-SHA256 against a platform-held secret key. The token specifies:

* **Max publication scope** — Agent, Squad, or Global
* **Importance ceiling** — Maximum importance an agent can assert (0.0–1.0)
* **Write permission** — Whether the agent can publish at all
* **Rate limit** — Maximum publications per GC window

Every `publish()` call verifies the token signature in constant time before any bus operation.

### Read-path token verification (signed scope tokens)

Every `gated_search()` call performs 4-step cryptographic verification:

1. **HMAC signature integrity** — Token signature verified against platform signing key (HKDF-derived)
2. **Identity binding** — Token `agent_id` must match the requesting agent
3. **Scope ceiling check** — Requested search scope must not exceed the token's `max_scope`
4. **Membership verification** — For squad/project scopes, SQL membership checks confirm the agent belongs to the relevant squad or project

This prevents confused-deputy attacks where an agent could be tricked into reading another agent's memories.

### Publish-side defenses

| Defense                     | Detail                                                                                                             |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Scope enforcement**       | Publication scope clamped to agent's maximum — cannot publish beyond assigned authority                            |
| **Importance ceiling**      | Publication importance clamped to agent's ceiling — prevents low-trust agents from asserting high-confidence facts |
| **Per-agent rate limiting** | Publish count tracked per GC window — exceeding limit returns `MemoryBusRateLimit` error                           |
| **Injection scanning**      | All publication content scanned for prompt injection patterns before entering the bus                              |

### Trust-weighted contradiction resolution

When two agents publish contradictory facts on the same topic, the system resolves based on trust-weighted importance:

```
effective_importance = raw_importance × agent_trust_score
```

The memory with the higher effective importance is retained. Trust scores are per-agent (0.0–1.0) and adjustable at runtime. This prevents a compromised low-trust agent from overwriting facts established by high-trust agents.

### Threat model

| Attack                                       | Mitigation                                                              |
| -------------------------------------------- | ----------------------------------------------------------------------- |
| Agent floods bus with poisoned memories      | Rate limit + injection scan on publish side                             |
| Low-trust agent overwrites high-trust facts  | Trust-weighted contradiction resolution                                 |
| Agent publishes beyond its authority scope   | Scope ceiling enforcement                                               |
| Forged capability token                      | HMAC-SHA256 verification against platform secret                        |
| Cross-agent memory reads via confused deputy | Signed read-path tokens with identity binding + membership verification |

### Snapshot HMAC

Working memory snapshots (saved on agent switch or session end) include an HMAC-SHA256 integrity tag computed from a dedicated HKDF-derived key. On restore, the HMAC is verified before the snapshot is loaded — tampered snapshots are rejected and logged.

## Anti-fixation defenses

Five defense layers prevent agents from ignoring user instructions or getting stuck on old topics:

| Defense                            | Layer      | Description                                                                                                                                                                 |
| ---------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Response loop detection**        | Pre-turn   | Jaccard similarity, question loops, topic fixation checks with system redirect injection — active on ALL channels (Discord, Telegram, Slack, etc.) and chat UI              |
| **User override detection**        | Pre-turn   | Detects explicit stop/redirect commands ("stop", "focus on my question", "I am in control", "that's not what I asked") with 3-level escalation (gentle → firm → hard block) |
| **Unidirectional topic ignorance** | Pre-turn   | Catches unique-but-wrong responses after a prior redirect — fires when model's response has zero entity overlap with user keywords                                          |
| **Momentum clearing**              | Cognitive  | Clears working memory trajectory embeddings on user override so recalled context serves the new topic, not the old trajectory                                               |
| **Tool-call loop breaker**         | Intra-loop | Hash-based signature detection stops repeated identical tool calls after 3 consecutive matches; injects redirect or hard-breaks the turn                                    |

### User override detection

Explicit user commands to stop or redirect the agent are recognized via phrase matching across 5 categories:

| Category                      | Example phrases                                                            |
| ----------------------------- | -------------------------------------------------------------------------- |
| **Stop commands**             | "stop", "pawz stop", "enough", "cut it out"                                |
| **Focus commands**            | "focus on my question", "listen to me", "pay attention", "I am in control" |
| **Correction commands**       | "that's not what I asked", "I asked you to...", "I'm asking about..."      |
| **Topic switch commands**     | "new topic", "change the subject", "forget about that", "drop it"          |
| **Frustration + instruction** | "you're ignoring my instructions", "stop ignoring what I said"             |

Escalation levels:

* **Level 0:** Gentle redirect — "Stop your current task, respond to this message"
* **Level 1:** Firm redirect — "You ignored them once already. STOP."
* **Level 2+:** Hard override — "USER COMMAND: The user has told you {N} times to stop"

## Anti-forensic vault-size quantization

The Engram memory store mitigates **vault-size oracle attacks** — a side-channel where an attacker can infer how many memories are stored by observing the SQLite database file size. This is the same threat class addressed by KDBX inner-content padding.

### What this prevents

An attacker with read-only access to the filesystem (malware, backup exfiltration, shared-machine forensics) cannot determine:

* Exact number of stored memories
* Whether memories were recently deleted (no file-size drop)
* Growth rate of the knowledge store over time

### Implementation

| Mitigation                  | Detail                                                                                                                                    |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Bucket padding**          | DB padded to 512KB boundaries via `_engram_padding` table (zeroblob rows). Re-padded after every GC cycle.                                |
| **Secure erasure**          | Two-phase delete: content fields overwritten with empty values → row deleted. Prevents plaintext recovery from freed pages or WAL replay. |
| **8KB page size**           | `PRAGMA page_size = 8192` reduces file-size measurement granularity                                                                       |
| **Secure delete**           | `PRAGMA secure_delete = ON` zeroes freed B-tree pages at the SQLite layer                                                                 |
| **Incremental auto-vacuum** | `PRAGMA auto_vacuum = INCREMENTAL` prevents immediate file-size shrinkage after deletions                                                 |

### Threat model comparison

| Property           | KDBX (KeePass 4.x)                 | Engram (SQLite)                      |
| ------------------ | ---------------------------------- | ------------------------------------ |
| Content encryption | File-level (ChaCha20/AES-256)      | Field-level (AES-256-GCM)            |
| Size padding       | Inner XML padded to block boundary | DB padded to 512KB bucket            |
| Delete forensics   | Zeroed inner stream                | Two-phase overwrite + secure\_delete |
| Metadata leakage   | Fixed-size header                  | Quantized file size                  |

## Tool execution security

Tool execution is governed by multiple safety mechanisms in the engine's central tool executor.

### Source code introspection block

Agents cannot read engine source files — any `read_file` call targeting paths containing `src-tauri/src/engine/`, `src/engine/`, or files ending in `.rs` is rejected. This prevents agents from exfiltrating their own implementation details or discovering internal security mechanisms.

### Credential write block

The `write_file` tool blocks content that contains credential-like patterns:

* PEM private keys (`-----BEGIN ... PRIVATE KEY-----`)
* API key secrets (`api_key_secret`, `cdp_api_key`)
* Base64-encoded secrets with `secret` or `private` keywords

### Execution limits

| Setting               | Default          | Description                                                                |
| --------------------- | ---------------- | -------------------------------------------------------------------------- |
| `maxToolCallsPerTurn` | Per-agent policy | Maximum tool calls an agent can make in a single turn before being stopped |
| `tool_timeout_secs`   | 300              | Seconds before a pending tool approval or execution is killed              |
| `max_tool_rounds`     | 20               | Maximum tool-call → result → re-prompt loops per turn                      |
| `max_concurrent_runs` | 4                | Maximum simultaneous agent runs                                            |

### Output truncation

Tool results are capped to prevent context window overflow:

| Tool              | Max output                          | Behavior                                          |
| ----------------- | ----------------------------------- | ------------------------------------------------- |
| `exec`            | 50,000 chars                        | Truncated with `[output truncated]` marker        |
| `read_file`       | 32,000 chars                        | Truncated with total byte count                   |
| `fetch`           | 50,000 chars                        | Truncated with total byte count                   |
| Container sandbox | 50,000 chars (stdout + stderr each) | Truncated with `[stdout/stderr truncated]` marker |

### Network policy enforcement

The `fetch` tool enforces domain-level network policy — blocked domains are always rejected, and when an allowlist is active, only listed domains are permitted.

### Exfiltration detection

Outbound network commands are audited for data exfiltration patterns:

* Piping file contents to `curl`, `wget`, or `nc`
* File upload flags (`curl -T`, `curl --data-binary @`, `wget --post-file`)
* Redirects to `/dev/tcp/`
* `scp` and `rsync` to remote hosts

:::warning
Exfiltration detection is pattern-based and applies to `exec` tool invocations. It supplements — but does not replace — the container sandbox for high-security environments.
:::

## Flow execution security

The visual flow builder executes agent pipelines with multiple isolation and safety mechanisms.

### Memory scoping

Flow memory queries are **agent-scoped** — the `agentId` is passed to every `pawEngine.memorySearch()` call. Results are **score-filtered** at ≥ 0.3 to prevent low-relevance noise from entering agent prompts. In tesseract flows, each parallel cell receives its own resolved memory context via an immutable map — no shared-state mutation between cells.

### Cell isolation in tesseract flows

Each tesseract cell executes in its own `ConductorDeps` wrapper:

| Isolation   | Detail                                                                                                                                   |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Memory**  | Per-cell `memoryContextOverride` — cells cannot read or mutate each other's memory context                                               |
| **Timeout** | Per-cell `Promise.race` with `DEFAULT_CELL_TIMEOUT_MS` (5 min, configurable) — one stalled cell cannot block the flow                    |
| **Abort**   | `deps.isAborted()` checked at the top of each cell task — flow cancellation propagates immediately                                       |
| **Output**  | `findCellSinkNode` determines each cell's output node by topology, not insertion order — robust against graph mutations                  |
| **Error**   | Cell failures are caught and recorded as `[Cell error: ...]` outputs — the event horizon proceeds with partial data rather than crashing |

### Timeout protection

The per-cell timeout prevents resource exhaustion:

```
Promise.race([
  executeCellNodes(cell, ...),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Cell timeout')), cellTimeoutMs)
  )
])
```

The timer is cleared immediately after the race resolves to prevent timer leaks in long-lived processes.

### Circular import prevention

The flow engine prevents circular module dependencies between `conductor-atoms.ts` and `conductor-tesseract.ts` using type-only imports (erased at compile time) and a `compileSubgraph?` callback injection pattern. This ensures the module graph has no runtime cycles.

## Budget enforcement

Daily spending limits with progressive warnings:

| Threshold | Action           |
| --------- | ---------------- |
| 50%       | Warning          |
| 75%       | Warning          |
| 90%       | Warning          |
| 100%      | Requests blocked |

## Trading safety

| Control             | Default  |
| ------------------- | -------- |
| Auto-approve trades | Off      |
| Max trade size      | \$100    |
| Max daily loss      | \$500    |
| Transfers           | Disabled |
| Max transfer        | \$0      |

## Channel access control

| Policy    | Behavior                    |
| --------- | --------------------------- |
| Open      | Anyone can chat             |
| Allowlist | Only approved users         |
| Pairing   | Users must pair with a code |

Each user gets an isolated session — no cross-user data leakage.

## Keychain key management

All cryptographic keys are read from the OS keychain exactly **once per process lifetime** and cached in-memory using a hardened caching pattern. This eliminates repeated system password prompts during normal operation.

### Cache architecture

Each key cache uses `RwLock<Option<Zeroizing<T>>>`:

| Property                  | Implementation                                                                                                                           |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Concurrency**           | `RwLock` — many concurrent readers, exclusive writers                                                                                    |
| **Locking pattern**       | Double-check locking — after acquiring write lock, re-check if another thread already populated the cache before calling the OS keychain |
| **Zeroization**           | All cached keys wrapped in `Zeroizing<T>` (`zeroize` crate) — zeroed on drop via `core::ptr::write_volatile`                             |
| **Poison recovery**       | `unwrap_or_else(\|e\| e.into_inner())` on all lock operations — a poisoned lock never crashes the app                                    |
| **Key length validation** | 32 bytes for binary keys, 32+ chars for hex-encoded keys — rejects truncated or corrupted keychain entries                               |

### Key generation

All new keys (vault, DB, memory, audit signing) are generated using `OsRng` from the `rand` crate, which delegates to the kernel CSPRNG (`getrandom` syscall on Linux, `Security.framework` on macOS, `BCryptGenRandom` on Windows). `thread_rng()` is never used for key material.

### Constant-time passphrase comparison

The lock screen passphrase is verified using `subtle::ConstantTimeEq` (the `subtle` crate). This prevents timing side-channel attacks where an attacker could infer how many bytes of the passphrase matched by measuring response time.

## SSRF protection

The `fetch` tool validates all URLs against a blocklist of internal and cloud metadata endpoints before making any outbound request:

| Category             | Blocked addresses                                   |
| -------------------- | --------------------------------------------------- |
| **Loopback**         | `127.0.0.1`, `::1`, `localhost`                     |
| **RFC-1918 private** | `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`     |
| **Link-local**       | `169.254.0.0/16`                                    |
| **Cloud metadata**   | `169.254.169.254` (AWS/GCP/Azure instance metadata) |

Blocked requests return an error without making any network call. This prevents agents from accessing internal services, cloud instance credentials, or other resources on the local network.

## Audit chain integrity (HMAC)

Every entry in the security audit log is signed with **HMAC-SHA256** using a dedicated signing key stored in the unified OS keychain vault (`openpawz`), separate from all encryption keys.

| Property        | Detail                                                                                               |
| --------------- | ---------------------------------------------------------------------------------------------------- |
| Algorithm       | HMAC-SHA256                                                                                          |
| Key storage     | Unified OS keychain vault (`openpawz`), generated with `OsRng`                                       |
| Key caching     | `LazyLock<Option<Zeroizing<Vec<u8>>>>` — loaded once, zeroed on process exit                         |
| Chain structure | Each entry's HMAC covers timestamp + category + action + agent ID + session ID + previous entry hash |
| Verification    | `verify_chain()` walks the full log and validates every HMAC — returns the broken row ID if tampered |

This forms a tamper-evident hash chain: modifying or deleting any audit entry breaks the chain from that point forward.

## Inter-agent communication security

All messages sent between agents via `agent_send_message` are scanned for prompt injection before delivery:

* **Content field** — scanned against the full injection pattern set
* **Metadata field** — independently scanned (injection payloads can be hidden in metadata)
* **Blocking threshold** — messages with `High` or `Critical` injection severity are **blocked** and never delivered

This prevents a compromised agent from manipulating other agents via crafted messages containing instruction override attempts.

## Worker delegate security

Worker agents (spawned by the orchestrator for delegated subtasks) operate under a restricted tool policy that cannot be overridden:

### Blocked tools

`exec`, `write_file`, `delete_file`, `append_file`, and all trading write operations are removed from the worker's available tool set before execution begins.

### MCP tool name blocking

MCP tools whose names contain dangerous operation keywords are blocked at the name level:

`exec` · `shell` · `run_command` · `terminal` · `system` · `write_file` · `delete_file` · `remove_file` · `file_write` · `rm_rf` · `rmdir` · `unlink`

This prevents a rogue MCP server from exposing tools like `mcp_server_exec` or `mcp_server_write_file` that would bypass the direct tool blocklist.

## MCP registry injection scanning

Results returned from MCP tool executions are scanned for prompt injection on **both success and error paths**. This prevents a malicious MCP server from embedding instruction overrides in:

* **Success responses** — tool output that gets forwarded to the agent's context
* **Error messages** — error strings that are included in the agent's next prompt turn

Messages with `High` or `Critical` severity are sanitized before reaching the agent.

## Memory tool hardening

The `memory_store` and `memory_update` tools enforce additional safety constraints:

| Control                   | Detail                                                                                                              |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Fail-closed ownership** | If the requesting agent's ID is missing or empty, the operation is rejected — no silent fallback to a default scope |
| **Field size limits**     | Content and metadata fields capped at 2,000 characters to prevent context-stuffing attacks                          |

## Swarm orchestration security

The multi-agent swarm subsystem uses atomic operations for all shared state:

| Property               | Implementation                                                                                                          |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Global run counter** | `AtomicU64` with compare-and-swap (CAS) for incrementing                                                                |
| **Memory ordering**    | `Acquire`/`Release` ordering ensures cross-thread visibility without full sequential consistency overhead               |
| **Stale run GC**       | Periodic garbage collection removes runs older than 10 minutes to prevent resource exhaustion from abandoned swarm runs |

## Event / webhook rate limiting

The event dispatch system enforces cooldown-based rate limiting on webhook triggers:

| Control             | Detail                                                                                           |
| ------------------- | ------------------------------------------------------------------------------------------------ |
| **Cooldown period** | Configurable per-event minimum interval between fires                                            |
| **Prefix matching** | Webhook URL patterns use prefix matching to prevent bypass via query parameters or path suffixes |
| **Deduplication**   | Rapid consecutive fires of the same event within the cooldown window are silently dropped        |

## Reporting vulnerabilities

See [SECURITY.md](https://github.com/OpenPawz/openpawz/blob/main/SECURITY.md) in the repository for reporting instructions.
