# PULSE Security Policy

> 17 layers of defense in depth. Each one enforced by code, not promises.
> Status: **ENFORCED** = active in production | **PARTIAL** = implemented but gaps exist | **NOT ENFORCED** = code exists but not wired

---

## Reporting Vulnerabilities

If you discover a security vulnerability in PULSE, please report it responsibly.

**Do NOT open a public GitHub issue for security vulnerabilities.**

Instead, email the maintainers directly or open a private security advisory via GitHub's Security tab.

---

## The 17 Layers

### Layer 1: Immutable Constitution (Hash Lock)

The Constitution (`Corpus_Callosum/CONSTITUTION.md`) is SHA-256 hash-locked. The hash is stored in `.env` as `CONSTITUTION_HASH`. At every orchestrator boot, the hash is recomputed and compared. Mismatch = system refuses to start.

- **Enforced by:** `pulse/admin/setup/update_constitution_hash.py` (auto-sync tool), `pulse/daemons/orchestrator_security.py` (boot verification), git pre-commit hook (blocks commits with stale hash)
- **Status:** ENFORCED

---

### Layer 2: Sealed Environment (RSA Signature)

The `.env` file is cryptographically sealed with RSA. The signature lives in `.env.sig`. At boot, the orchestrator loads Founder public keys from `authorized_humans.json` and verifies the signature. Tampered `.env` = system refuses to start.

- **Enforced by:** `pulse/daemons/orchestrator_security.py` → `verify_sealed_environment()`, called at orchestrator boot
- **Status:** ENFORCED

---

### Layer 3: Cryptographic Identity (HMAC-SHA256 Proof-of-Origin)

Every agent signs its output with HMAC-SHA256 using an agent-specific key derived from `PULSE_BRAIN_KEY`. The bridge daemon verifies these signatures before accepting knowledge into the pipeline.

- **Enforced by:** `pulse/core/pulse_crypto.py` → `sign_string()` / `verify_string()`. Agents sign in `pulse_claude.py`, `pulse_gemini.py`, `pulse_codex.py`. Bridge verifies in `pulse_bridge.py`.
- **Key management:** `PULSE_BRAIN_KEY` env var is mandatory — `RuntimeError` raised if missing.
- **Status:** ENFORCED
- **Coverage:** All JSON write paths to Evidence/ and Private/ are HMAC-signed via `brain_io.sign_item()`. Consolidation re-signs items after modification. Unsigned items from before this enforcement remain unsigned until consolidation rewrites them.

---

### Layer 4: Anti-Replay Protection (5-Minute Window)

All HMAC-signed payloads include a `_signed_at` timestamp. Verification rejects payloads older than 300 seconds (5 minutes). Prevents captured signatures from being replayed.

- **Enforced by:** `pulse/core/pulse_crypto.py` → `verify()` checks `REPLAY_WINDOW = 300`
- **Status:** ENFORCED
- **Note:** If `_signed_at` field is missing from a payload, the check is skipped. All current signing paths include the timestamp.

---

### Layer 5: Agent Whitelist

Only known agents can register and operate. Dynamic worker IDs are matched by prefix. Daemons are explicitly blocked from claiming tasks (Separation of Concerns).

- **Enforced by:** `pulse/tasks/dispatcher_lifecycle.py` → `DAEMON_AGENTS`, `ALLOWED_AGENTS`, `ALLOWED_AGENT_PREFIXES`. Unknown agents force-released in `check_stale_claims()`. Daemon claiming blocked in `pulse/workers/runner/worker_router.py` → `claim_task_atomic()`.
- **Status:** ENFORCED

---

### Layer 6: Dispatch Authentication

Task dispatches are HMAC-signed. The dispatcher flags unsigned dispatches (H4 rule) rather than silently accepting them.

