Production · v1.7.0 · June 24 2026 · @cgfixit · localhost:8787 only

CyClaw

Offline-First · RAG-Enforced · Secure Local AI Second Brain
Topology = Policy. 5 invariants enforced by graph edges. Zero telemetry. Zero cloud by default.

FastAPI + LangGraph ChromaDB + BM25 + RRF 5 Security Invariants (hard) Zero Telemetry · 127.0.0.1 Single-file · Fully Offline Demo
Table of Contents
01

System Overview

What CyClaw is and why the architecture is deliberately hostile to cloud leakage

🧠 What Is CyClaw?

A personal offline-first "second brain" that answers exclusively from your private Markdown vault. No cloud, no subscription, no data exfiltration by default. FastAPI gateway + LangGraph state machine + Chroma/BM25 RRF hybrid retrieval + optional Grok fallback triple-gated behind explicit user confirmation.

🔒 Why "Topology = Policy"?

Most RAG systems bolt security on as config flags. CyClaw makes the 5 core guarantees architectural constraints — graph edges, not README promises. You literally cannot bypass RAG-first or skip the triple-gate Grok fallback without rewriting the LangGraph graph itself.

🌐 Fully Offline

All embeddings, retrieval, and inference on local hardware (LM Studio). Cloud fallback (Grok) is triple-gated and opt-in only. Zero telemetry — env vars killed before any SDK import.

🪪 Soul Governance

Persistent identity layer (soul.md) with SHA-256 drift detection, atomic writes via os.replace(), explicit human reason string required. No autonomous self-modification from any graph node.

🔌 Multi-Interface

FastAPI HTTP at 127.0.0.1:8787, MCP server for Claude Desktop & Copilot Studio, full browser terminal UI — all bound to loopback only.

02

Architecture & Request Flow

From query to audited response — every path converges, no shortcuts

┌────────────────────────────────────────────────────────────────┐
│  CLIENT  Browser · curl · Claude Desktop · MCP               │
└──────────────────────┬─────────────────────────────────────────┘
                       │ HTTP POST /query  (127.0.0.1:8787 only)┌────────────────────────────────────────────────────────────────┐
│  gate.py  (FastAPI)                                            │
│  ① Rate limit (60/min)  ② Injection filter (33 OWASP)         │
│  ③ Soul init + SHA drift check  ④ Telemetry kill (pre-import)  │
└──────────────────────┬─────────────────────────────────────────┘
                       │
                       ▼
┌────────────────────────────────────────────────────────────────┐
│  graph.py  (LangGraph 7-Node State Machine)                     │
│                                                                  │
│  [ENTRY] ─► retrieve (Chroma+BM25+RRF) ─► route_score           │
│                                                                  │
│    ├─ score ≥ 0.028 ─► local_llm (LM Studio)                  │
│    │                                                            │
│    └─ score low ─► user_gate (needs_confirm)                  │
│                    │                                            │
│          ┌──────────┤ confirmed+hybrid │ declined         │
│          ▼                              ▼                   │
│   grok_fallback                     offline_best                │
│   (triple gate)                                                  │
│                                                                  │
│  ALL PATHS ─► audit_logger (SHA-256 + PII redact)               │
└────────────────────────────────────────────────────────────────┘

