Skip to content
cgfixitPublic

About

CyClaw is a secure, offline-first local AI agent LangGraph topology w/ NeMo Guardrails and audit-friendly

Topics

Resources

Security policy

Stars

6 stars

Watchers

1 watching

Forks

Latest commit

 

History

4,012 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CyClaw

Python 3.12 FastAPI LangGraph CyClaw CI/CD testing

CyClaw console answering from the local library

CyClaw answers questions from your documents on your hardware. Its 12-node LangGraph starts with retrieval, ends every path in the audit log, and requires per-question consent for paid Grok or Claude fallback. The server binds to 127.0.0.1:8787.

  • Embeddings, BM25, and the cross-encoder run locally on CPU; cache both retrieval models before going offline.
  • Soul, ops, memory, and audit routes require operator access, which fails closed without CYCLAW_API_KEY (an enabled admin's login also works when per-user auth is on).
  • Audit records hash questions by default; the spend ledger records billed tokens; cyclaw-metrics reads both offline.
  • Guardrails (NeMo in every base install), the Numbat stream, and the spend ledger ship on. Auth, memory, connectors, the agentic loop, and Telegram/X channels ship off.

CyClaw serves one trusted operator, or a mutually trusted group with auth enabled; it provides neither tenant isolation nor a microVM (threat model).

Step-by-step installs and every REST call: Full Setup Guide. Contributor rules for people and agents: CLAUDE.md, AGENTS.md, and the tracked git-hook gate in docs/GITHOOKS.md.


Quick Start

macOS (Apple Silicon) — install, keys, Ollama, index, and startup:

git clone https://github.com/cgfixit/CyClaw && cd CyClaw
bash macos/setup-cyclaw.sh

Linux — Ollama already running on 127.0.0.1:11434:

git clone https://github.com/cgfixit/CyClaw && cd CyClaw
python3.12 -m venv .venv && source .venv/bin/activate
pip install --require-hashes --no-deps -r locks/requirements-torch-lock-linux.txt --index-url https://download.pytorch.org/whl/cpu
pip install --require-hashes -r locks/requirements-lock-linux.txt
pip install --require-hashes -r locks/requirements-test-lock-linux.txt
ollama pull qwen3.8:27b-mlx
export CYCLAW_API_KEY="$(openssl rand -hex 20)"  # operator routes; /query uses sessions/tokens when auth is on
python -m retrieval.indexer                      # once; without this, /query is 503
python gate.py                                   # http://127.0.0.1:8787

Windows uses the same pins with the -windows lock files (py -3.12 -m venv .venv, .\.venv\Scripts\Activate.ps1): Windows, Linux.

Confirm: curl http://127.0.0.1:8787/health, then open http://127.0.0.1:8787/.

Updating an existing environment? Rerun the install, then the NeMo verification. A manual macOS install uses plain torch==2.13.0 (no +cpu wheel) then the hashed macOS lock (macOS (Apple Silicon)).


For newcomers

Python and install paths. Use Python 3.12 (>=3.12,<3.13), not an unversioned python3 that may resolve to 3.11; .venv needs no admin rights. Choose native macOS, Windows, the Linux Quick Start, Docker (linux/amd64, published on 127.0.0.1 only), or conda (environment.yml). The constrained editable install in the setup guide adds the cyclaw-* commands; python -m … always works.

Secrets. gate.py reads environment variables, never dotenv files (Invoke-CyClaw.ps1 reads only non-secret settings from an owner-only %USERPROFILE%\.CyClaw\.env or checkout dotenv).

Secret persistence.

Platform Setup Operation Default secret store Plaintext opt-in
macOS macos/setup-cyclaw-keys.sh Set up or preserve gateway key Keychain --write-env-file
Windows powershell/Install-CyClaw.ps1 Migrate existing plaintext secrets Credential Manager -WriteEnvFile

On Linux, macos/invoke-cyclaw.sh stores the gateway key in libsecret or an owner-only file (macos/cyclaw-linux-key.sh). Services read secrets at execution through macos/cyclaw-keychain-env.sh or powershell/CyClaw-CredMan-Env.ps1, never from dotenv, plist, task XML, config.yaml, shell rc files, or argv; a configured keystore lookup fails closed when its item is missing. A missing provider key leaves that provider unavailable, not the server down (provider keys).

Offline and hybrid. Shipped app.mode: hybrid permits a paid call only when the selected provider is enabled, available, and confirmed for that one request; confirmation is never persisted. Set app.mode: offline or decline to stay local. Model downloads are separate from that consent: models.embeddings.offline_after_index: true forces local files after indexing, and the ~91 MB reranker loads on the first query, so cache it first.

Ports. Gateway 127.0.0.1:8787 (api.port, launcher override CYCLAW_GATE_PORT), Ollama 127.0.0.1:11434. The old :8790 coding console is now the separate CG-agent-harness.

First-run checks and limits.

  • Without an index, /query returns 503 INDEX_NOT_FOUND. Run python -m retrieval.indexer, or click Build my library in the console (POST /index/build, loopback + same-origin + no forwarding headers, then GET /index/status); once CYCLAW_API_KEY is set a locked build opens the unlock dialog and retries after the unlock.
  • The console header shows the mode in plain words ("Cloud fallback · ask first" or "Offline only") and Library/Engine health chips. /health reporting degraded usually means Ollama is down; TELEMETRY KILL at startup is expected.
  • The 60/min per-IP rate limit resets on restart unless api.rate_limit.persist_path or a Postgres DSN is set. /health and /index/status are public and not rate-limited. Request bodies above security.max_request_body_bytes (1 MiB) get 413.
  • Browser CORS allows http://127.0.0.1:8787 and http://localhost:8787; /query always rejects cross-site requests.
  • Restart the gateway after editing sanitizer config (it is cached). CYCLAW_EMBED_CACHE_SIZE (default 2048) is an env var, not a YAML key.

Check policy offline with python .claude/skills/invariant-guard/check_invariants.py; the unit suite is GROK_API_KEY=dummy python -m pytest tests/ -q --tb=short with no live provider.

Local records. Paths are relative to the repository.

Path Git status Contents and behavior
logs/audit.jsonl ignored Authoritative audit, written synchronously on the request thread. Questions use persistent HMAC-SHA256 fingerprints by default, including when logging.audit_fields is absent or empty. Explicit include_query_hash: false stores redacted query text
logs/spend.jsonl ignored Tokens for billed Grok/Claude calls, without query text or prices
logs/numbat-events.ndjsonl ignored Derived redacted audit and out-of-band events; observation only
logs/cyclaw.log ignored Application log; a bounded writer drops records rather than holding a request on stalled I/O
logs/evals/ ignored Opt-in dogfood and judge output, separate from production billing
index/ ignored Chroma and bm25.json; rebuild after corpus changes
data/personality/soul.md tracked Changes appear in git status and broad staging
data/personality/cyclaw_soul.db ignored Version DB; not yet generated in a clean checkout
data/personality/soul.md.bak ignored Vetted backup; not yet generated in a clean checkout

Day-two:

python -m metrics                           # spend, audit aggregates, sequences
python -m retrieval.clear_cache             # dry-run; --apply deletes .emb_cache
python -m retrieval.indexer                 # rebuild after editing data/corpus/
curl -s http://127.0.0.1:8787/index/status   # build progress

What It Does

CyClaw retrieves Markdown and .txt documents before generation. Output rails enforce a token-overlap floor on retrieved local answers; that heuristic does not verify individual claims, which the evals measure.

  1. Retrieval first. retrieve starts the 12-node graph; a miss can still reach offline_best_effort with partial context.
  2. Hybrid search. ChromaDB and BM25 fuse through RRF (retrieval.rrf_k). A vault hit needs the best cosine to clear retrieval.min_semantic_score (without cosines, the fused score must clear the RRF-scale retrieval.min_score); the cross-encoder runs in shadow mode (retrieval.min_rerank_score: null). Retrieval reference.
  3. Local generation. Ollama runs models.local_llm.model, shipped as qwen3.8:27b-mlx. Tunables live in config.yaml.
  4. Governed soul. data/personality/soul.md has SHA-256 drift detection and atomic writes; POST /soul/apply requires a human reason and an enforced injection scan. Restore, /soul/reload, and startup drift recovery are the documented unscanned or advisory exceptions, and a missing soul self-initializes (Soul invariants, Rules 4–5).
  5. Confirmed external fallback. Grok (grok-4.5, api.x.ai) or Claude (claude-sonnet-5, api.anthropic.com) needs hybrid mode, that provider enabled and selected, user_confirmed_online: true on the request, and a key. Calls share the remaining api.graph_timeout_sec budget, and utils/endpoint_trust.py rejects rewritten provider URLs.
  6. HTTP and MCP. FastAPI serves the browser at /; the separate MCP server exposes sanitized retrieval only (sampling: None, no generation).
  7. Audit convergence. Every path converges at audit_logger before END; cyclaw-metrics reads the audit offline.

The six invariants cover retrieval-first entry, topology, external consent, audit convergence, soul governance, and import isolation (core modules never import agentic, sync, guardrails, telegram, or opentweet; the invariant checker enforces it). INVARIANTS.md states Rules 1–9 and the test behind each.

Storage and console. indexing.vector_backend defaults to embedded chroma (optional pgvector); BM25 is JSON, never pickle. static/terminal.html provides queries, the first-run build panel, health chips, Soul, Sync, Agentic, Filesystem, and SQL panels, plus Users and Audit when auth is on.

Optional layers at a glance

Layer Purpose Ships
Per-user auth Passwords, sessions, device tokens, roles off
Memory SQLite/FTS5 facts and episodes, propose/apply, optional retrieval fusion and HTML export off
NeMo Guardrails Deny-only input/output checks, deterministic fallback on NeMo failure on
Dropbox sync Out-of-band rclone corpus pull CLI
Connectors Scoped filesystem, SELECT-only SQL, passive LAN inventory off
Agentic loop GitHub context, skills, clone/plan/patch/verify, human decisions off
Telegram / OpenTweet Phone remote and X drafts through loopback /query off
Numbat stream / pre-action hook Derived NDJSON, observation only; deny-only gate before a confirmed external call on / off
Spend ledger Billed token counts on
Fine-tune kit Separate QLoRA toolkit toolkit

Architecture

gate.py initializes the soul at startup. Requests pass the body cap, Host allowlist, any required authentication, the 60/min per-IP limit (before injection filtering), and the config-driven filter, then enter the graph as GraphState, where edges control routing.

flowchart TD
    A(["Client\nHTTP POST /query"])
    A --> B

    subgraph GATEWAY ["gate.py — FastAPI 127.0.0.1:8787"]
        B["TrustedHostMiddleware\nHost header allowlist"]
        B --> C["Rate Limiter\n60 req/min per IP"]
        C --> D["Prompt Injection Filter\n40 patterns · config-driven"]
        D --> E["Build GraphState\nquery + user_confirmed_online"]
    end

    E --> F

    subgraph GRAPH ["graph.py — LangGraph 12-node State Machine"]
        F(["retrieve\nChroma + BM25 + RRF"])
        F --> G["route_by_score\nbest cosine ≥ 0.30?\n(RRF ≥ 0.028 if no cosine)"]
        G -->|"YES — local context"| X["guardrail_input\noffline rail · default on"]
        X -->|"blocked"| L
        X -->|"passed · high score"| H["local_llm\nOllama :11434"]
        G -->|"NO — vault miss"| I["user_gate\nneeds_confirm = true"]
        I -->|"confirmed + hybrid\n+ grok.enabled + provider=grok"| PG["pre_action_hook_grok\ndeny-only · off = pass-through"]
        PG -->|"allow"| J["grok_fallback\ngrok-4.5 · triple-gated"]
        I -->|"confirmed + hybrid\n+ claude.enabled + provider=claude"| PC["pre_action_hook_claude\ndeny-only · off = pass-through"]
        PC -->|"allow"| W["claude_fallback\nclaude-sonnet-5 · triple-gated"]
        I -->|"declined or offline"| X
        X -->|"passed · vault miss"| K["offline_best_effort\nlocal LLM · no RAG gate"]
        I -->|"confirmed=None — pause"| L
        H --> Y["guardrail_output\noffline rail · default on\ngrounding: local_llm only"]
        J --> Y
        W --> Y
        K --> Y
        Y --> L
        PG -.->|"deny"| L
        PC -.->|"deny"| L
        L(["audit_logger\nHMAC fingerprint · PII redact\nlogs/audit.jsonl\n+ derived Numbat stream"])
    end

    L --> M(["QueryResponse\nanswer · sources · model_used\nretrieval_mode · needs_confirm"])

    subgraph RETRIEVAL ["retrieval/hybrid_search.py"]
        N["ChromaDB\nsemantic · 384-dim cosine"]
        O["BM25Okapi\nkeyword · Porter stemming"]
        P["RRF fusion\nk=60 · equal weighting"]
        N --> P
        O --> P
    end

    F <-->|"hybrid search"| P

    subgraph SOUL ["utils/personality.py"]
        Q["soul.md\nSHA-256 drift detection"]
        R["SQLite / Postgres\nversion history"]
        Q <--> R
    end

    H <-->|"soul preamble\n≤ 8000 chars"| Q
    K <-->|"soul preamble"| Q
Loading

The MCP server calls the retriever directly after sanitization and never enters this gateway or graph. The diagram omits the per-answer NeMo check() and the post-graph CEL monitor (Optional layers).


API Key Setup (Soul Mutations)

/soul/*, /ops/*, /memory/*, /query/export/html, and /audit/summary require operator access. /health is public; /query requires a session or device token when auth.enabled is true. Operator credentials are:

  • Bearer CYCLAW_API_KEY, for HTTP clients and scripts (compared with hmac.compare_digest).
  • The console cookie. "Unlock operator tools" trades the key once for an HttpOnly, SameSite=Strict cookie (security.console_session_ttl_sec, 1 h shipped); "Lock" deletes it, and rotating the key revokes every cookie.
  • An admin login, when auth.enabled is on (no key needed); operator and audit accounts do not get operator access.

Cookie writes need CSRF tokens, cookies reject cross-site requests, console tokens bind the request origin, and TLS uses __Host- cookies. Cookies are not port-scoped, so give CyClaw its own hostname plus TLS when other services on the host are untrusted. Without CYCLAW_API_KEY, Bearer and cookie access fail closed.

macos/invoke-cyclaw.sh and powershell\Invoke-CyClaw.ps1 generate a missing key into the OS keystore and open a single-use #pair=... unlock link (5 minutes). To unlock by hand, copy the key into the dialog:

# macOS
security find-generic-password -a "$(whoami)" -s com.cgfixit.cyclaw.api-key -w | pbcopy
# Windows, from the CyClaw folder
. .\powershell\CyClaw-SecretStore.ps1; (Read-CyclawCredential com.cgfixit.cyclaw.api-key).Secret | Set-Clipboard

Do not persist the key with setx or a user environment variable; the launchers read it from the keystore into the gateway process only (powershell/README.md). Rotation, the macOS bootstrap, and 401 recovery: macos/README.md.

security.api_key_optional ships false; when set, a request skips the key only if the socket peer is loopback, no forwarding header is present, and the request is not cross-site (inert under Docker NAT). Full boundary: INVARIANTS.md Rule 6 and the threat model (fourteenth amendment).

Per-User Authentication

Separate from the operator API key. gate_auth.py: scrypt passwords, a session cookie plus CSRF for browsers, named device tokens for scripts, roles admin, operator, and audit. Ships auth.enabled: false; while off, every /auth/* route returns 503. Design, TLS, and the non-loopback bind rule: docs/AUTHENTICATION_DESIGN.md; first-boot curl and cyclaw-user: setup-guide.md. cyclaw-gen-cert writes the self-signed cert for the auth + TLS bind exception.


Spend Tracking

Billed Grok/Claude calls append tokens to logs/spend.jsonl; dollars are computed at read time, so rate-card fixes re-price history. Rows exclude queries, prompts, and keys, and a failed write drops the row, not the answer. Core Grok/Claude requests also reserve a durable call allowance before each POST attempt, retries included: 100 per UTC day and 1,000 per UTC month by default (zero denies all). Optional agentic providers are excluded, and dollar spend is not enforced.

source Writer Covers
query llm/client.py Confirmed /query fallback
agentic agentic/deepagent_github/chat_client.py Out-of-band planner calls
eval tests/judge_eval.py Opt-in judge runs, kept separately under logs/evals/

python -m metrics reports today and last_7d tokens and USD by provider and source, and its forensic Sequences section flags a blocked injection followed within 15 minutes by a paid call (no policy enforced). tests/spend_live_probe.py spends real money and is opt-in only. Ledger schema, rate bands, and probes.


Benchmarks and Evals

Only the retrieval gate blocks merges; none of these four paths is a graph node or a security control. Thresholds and limits.

Evaluation Command Scope
Retrieval gate python -m tests.ci_rag_smoke Every PR, no LLM. Corpus probes through route_by_score_node, then hit@5, Recall@5, and MRR on the 52-case fixture (44 scored, 8 out-of-corpus skipped). Metric floors and [FILTERED] injection-chunk assertions block CI
Local dogfood CYCLAW_EVAL_DOGFOOD=1 python scripts/cyclaw-eval-dogfood.py Opt-in real loopback model, one case per category (recipe)
Anthropic judge CYCLAW_EVAL_LIVE=1 python tests/judge_eval.py Paid opt-in Claude grades groundedness, completeness, abstention
Local judge Same command with evals.local_judge.enabled: true Same rubric on a second loopback model from another family

Fixture retrieval scores 1.0 on all three metrics (no claim about arbitrary corpora). The reranker bake-off found no passing threshold, so vetoing stays off. The only published local run is the Qwen dogfood audit; no judge result is published yet.


Current development

Checked against origin/main at 2d7191a on 2026-10-08. Recent work has gone into the tracked git-hook secrets gate (docs/GITHOOKS.md), sandboxed agentic verification, hashed installs, credential and request-body hardening, and console first-run fixes; see merged PRs for details.


Optional layers

Dropbox sync. rclone pulls into data/corpus/ outside the request path, bounded by max_delete, max_transfer, and a single-instance lock; exit code 10 signals a reindex. python -m sync.cli setup, then test, sync --dry-run, sync, status, schedule, or unschedule (guide, CLI).

Native scheduling. Generators write plist/task files and print load commands; token-bearing jobs use the keystore wrappers, and a supervised gateway (macos/generate_service_plist.py, windows/generate_service_task.py) requires --confirm and a non-empty --reason (macOS operations, launchd design).

Fine-tuning. The QLoRA kit is excluded from runtime installs and audited separately; finetune_qwen38.py may download its base checkpoint, so seed caches before a no-egress run. GPU-free dry run: python tools/lora_finetune/build_cyclaw_corpus.py, then python tools/lora_finetune/dryrun_finetune.py (toolkit).

Agentic coding. agentic.enabled: false makes the out-of-band CLI a no-op; writes also need a per-call reason and confirm, and allow_git_write_tools ships false. python -m agentic.cli real-repo-run clones into a jail, plans, patches, and verifies, then waits for a human decision before committing; push and draft PR are separate decisions. Checks run in fresh gitless copies under Linux bubblewrap or macOS Seatbelt and fail closed without them (Windows refuses); no platform uses a microVM (threat model). real-repo-run* is CLI-only (POST /ops/agentic returns 422).

python -m agentic.cli status
python -m agentic.cli context --repo          # also --pr 123 / --issue 45

Real-repo loop, governed harness, and write rollback.

Connectors. All three ship off, outside the request path: fsconnect (bounded reads; writes gated, refused on Windows), sqlconnect (SELECT/WITH only), and netconnect (passive, explicit RFC1918/loopback CIDRs). Filesystem, SQL, and passive network.

NeMo Guardrails

guardrails.enabled: true ships in config.yaml; explicit false or an absent block disables it. utils/guardrail_bridge.py is the graph's only path to guardrails/.

Guard Scope and refusal
Offline graph input Local and best-effort paths; returns block_message through audit_logger without a model call
Offline graph output local_llm only; replaces answers below hallucination_threshold (0.18) or matching soul-leak markers
NeMo check() Wraps all four answer nodes (nemoguardrails==0.24.0); input refusal skips generation, output refusal replaces the answer
Broker fallback Missing, failed, or unsupported live verdicts run deterministic input and soul-leak checks on every answer route; grounding stays local_llm only

Active rails are Python checks and add no model calls; NVIDIA's model-assisted self_check_* rails are inactive. NeMo failures audit guardrail_degraded. Existing environments: rerun the install, then python -m pip check and python -m guardrails.verify_install (setup guide, package guide, NeMo reference, Track B record).

Numbat

CyClaw calls the external CLI pinned at 0.2.0 (schema 0.3.0); it never vendors or imports it.

Piece Switch Default and behavior
Stream numbat.enabled On. utils/numbat_emitter.py writes redacted audit/out-of-band events to logs/numbat-events.ndjsonl (rolls at 50 MiB); its bounded writer cannot hold requests
Pre-action hook policy.fallback.pre_action_hook.enabled Off. Prepared with engine: numbat and monitor-only rules in config/numbat/gate/; after consent, rules test --no-builtin-rules denies enforcing matches and every engine failure
CEL monitor numbat.cel.enabled, extra numbat-cel Off. Two structured-field rules observe weak-retrieval cloud answers and hook/guardrail refusals after /query; never blocks
Offline scoring None Operator CLI and numbat-rules.yml; fixture, stream-contract, and CEL checks block CI

Nothing scores the live file during a request. The CLI and CEL extra are not in standard installs, so keep their switches off until installed (an enabled hook without the binary denies every confirmed external call), trial rules before promoting any to enforce: true, and never use numbat hook as the command engine (it exits 0 on errors). Stream events include hostname, user, and uid. Once enabled, /health reports hook and CEL readiness and /audit/summary shows pre_action_hook_last_verdict. Pre-action gate, stream, phase status, and the Track A acceptance record.

Telegram and OpenTweet

Both ship off and call loopback POST /query outside the core graph imports, reading credentials from the environment variable named in config. Telegram offers outbound notify or long-poll chat (no public webhooks), requires non-empty allowed_chat_ids, and can confirm one paid call only through the exact /online on <grok|claude> command with allow_hybrid_confirm on (default off); the triple gate still applies. OpenTweet forces user_confirmed_online: false and writes drafts; scheduled_date needs opentweet.schedule_enabled, and schedulers never send publish_now. Check either with python -m telegram.cli status / python -m opentweet.cli status. Telegram design and operations; OpenTweet design and operations.


Security Model

Layer Mechanism
Network Binds 127.0.0.1:8787; request bodies capped at 1 MiB (security.max_request_body_bytes). A non-loopback api.host is refused except the documented auth + TLS path, or CYCLAW_ALLOW_NON_LOOPBACK_BIND (bind guard)
Endpoint trust Local nodes: loopback or an exact host in models.local_llm.trusted_hosts (ships []). Online nodes: api.x.ai and api.anthropic.com only
Input policy.prompt_filter: 40 banned_patterns, max_input_chars; same filter on MCP search
Rate limit 60 req/min per IP, after same-origin rejection and before the filter; in-memory unless SQLite or Postgres is set
Proxy bypass httpx clients set trust_env=False
Telemetry Kill maps before any SDK import (invariant-guard G1), plus ONNX's post-import call; not a network firewall (SECURITY.md)
Audit HMAC-SHA256 query fingerprint + redacted metadata in logs/audit.jsonl, then the derived Numbat stream
Grok / Claude Triple gate item 5, then the opt-in deny-only pre-action hook
Soul writes Human reason + enforced scan + atomic replace on POST /soul/apply only (What It Does item 4 lists the exceptions)
API key Fail closed: Bearer key, CSRF-guarded console cookie, or an admin login; the opt-in loopback bypass is peer-bound (API Key Setup)
Guardrails On by default, deny-only, deterministic on NeMo failure (NeMo Guardrails)
Optional surfaces Agentic writes, connectors, channels, and /memory/* ship off; /ops/* needs operator access and runs subprocess argv lists; no tokens in plist or task XML
/auth/* Present either way; 503 while auth is off. When on, /query needs a session or device token and refuses audit accounts; the last admin cannot be removed
Container Non-root, no-new-privileges, dropped caps, read-only rootfs; optional Falco (deploy/falco/) ships off
Dependency risk chromadb==1.5.9 carries CVE-2026-45829, accepted only for embedded PersistentClient. SECURITY.md
Commit-time gate Tracked .githooks/ refuse secrets and private data at commit and push, protected-path edits at commit (reminder only at push), and main/force pushes without an operator override (docs/GITHOOKS.md)

Full threat model: docs/THREAT_MODEL.md. Design notes: docs/security-philosophy/.


Project Structure

CyClaw/
├── gate.py  gate_ops.py  gate_auth.py  gate_memory.py   # gateway, /ops, /auth, /memory
├── graph.py                # 12-node LangGraph
├── metrics.py              # cyclaw-metrics: audit + spend + sequences
├── mcp_hybrid_server.py    # retrieval-only MCP
├── config.yaml             # tunables
├── retrieval/  llm/        # hybrid search, embeddings, rerank; local, Grok, Claude clients
├── utils/                  # sanitizer, logger, personality, spend, numbat_*, endpoint_trust, auth
├── guardrails/             # default-on rails, reached only via guardrail_bridge
├── memory/  agentic/  sync/  telegram/  opentweet/   # optional layers, off or CLI-only
├── macos/  powershell/  windows/                     # native installers and schedulers
├── spend/  schemas/  static/  tests/  docs/  deploy/
├── tools/lora_finetune/    # operator QLoRA kit, not a runtime extra
└── .github/workflows/

data/corpus/ is the sample corpus, data/personality/soul.md the live soul; data/agentic/skills_registry.json ships empty.


Documentation Map

Docs each section above links are not repeated here.

Read this When you want
setup-guide.md Install paths, Docker, conda, and every REST call
INVARIANTS.md Rules 1–9, code vs convention, and the test that pins each
CLAUDE.md / AGENTS.md / docs/GITHOOKS.md Request-path map, agent rules, and the commit/push security gate
SECURITY.md / docs/THREAT_MODEL.md Egress, accepted CVEs, disclosure; scope, bind exceptions, amendments
docs/AUTHENTICATION_DESIGN.md / docs/DOCKER.md Per-user auth; GHCR image and compose hardening
docs/security-philosophy/ Telemetry suppression, offline operation, and Numbat designs
macos/README.md / powershell/README.md Keychain, Credential Manager, 401 recovery

License

Source-available, all rights reserved; personal use is permitted. See LICENSE.

Designed and built by Chris Grady, with AI tooling used under human review, CI, and the invariant guard.

About

CyClaw is a secure, offline-first local AI agent LangGraph topology w/ NeMo Guardrails and audit-friendly

Topics

Resources

Security policy

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages