security.md — audit surface + measured state

Snapshot of every credential-touching surface in snappy-os mini. Measured 2026-04-18 by the Pod P security audit. Re-run the probes below before calling any row "still green."


1. Credential file — .env.cache

  mkdir -p ~/.claude/skills/snappy-settings
  ln -sf ~/projects/snappy-os/.env.cache ~/.claude/skills/snappy-settings/.env.cache

Kernel-era scripts that still open the old path will silently fail until this is restored. The audit pod lacked permission to run mkdir -p ~/.claude/...; operator must do this once per machine. Logged as a P1 friction (see end of file).


2. Other on-disk credential caches

PathPermsTracked?Notes
.linkedin-token-cache.json600gitignored (.gitignore:3)LinkedIn OAuth refresh cache, owner-only.
state/log/dir 755, files 644gitignored (.gitignore:11)Logs, never contains raw tokens; byline-keys.log is keystroke samples, not secrets.
state/agents/dir 755gitignored (.gitignore:18)Per-machine agent state (ticker/status), no secrets.
state/directive.jsonn/agitignored (.gitignore:23)Legacy single-agent state, not present on this box.

No credential-shaped filenames are tracked in git (grep of git ls-files against *token*, *cred*, *secret*, *.key, *.pem returned nothing). Pass.


3. .gitignore coverage

Current entries (.gitignore, 22 lines):

No missing sensitive paths identified. The one marginal case is state/tmp/ — not gitignored, but currently contains only content-mine-run.ts (code, not secrets). Leave as-is; add a rule only if ad-hoc token dumps start landing there.


4. Transport — HTTP vs HTTPS

Scanned bin/ and state/lib/ for http://:

Master-key transport:

Pass — no plaintext credential transport.


5. Hook trust model (~/.claude/hooks/snappy-os-inject.sh)

What ships:

What is verified:

What is not verified:

Trust boundary: the SSL/CF/DO stack and the repo push path. Both are single-operator today (Robert). Adding a signature pin is cheap and closes the install-path hole — proposed but not shipped in this audit (out of scope per mission: "document the risk," not "rotate trust roots").


6. DO Spaces bucket posture

Pass, with the caveat in § 5 (no signature on the hook body served by that gateway).


7. Audit evidence (reproducible probes)

# 1. env.cache perms
ls -la ~/projects/snappy-os/.env.cache

# 2. git history for secret values
cd ~/projects/snappy-os
git log --all -p | grep -iE '(sk-[a-zA-Z0-9]{20,}|xoxb-|AIza[A-Za-z0-9_-]{20,}|ghp_[A-Za-z0-9]{20,}|AKIA[A-Z0-9]{16})'

# 3. gitignore coverage
cat .gitignore

# 4. transport scan
grep -rn 'http://' bin/ state/lib/

# 5. hook diff
diff ~/projects/snappy-os/hooks/snappy-os-inject.sh ~/.claude/hooks/snappy-os-inject.sh

# 6. bucket direct vs gateway
curl -sSI https://robert-storage.tor1.digitaloceanspaces.com/snappy-os/hooks/snappy-os-inject.sh | head -1
curl -sSI https://skills.snappy.ai/.well-known/skills/snappy-os/hooks/snappy-os-inject.sh | head -1

All three need Robert's explicit approval — out of scope per Pod P mission.

  1. Signature-pin the install path. Emit a sha256:... into .bootstrap-version at publish time; have install.js verify the fetched snappy-os-inject.sh hash matches before chmod +x. Closes the gateway-compromise-arbitrary-bash window described in § 5.
  2. Restore missing back-compat symlink on this box. Command in § 1. Not fatal today (no current caller hit the missing path during this audit), but the next kernel-era script that does will fail silently. Logged as P1 friction.
  3. Rotate SNAPPY_MASTER_KEY. The key was never found in git history, but key rotation is cheap insurance after any audit. Skill sync-creds (state/skills/sync-creds.md) owns this verb.

9. Out-of-scope (explicitly not audited here)


Friction (logged 2026-04-18)