- **Enforced by:** `pulse/tasks/pulse_dispatcher.py` H4 flagging, `pulse/core/pulse_crypto.py` HMAC on dispatch payloads
- **Status:** ENFORCED
- **Coverage:** Unsigned dispatches have their tasks set to `status: "rejected"` with `rejection_reason: "unsigned_dispatch"`. Workers only claim `open`/`escalated` tasks, so rejected tasks are never executed. Founder can bypass with `--force-unsigned` flag via pulse_relay.py.

---

### Layer 7: Path Traversal Prevention

All file operations in worker tools and the API validate that paths resolve within the project root. Attempts to escape the sandbox are rejected with `PermissionError` or HTTP 403.

- **Enforced by:** `pulse/workers/worker_tools.py` → `_guard_path()` on `read_file`, `write_file`, `list_files`, `search_code`, `edit_file`. `pulse/api/pulse_api.py` → path validation on evidence endpoint.
- **Status:** ENFORCED

---

### Layer 8: Rate Limiting

In-memory token bucket rate limiter on all API endpoints. 10 requests per minute per client IP. Returns HTTP 429 when exceeded.

- **Enforced by:** `pulse/api/pulse_api.py` → `RateLimiter` class, checked on every endpoint
- **Status:** ENFORCED
- **Note:** State is in-memory — resets on API restart. No persistence across restarts.

---

### Layer 9: CORS Restriction

API only accepts requests from whitelisted origins. Production: `localhost:3000`, `localhost:5173`, `localhost:5174`.

- **Enforced by:** `pulse/api/pulse_api.py` → FastAPI `CORSMiddleware` with explicit `allow_origins` list
- **Status:** ENFORCED

---

### Layer 10: PII Detection

Compliance validator scans all action queue outputs for personally identifiable information: email addresses, phone numbers, SSNs, credit card numbers, IP addresses, passport numbers.

- **Enforced by:** `pulse/daemons/compliance_validator.py` → `PII_PATTERNS` regex bank, `validate()` method scans all actions
- **Status:** ENFORCED
- **Note:** Detection is regex-based. Obfuscated PII (e.g., base64-encoded) may evade detection.

---

### Layer 11: Harm Content Filtering

Compliance validator scans action queue for content enabling malicious exploits, fraud, systemic oppression, or lethal force. Matches are blocked and logged.

- **Enforced by:** `pulse/daemons/compliance_validator.py` → `HARM_PATTERNS` regex bank
- **Status:** ENFORCED
- **Coverage:** Synchronous compliance gate in `worker_compliance.py` checks task title + description against HARM_PATTERNS and PII_PATTERNS before execution in `worker_execute.py`. Additionally, `pulse_dispatcher.py` checks at dispatch time, rejecting harmful tasks before they reach the board. The async daemon remains as defense-in-depth.

---

### Layer 12: Private Region Segregation

User private data is stored in `Hippocampus/Private/` which is gitignored. Private lessons, preferences, and personal data never leave the local machine.

- **Enforced by:** `.gitignore` entry for `Hippocampus/Private/`, separate `write_private_learnings()` function in `pulse/brain/save_writers.py`
- **Status:** ENFORCED

---

### Layer 13: File Locking (Atomic Operations)

All shared JSON files use file-based mutex locking to prevent corruption from concurrent agent access. Read-modify-write operations are atomic.

- **Enforced by:** `pulse/core/brain_io.py` → `atomic_update_json()` uses `filelock.FileLock`. `save_json()` writes to temp file then atomic rename.
- **Status:** ENFORCED

---

### Layer 14: SSRF Prevention (Outbound Allowlist)

LLM API endpoints are validated against a whitelist of allowed hosts before any outbound HTTP call. Prevents agents from being tricked into calling arbitrary URLs.

- **Enforced by:** `pulse/tasks/decomposer.py` → `_ALLOWED_HOSTS` frozenset (`api.anthropic.com`, `api.openai.com`, `generativelanguage.googleapis.com`, `api.x.ai`, `localhost`, `127.0.0.1`), checked in `_validate_endpoint()`
- **Status:** ENFORCED
- **Note:** Only covers LLM decomposer endpoint routing. Worker tool `run_command` can still make arbitrary network calls via subprocess.

