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
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.
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}}.
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.
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.
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
1---2name: veil3description: 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.4---56# VEIL — Privacy Layer for Hermes Memory78## When to Use910**Trigger this skill before writing to `MEMORY.md` or `USER.md`** when the11user's message contains any of the following categories of data:1213- Credentials: passwords, API keys, private keys, tokens, connection strings14- Identifiers: SSH keys, TLS certs, webhook secrets, database URLs with auth15- Personally identifiable information of third parties: full names paired16 with emails/phones, monetary amounts tied to named clients, addresses17- Anything the user explicitly marks as sensitive: "private", "secret",18 "don't tell anyone", "between us"1920Common triggers in user language:2122- "remember my X password is ..."23- "save my API key for ..."24- "my client Y paid Z amount"25- "don't forget the SSH key for staging is ..."2627**Do not trigger for**: generic environment facts ("I use Ubuntu 22.04"),28public project names, the user's own first name, or tool/library preferences.29These belong in plain memory.3031## Procedure32331. **Detect**: Parse the user's remember-request. Identify the sensitive34 span(s). If unsure whether something is sensitive, ask the user once —35 do not default to saving in plaintext.36372. **Seal**: Call `scripts/veil.py seal` with the sensitive value and a38 short human-readable label (e.g., `pg-prod`, `client-anna`). The script39 returns an opaque reference like `{{veil:pg-prod-2026a}}`.40413. **Write placeholder to memory**: Use the `memory` tool to save an entry42 containing the placeholder instead of the plaintext.4344 Good: `User's Postgres prod credentials stored in VEIL: {{veil:pg-prod-2026a}}. Host: db.example.com, port 5432.`4546 Bad: `User's Postgres prod password is hunter2.`4748 The non-sensitive scaffolding (host, port, which system it's for)49 stays in plaintext — the agent needs it to decide when to resolve.50514. **Resolve at execution time**: When the agent later needs the real52 value to run a command or make a request, call53 `scripts/veil.py resolve <reference>`. The plaintext is returned only54 to the tool execution context and only on the user's machine. Never55 write the resolved value back to `MEMORY.md` or echo it in a chat56 response.57585. **Audit (if enabled)**: If `veil.onchain_audit` is true, every `seal`59 and `rotate` operation automatically appends60 `SHA256(vault_state || timestamp)` to the Solana audit log. No user61 action needed.6263## Commands6465```66veil init one-time setup; creates vault, prompts for passphrase67veil seal <label> <value> returns {{veil:label-hash}}68veil resolve <reference> returns plaintext (local only)69veil list lists all references with labels, no values70veil rotate re-encrypt vault with new passphrase71veil migrate scan existing MEMORY.md, propose redactions72veil audit verify check local vault matches latest on-chain digest73veil audit publish manually publish current digest74```7576## Pitfalls7778- **Never log the resolved value.** If the agent prints plaintext into a79 chat response, a log file, or a terminal command visible to another80 user, the leak is real. Prefer piping into stdin of the target81 command: `veil resolve pg-prod-2026a | psql -h db.example.com ...`8283- **Placeholders are not semantically hidden.** An LLM seeing84 `User's Postgres prod credentials: {{veil:pg-prod-2026a}}` can infer85 the *category* of the secret. This is acceptable — the value is still86 protected — but don't claim VEIL hides the existence of credentials.8788- **Vault corruption is unrecoverable.** If `vault.age` is deleted or89 the passphrase is forgotten, references become dangling. Back up90 `vault.age` to an external location; it is encrypted, so backup91 exposure is not a leak.9293- **On-chain audit is opt-in for a reason.** It costs ~0.000005 SOL per94 operation and publicly reveals *that* the user updates their vault,95 even though it hides *what*. Users who don't want any network96 footprint should keep it off.9798- **Do not mix VEIL references across machines.** Each vault is a closed99 world. If a user copies `MEMORY.md` to a new machine without the100 matching `vault.age`, references cannot be resolved.101102## Verification103104After `seal`:105106```107veil list | grep <label> reference should appear108grep -r "<plaintext>" ~/.hermes/ should return nothing109```110111After `resolve`:112113```114veil resolve <reference> should return exact plaintext115```116117After on-chain audit publish:118119```120veil audit verify should report "OK: local digest matches chain"121```122123## References124125- `references/threat-model.md` — what VEIL does and does not protect against126- `references/crypto-choices.md` — why age, why Argon2id, why Solana for audit127- `references/usage-examples.md` — end-to-end examples for common workflows