Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
8.4 KiB
Security model
What the kit touches, what it never touches, and the mitigations baked in.
Threat surface overview
| Risk | Where it lives | Mitigation |
|---|---|---|
| Memory-repo leaks session content | Sleep-sync pushes trace JSONL off-machine | Private repo enforced by wizard; [SENSITIVE-IP] sessions skip push entirely |
| Hardcoded tokens in source | Edits by agents / humans | secrets-guard Rust hook (PreToolUse Edit|Write) blocks known token shapes |
| Accidental push of flagged content | git push command |
Optional git-push-guard + leak-matrix pre-commit hooks (off by default; opt-in for users with sensitive private code) |
| Malicious GitHub Action tag re-point | .github/workflows/*.yml |
SHA-pinning + validate-workflow-shas.sh + actionlint in CI |
| S3 SSRF / IMDS credential exfil | kei-store with custom endpoint |
validate_endpoint rejects loopback / link-local / metadata hosts |
| Escape-sequence injection via brain name | keisei status / attach output |
Control-byte sanitiser on every manifest-sourced string |
Brain → $HOME pivot via symlink |
keisei attach <USB> |
Brain root rejected if symlink; mcp_server path must be relative + inside brain |
| SQLite WAL corruption on USB mount | keisei mount <exFAT drive> |
Runtime advisory; exFAT/FAT32 warning in USB guide |
Key mitigations in detail
Memory-repo should be private (your choice)
Sleep-sync pushes your session traces (prompts, tool calls, file paths, code snippets) to a git repo you control. /sleep-setup Phase 1 warns loudly on PUBLIC visibility. A public memory-repo leaks everything your agents have seen.
For users with sensitive private code: tag a session with [FLAG-LOCAL] in the prompt (or run from a CWD listed in ~/.claude/skip-sync-paths), and session-end-dump.sh skips the push entirely — local trace is kept, never leaves the machine.
Optional: pre-commit guard for users with private-IP
If your project has unpublished patents, confidential material, or otherwise-flagged content, kit ships leak-matrix (regex SSoT scanner) and an optional git-push-guard pre-commit hook template. Both are off by default — public-source projects don't need them.
To enable: copy _templates/leak-guard.sh.tmpl → your repo's .git/hooks/pre-commit, edit the term list, chmod +x. The hook scans staged files against your patterns and refuses commits that match. Override for a specific commit: set env LEAK_GUARD_BYPASS=1 + document the bypass reason in the commit body.
Secrets by reference only
secrets-guard Rust hook blocks hardcoded tokens at PreToolUse(Edit|Write). Every SSH key, API key, deploy token lives in ~/.claude/secrets/.env (chmod 600, gitignored) or per-project secrets/*.env.
Hook detects these token shapes:
| Pattern | Source |
|---|---|
sk-[A-Za-z0-9]{20+} |
OpenAI/Anthropic legacy |
sk-ant-[A-Za-z0-9_-]{40+} |
Anthropic current |
ghp_[A-Za-z0-9]{36} |
GitHub classic PAT |
github_pat_[A-Za-z0-9_]{82} |
GitHub fine-grained |
xoxb-[0-9]+-[0-9]+-[A-Za-z0-9]+ |
Slack bot |
[0-9]{8,10}:[A-Za-z0-9_-]{35} |
Telegram bot |
AKIA[A-Z0-9]{16} |
AWS access key |
-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY----- |
PEM private keys |
Bearer [A-Za-z0-9._-]{20+} |
generic bearer |
Allowlist (no false-positives): env references ($VAR, os.environ[...], std::env::var(...)), placeholders (YOUR_TOKEN_HERE, <redacted>), safe paths (*/secrets/**, *.env.example).
Bypass for emergency: set env SECRETS_GUARD_BYPASS=1 on the single call.
Supply-chain defences
All GitHub Actions in .github/workflows/ are pinned by full commit SHA (defends against CVE-2025-30066-class mutable-tag attacks).
scripts/validate-workflow-shas.shverifies every pin exists upstream viagit ls-remotescripts/install-actionlint.shchecks SHA-256 of the downloaded tarball before extractionscripts/lint-workflows.shrunsactionlintover every workflow file- CI job
workflow-lintruns all three on every push + PR (< 30 s) dependabot.ymlraises weekly PRs for SHA updates across github-actions, npm, and cargo ecosystems
S3 / R2 / MinIO hardening
kei-store::s3_cloud::validate_endpoint rejects loopback, link-local, and cloud-metadata hosts by default to close the SSRF / IMDS-credential-leak surface:
127.0.0.0/8,::1(loopback)169.254.0.0/16,fe80::/10(link-local)metadata.google.internal,metadata.aws.internal(cloud metadata)
Plain HTTP requires opt-in via KEI_STORE_S3_ALLOW_INSECURE=1. When a custom (non-AWS) endpoint is set, explicit access_key_env + secret_key_env are REQUIRED — the AWS default credential chain is not consulted for non-AWS endpoints (closes the "IMDS credentials leaked to unrelated endpoint" path).
Brain attach-marker is owner-only
~/.keisei/attached.toml is chmod 0o600 on unix (Windows unchanged — no equivalent bit). Every manifest-sourced string printed by keisei status / attach / mount / detach is scrubbed through display::sanitize_display, which replaces every ASCII control byte (< 0x20 or == 0x7F) with ?. Closes the escape-sequence-injection surface from a malicious brain.name like "evil\x1b[2Jpayload" that would otherwise clear the user's terminal or rewrite already-printed lines.
manifest.toml is capped at 64 KiB — fs::metadata check runs before read_to_string so an attacker-supplied 1 GB file can't exhaust memory inside the TOML parser.
Brain path & name validation
- Brain
mcp_serverpath MUST be relative + inside the brain root (rejects/usr/bin/curl,../../etc/shadow, Windows-style..\..\) - Brain
namematches^[a-z][a-z0-9_-]{0,63}$ - Brain root rejected if it's a symlink (blocks USB →
$HOMEpivot) - Adapters refuse to clobber existing
mcpServers.<name>entries — explicitNameConflicterror, no silent overwrite - All config writes go through
fsx::write_atomic_json(Windows-safe viatempfile::NamedTempFile::persist)
exFAT / FAT32 warning
SQLite WAL shared-memory mmap is unreliable on those filesystems; keisei mount (multi-client) WILL corrupt kei-memory / kei-artifact / kei-social-store DBs. Brain load prints an advisory when exFAT/FAT32 is detected via statfs(2). Single-client keisei attach on exFAT stays supported.
See USB-BRAIN-GUIDE-macos.md / -linux.md / -windows.md for APFS / ext4 / NTFS-native walkthroughs.
Battle-test matrix
Install-test battle matrix runs every profile against three base images before each release (tests/battle/):
| Image | Libc | Known quirks |
|---|---|---|
ubuntu:24.04 |
glibc | baseline; most widely deployed |
alpine:3.19 |
musl | exposes musl-static-link issues in rusqlite, git2, aws-sdk-s3 |
debian:12 bookworm |
glibc | different apt structure from Ubuntu |
Assertions per run: blocks ≥ 82, skills ≥ 43, top hooks ≥ 12, _lib hooks ≥ 2; hooks/_lib/test-gate.sh runs; settings.json validates. "Does it work on a fresh machine?" signal before every version ships.
See tests/battle/README.md for running locally.
Rule references
For the underlying discipline: these mitigations are driven by rules in the user's Claude Code CLAUDE.md. The relevant ones:
- RULE 0.4 — NO HALLUCINATION / CITATION VERIFY
- RULE 0.8 — SECRETS SINGLE SOURCE
- RULE 0.10 — RECURRENCE ESCALATE (same mistake ≥2× → codify via
/escalate-recurrence) - RULE 0.13 — ORCHESTRATOR BRANCH FIRST (agents write files; orchestrator owns git)
- RULE 0.14 — SESSION SELF-AUDIT
- RULE 0.15 — SLEEP LAYER (three-phase nightly consolidation)
Secret hygiene
This repository is public. The .gitignore actively blocks commits of:
.env/.env.*(except.env.example,.env.template)secrets/,**/secrets/- Key files:
*.pem,*.key,id_rsa*,id_ed25519*
If you accidentally stage a secret:
- Do not push. Drop it from the working tree immediately.
- Revoke the leaked credential at its provider dashboard.
- Rotate any adjacent credentials that may share the leak context.
- If already pushed to remote: rewrite history (
git filter-repo) and force-push is NOT safe on a widely-cloned repo; prefer revoke + rotate + new-commit-atop.
The canonical secret store for Claude Code is ~/.claude/secrets/.env
(chmod 600, RULE 0.8 in your personal umbrella). Project-specific
tokens live at <repo>/secrets/<name>.env — both are .gitignore'd.