---

### Layer 15: Input Sanitization

All task fields (title, description, domain, complexity) are sanitized: control characters stripped, length capped at 500 characters. Prevents injection via task board.

- **Enforced by:** `pulse/tasks/decomposer.py` → `_sanitize_text()` regex strips `\x00-\x09`, `\x0b-\x1f`, `\x7f`. Applied in `_normalize_tasks()`.
- **Status:** ENFORCED

---

### Layer 16: Hash Quarantine (3-Strike Kill Switch)

Agents that violate governance can be quarantined by SHA-256 script hash (not just name — prevents rename evasion). The orchestrator continuously checks the quarantine list. Quarantined agents are force-killed. 3 kills within 300 seconds = permanent ban for that session.

- **Enforced by:** `pulse/daemons/orchestrator_security.py` → `enforce_quarantine()`, `quarantined_entities.json` in `state/`
- **Status:** ENFORCED

---

### Layer 17: Daemon Resilience (Exponential Backoff)

Crashed daemons are automatically restarted with exponential backoff (1s, 2s, 4s, 8s, 16s, 30s cap). After 5 consecutive failures, the daemon is marked dead and an alert is written to `state/daemon_alerts.json`.

- **Enforced by:** `pulse/daemons/orchestrator.py` → orchestrator main loop, `MAX_RESTARTS = 5`, `backoff = min(2 ** count, 30)`
- **Status:** ENFORCED

---

## Enforcement Summary

| # | Layer | Status | Coverage |
|---|-------|--------|----------|
| 1 | Constitution Hash Lock | ENFORCED | 100% |
| 2 | Sealed Environment (RSA) | ENFORCED | 100% |
| 3 | Cryptographic Identity (HMAC) | ENFORCED | 100% of new JSON writes signed |
| 4 | Anti-Replay (5-min window) | ENFORCED | 100% of signed payloads |
| 5 | Agent Whitelist | ENFORCED | 100% |
| 6 | Dispatch Authentication | ENFORCED | Rejects unsigned, Founder override available |
| 7 | Path Traversal Prevention | ENFORCED | 100% of file operations |
| 8 | Rate Limiting | ENFORCED | 100% of API endpoints |
| 9 | CORS Restriction | ENFORCED | 100% |
| 10 | PII Detection | ENFORCED | Regex-based, no obfuscation handling |
| 11 | Harm Content Filtering | ENFORCED | Inline gate + async daemon |
| 12 | Private Region Segregation | ENFORCED | 100% |
| 13 | File Locking (Atomic Ops) | ENFORCED | 100% of shared JSON |
| 14 | SSRF Prevention | ENFORCED | LLM endpoints only |
| 15 | Input Sanitization | ENFORCED | 100% of task fields |
| 16 | Hash Quarantine | ENFORCED | 100% |
| 17 | Daemon Resilience | ENFORCED | 100% |

**Overall: 17/17 layers fully enforced.**

---

## Known Gaps (Roadmap)

All 3 previously identified gaps have been resolved:

- **Gap 1 (Layer 3):** RESOLVED — `brain_io.sign_item()` seals all JSON writes to Evidence/Private. Consolidation re-signs.
- **Gap 2 (Layer 6):** RESOLVED — Unsigned dispatches rejected. Founder `--force-unsigned` override available.
- **Gap 3 (Layer 11):** RESOLVED — Synchronous compliance gate in worker pipeline + dispatcher. Async daemon remains as defense-in-depth.

---

## Supported Versions

| Version | Supported |
|---------|-----------|
| V10.x   | Yes       |
| V9.x    | Yes       |
| < V9.0  | No        |

## Dependencies

PULSE minimizes external dependencies:
- `filelock` — concurrent file access safety
- `cryptography` — RSA signature verification
- `fastapi` + `uvicorn` — REST API gateway
- `sentence-transformers` — semantic extraction (optional, graceful fallback)
- `networkx` — knowledge graph (optional, graceful fallback)
- All other functionality uses Python stdlib only