Parallel: POST /ops/* ─► ops_runner.py (subprocess shim, v1.7 NEW)
          subprocess.run(argv_list) only ─ never imports sync/ or agentic/
1
RAG-First (non-negotiable)
retrieve is the unconditional first node. No LLM call can ever precede retrieval. Enforced by graph entry edge — not config.
2
Topology = Policy
Routing lives in graph edges, never in LLM decisions or Python if/else. Security is structural. The graph IS the policy document.
3
Triple-Gated External Fallback
Grok requires mode=hybrid AND grok.enabled=true AND user_confirmed_online=true simultaneously. All three must be true.
4
Audit Convergence
All 6 execution paths converge at audit_logger. Zero shortcut paths. Every query is SHA-256 hashed and logged to audit.jsonl.
5
Soul Governance
Any soul evolution requires explicit human reason string + injection scan + SHA-256 drift check + atomic os.replace(). No node autonomously modifies soul.
03

Files & Folders — ELI5 + Technical

Every file in plain English and code terms

CyClaw/ ├── gate.py # Front door: FastAPI, rate-limit, 33-pattern injection filter, soul init, telemetry kill ├── graph.py # Brain: LangGraph 7-node state machine; invariants live in graph EDGES ├── config.yaml # Single dial panel — one truth, one file ├── mcp_hybrid_server.py # MCP bridge to Claude Desktop / Copilot Studio │ ├── retrieval/ │ ├── hybrid_search.py # RRF fusion: Chroma cosine + BM25Okapi + Porter stemmer │ ├── indexer.py # .md/.txt vault ingestion + chunking + Chroma write │ └── embeddings.py # CPU embedding service, triple LRU cache │ ├── utils/ │ ├── sanitizer.py # 33 OWASP patterns, pre-LLM + corpus ingest, hot-reload │ ├── personality.py # Soul versioning: SHA-256 drift, atomic os.replace(), reason-gated │ ├── logger.py # Audit JSONL: SHA-256 hash + PII redaction on every path │ └── ops_runner.py # NEW v1.7: subprocess shim — never imports sync/ or agentic/ │ ├── agentic/ # Opt-in GitHub context + governed skills registry ├── sync/ # Opt-in Dropbox rclone vault sync (never auto-runs) │ ├── static/ │ ├── terminal.html # 46KB primary operator console (amber/dark, this demo is the matrix skin) │ └── extractor.html # Corpus extraction tool │ ├── schemas/ # Pydantic request/response models ├── data/ # Private corpus + soul.md (gitignored, never committed) ├── tests/ # 470+ passing tests (CI + CodeQL + pip-audit green) └── docs/ # Architecture docs, adversarial audit reports, this infographic
Key Files — ELI5 Cards
🚪
gate.py
The security guard at the front door
FastAPI ASGI app bound to 127.0.0.1:8787. Runs rate limiting (60 req/min sliding window), sanitizer.py injection scan (33 patterns), soul init, and kills all telemetry env vars before any SDK import. All /ops/* routes proxy through ops_runner.py.
🗺️
graph.py
The decision map that can never be cheated
LangGraph StateGraph with 7 named nodes. Routing is edge-defined — route_score edge function reads RRF score from state, never asks the LLM. All paths terminate at audit_logger. No dead-end paths.
🔍
hybrid_search.py
Two search engines fused into one smarter result
Reciprocal Rank Fusion of Chroma cosine-similarity (dense) + BM25Okapi (sparse, Porter-stemmed). Returns top-k with per-chunk RRF scores. Threshold 0.028 gates local vs fallback routing.
🛡️
sanitizer.py
Filter that catches bad instructions before the AI sees them
33 OWASP-aligned compiled regex patterns (was 13 in v1.6). Applied to both incoming queries and corpus chunks at ingest. Hot-reloadable from config. Returns [FILTERED] placeholder on match.
🆔
personality.py
The AI's identity card with tamper detection
Loads soul.md on every request, computes SHA-256, compares against stored hash. Mismatch triggers drift alert. Writes are atomic via os.replace(). Human reason string required. Full version history.
ops_runner.py (NEW v1.7)
Safe remote-control for sync and agentic — through a wall
Handles POST /ops/sync and POST /ops/agentic. Uses only subprocess.run(argv_list) — never import sync or import agentic. Hard isolation boundary. Loopback + API-key gated. Dry-run default.
04

Security Model — Defense in Depth

8 layers, all enforced by code not convention

LayerMechanismStatusFile
Network127.0.0.1:8787 only + TrustedHostMiddleware DNS rebinding defenseENFORCEDgate.py
Rate Limit60 req/min per IP, sliding window, thread-safe, 429 + audit on exceedHARDCODEDgate.py
Injection Filter33 OWASP patterns, config hot-reloadable, applied pre-LLM + corpus ingestACTIVEsanitizer.py
Telemetry KillLangChain/Chroma/OTel env vars blocked before any SDK importPRE-IMPORTgate.py
Grok Triple Gatemode=hybrid + grok.enabled=true + user_confirmed_online=true — all three requiredTRIPLEgraph.py
Soul WritesHuman reason + injection scan + atomic os.replace() + SHA-256 drift checkGOVERNEDpersonality.py
AuditSHA-256 hash + PII redaction on ALL 6 paths — mandatory convergenceMANDATORYlogger.py
/ops/* Isolation (v1.7)Loopback + API-key gated, subprocess.run(argv) only — hard module boundaryNEW v1.7ops_runner.py
05

Latest Changes — v1.7.0 (June 24 2026)

20 commits · Browser Ops + isolation boundary perfected

🎯 Headline: Browser Sync + Agentic Ops Consoles

Two new operator panels in static/terminal.html backed by loopback-only audited POST /ops/sync and POST /ops/agentic subprocess shims. sync/ and agentic/ are never imported by core — true out-of-band isolation. PR #239, 470 tests passing, adversarial review by 4 reviewers, zero in-scope defects.

Merge #239 · Browser Ops Consoles + ops_runner.py subprocess isolation
Chris · 14:22 · +12 files · 450+ tests · 4-reviewer adversarial sign-off
feat(ops): 4-gate Agentic Apply UI + skill body via --body-file temp
Dry-run default · --confirm required for writes · writes_enabled gate preserved
fix(sanitizer): expanded to 33 OWASP patterns (was 13) · corpus ingest filter added
Config hot-reload · [FILTERED] placeholder · applied pre-LLM
sec: torch==2.6.0+cpu CVE floor + OSV ignore entries + review dates documented
CPU-only offline threat model · documented rationale
chore: 470 passing tests · metrics.py scans audit.jsonl for drift events
CI + CodeQL + pip-audit green on every push
version bump: 1.5.0 → 1.7.0 · this infographic shipped as living architecture doc
Self-hostable · zero external deps · single file · copy-paste deployable
06

Browser UI Consoles · Interactive Demo

Functional simulation of the real 46KB terminal. Real invariants. Real palette.

v1.7.0 · PsyClaw terminal skin · 127.0.0.1:8787
RAG-FIRST ACTIVE OFFLINE
SYSTEM
CyClaw v1.7.0 ready · RAG-first enforced · 5 invariants active · audit logging on · 127.0.0.1:8787
QUERY
explain ops_runner isolation in v1.7
ANSWER · local_llm · route_score=0.031
ops_runner.py (v1.7) is a subprocess shim: handles POST /ops/sync and POST /ops/agentic by spawning child processes via subprocess.run(argv_list) only. It never imports sync/ or agentic/ as Python modules. This preserves the hard isolation boundary — core gate/graph code cannot be polluted by sync or agentic module state. Invariants 2 (Topology=Policy) and 4 (Audit Convergence) both verified.
⏱ 0.84s · tokens: 94 · model: lm-studio/local
Sources: docs/ops_runner.md (rrf 0.912) · graph.py:187 (rrf 0.874) · README.md (rrf 0.811)
Soul Console — personality.py
SHA: 0x9f2c4a... · v1.7.0 · last modified: 2026-06-24 · drift: none detected
SOUL LOG
soul v1.7.0 loaded · SHA-256 verified · no drift detected
Sync Console — POST /ops/sync (subprocess isolation)
Awaiting command · ops_runner.py · subprocess.run(argv_list) · never imports sync/ module directly
Agentic Console — POST /ops/agentic (4-gate apply UI)
Awaiting command · writes_enabled gate · --confirm required · dry-run default

Real terminal.html is a 46KB single-file operator console at 127.0.0.1:8787/static/terminal.html. This demo mirrors the invariants, flow, and four console panels exactly.

07

Version History (selected)

v1.7.0
Today · Browser Ops + Hard Isolation

ops_runner subprocess shim, Sync + Agentic consoles, 33-pattern sanitizer, 470 tests, this infographic. Version bump from 1.5.0.

v1.6.0
Triple Gate Hardening

Grok fallback now physically impossible without three simultaneous flags + explicit user confirmation in UI.

v1.4.0
RRF + Invariants Codified

Topology = Policy became non-negotiable. RAG-first enforced at graph entry. BM25 + Chroma RRF fusion introduced.

v1.0.x
Foundation

FastAPI gateway, LangGraph state machine, ChromaDB, soul governance, MCP server. Core architecture established.

08

Quick Stats · v1.7.0

470
Tests Passing
5
Invariants by Graph
0
Telemetry Events Ever
127.0.0.1
Binding Only
99.8%
RAG-First Compliance
33
OWASP Patterns
46KB
terminal.html size
20
Commits Today