# Security

This document describes the security boundary for the memory core, DSH host
adapter, package contents, and repository deployment helper.

## Principles

1. **Vault content is data, not instructions.** All search output is wrapped
   in `<memory-data>` markers; every tool description states this. A vault
   that gets poisoned with malicious .md cannot inject instructions.
2. **Read-only canonical, single write inbox.** Agents never edit canonical
   notes directly; the only write path is 提交区 via memory_submit (validated:
   credential-shaped lines are rejected at submission).
3. **No plaintext credentials, ever.** Credential-shaped text (api_key=,
   token=, password=, sk-…, 32+ hex) is rejected at submission and redacted in
   all output (search/show) and before local or remote indexing. Store only
   label/location references. A schema upgrade rebuilds the local index so
   entries produced by an older redaction policy do not persist.
4. **Local-first.** The default index is on your machine; nothing leaves it
   unless you configure a remote endpoint yourself.
5. **Least privilege.** The core reads only the configured vault directory and
   writes only inside it (plus ~/.unified-memory.yaml and the per-vault local
   index at `~/.unified-memory/index-<vault-hash>.db`).
6. **Multi-writer safety.** Promotion takes a vault-level lock
   (<vault>/.lock, 30 s timeout, stale-lock breaking) and writes atomically
   (temp file + rename). Concurrent promoters wait or fail loudly — canonical
   notes are never half-written or overwritten.


## Deployment boundary

The repository deployer recognizes only existing allowlisted dsh, Codex, and
Claude instruction files. It never interprets document text as commands. The
safe sequence is `detect` → `preview` → review → `apply`; writes use a lock,
timestamped backup, temporary sibling, atomic replace, and post-write check.
`rollback` restores the newest backup. Missing capabilities, non-file targets,
and paths outside the allowlist fail closed. The deployer does not touch the
canonical vault, session archives, credentials, or unrelated runtime config.

## Threat model

In scope:

- **Vault poisoning** (someone writes malicious content into a note) →
  <memory-data> marker + tool descriptions treat results as data.
- **Credential leakage** (a fact line contains a secret) → submission
  rejection + redaction on every output path.
- **Concurrent promotion races** (two agents' promoters run at once) → file
  lock + atomic rename.
- **Accidental canonical edits** (an agent "helpfully" rewrites a note) →
  documented read-only boundary + AGENTS.md/CLAUDE.md templates + plugin tools
  that never write canonical.

Out of scope (by design):

- Protecting the vault from an agent that deliberately runs arbitrary file
  commands — any agent with shell access can already write anything.
- Remote-index transport security (only relevant when you configure one; use
  SSH/HTTPS and your own server, per DEPLOY.md).

## Remote index

- The optional remote server requires a Bearer token on every request
  (constant-time comparison). Use a long random token, stored in the OS secret
  store or an env var — never in the vault.
- Localhost is the default and safe. Beyond localhost the token prevents
  forgery but NOT eavesdropping: terminate TLS in front of the port.
- The client falls back to the local index (with a stderr note) if the server
  is unreachable or the token is rejected — an offline server never blocks the
  vault's facts.

## Session archive

`archive_session.py` (and any post-turn hook) redacts credential-shaped lines
before writing to `会话归档/`. Archived sessions are raw history, not
canonical facts; secrets never enter the vault in any form.

## Reporting

Community-maintained project. Security issues: open a GitHub issue; critical
vulnerabilities get priority attention.
