# Veil

> Encrypt secrets and PII before they reach LLM providers via MEMORY.md. Intercept save-to-memory requests containing credentials, API keys, or personal data; replace sensitive payload with opaque reference; store real value in local age-encrypted vault. Agent resolves references locally at tool-execution time via veil_resolve.

- Skill: `zhekinmaksim/veil` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add zhekinmaksim/veil`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zhekinmaksim/veil/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: Zhekinmaksim (https://skillmd.com/u/zhekinmaksim)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/zhekinmaksim/veil

---


# VEIL — Privacy Layer for Hermes Memory

## When to Use

**Trigger this skill before writing to `MEMORY.md` or `USER.md`** when the
user's message contains any of the following categories of data:

- Credentials: passwords, API keys, private keys, tokens, connection strings
- Identifiers: SSH keys, TLS certs, webhook secrets, database URLs with auth
- Personally identifiable information of third parties: full names paired
  with emails/phones, monetary amounts tied to named clients, addresses
- Anything the user explicitly marks as sensitive: "private", "secret",
  "don't tell anyone", "between us"

Common triggers in user language:

- "remember my X password is ..."
- "save my API key for ..."
- "my client Y paid Z amount"
- "don't forget the SSH key for staging is ..."

**Do not trigger for**: generic environment facts ("I use Ubuntu 22.04"),
public project names, the user's own first name, or tool/library preferences.
These belong in plain memory.

## Procedure

1. **Detect**: Parse the user's remember-request. Identify the sensitive
   span(s). If unsure whether something is sensitive, ask the user once —
   do not default to saving in plaintext.

2. **Seal**: Call `scripts/veil.py seal` with the sensitive value and a
   short human-readable label (e.g., `pg-prod`, `client-anna`). The script
   returns an opaque reference like `{{veil:pg-prod-2026a}}`.

3. **Write placeholder to memory**: Use the `memory` tool to save an entry
   containing the placeholder instead of the plaintext.

   Good: `User's Postgres prod credentials stored in VEIL: {{veil:pg-prod-2026a}}. Host: db.example.com, port 5432.`

   Bad: `User's Postgres prod password is hunter2.`

   The non-sensitive scaffolding (host, port, which system it's for)
   stays in plaintext — the agent needs it to decide when to resolve.

4. **Resolve at execution time**: When the agent later needs the real
   value to run a command or make a request, call
   `scripts/veil.py resolve <reference>`. The plaintext is returned only
   to the tool execution context and only on the user's machine. Never
   write the resolved value back to `MEMORY.md` or echo it in a chat
   response.

5. **Audit (if enabled)**: If `veil.onchain_audit` is true, every `seal`
   and `rotate` operation automatically appends
   `SHA256(vault_state || timestamp)` to the Solana audit log. No user
   action needed.

## Commands

```
veil init                     one-time setup; creates vault, prompts for passphrase
veil seal <label> <value>     returns {{veil:label-hash}}
veil resolve <reference>      returns plaintext (local only)
veil list                     lists all references with labels, no values
veil rotate                   re-encrypt vault with new passphrase
veil migrate                  scan existing MEMORY.md, propose redactions
veil audit verify             check local vault matches latest on-chain digest
veil audit publish            manually publish current digest
```

## Pitfalls

- **Never log the resolved value.** If the agent prints plaintext into a
  chat response, a log file, or a terminal command visible to another
  user, the leak is real. Prefer piping into stdin of the target
  command: `veil resolve pg-prod-2026a | psql -h db.example.com ...`

- **Placeholders are not semantically hidden.** An LLM seeing
  `User's Postgres prod credentials: {{veil:pg-prod-2026a}}` can infer
  the *category* of the secret. This is acceptable — the value is still
  protected — but don't claim VEIL hides the existence of credentials.

- **Vault corruption is unrecoverable.** If `vault.age` is deleted or
  the passphrase is forgotten, references become dangling. Back up
  `vault.age` to an external location; it is encrypted, so backup
  exposure is not a leak.

- **On-chain audit is opt-in for a reason.** It costs ~0.000005 SOL per
  operation and publicly reveals *that* the user updates their vault,
  even though it hides *what*. Users who don't want any network
  footprint should keep it off.

- **Do not mix VEIL references across machines.** Each vault is a closed
  world. If a user copies `MEMORY.md` to a new machine without the
  matching `vault.age`, references cannot be resolved.

## Verification

After `seal`:

```
veil list | grep <label>            reference should appear
grep -r "<plaintext>" ~/.hermes/    should return nothing
```

After `resolve`:

```
veil resolve <reference>            should return exact plaintext
```

After on-chain audit publish:

```
veil audit verify                   should report "OK: local digest matches chain"
```

## References

- `references/threat-model.md` — what VEIL does and does not protect against
- `references/crypto-choices.md` — why age, why Argon2id, why Solana for audit
- `references/usage-examples.md` — end-to-end examples for common workflows

