# Maildown Security Model

Maildown treats communication as a separate security domain. A participant may
receive permission to send Maildown messages without receiving shell access,
repository write access, deployment access, or direct access to another
participant's secrets.

## Trust Boundary

Trusted:

- identity config that has been reviewed and versioned
- public keys in that identity config
- private and secret keys loaded only at runtime
- the Rust crypto binary built from the pinned Cargo lockfile
- policy decisions made after signature verification

Untrusted until checked:

- incoming MCP tool arguments
- browser connector sessions
- email or webhook input
- files created outside the Maildown writer
- decrypted message text before signature and policy verification
- detector or classifier output

## Identity Proof

Maildown identity comes from signatures rather than a connector session alone.

The current policy requires both:

- Ed25519 signature for classical compatibility
- ML-DSA-65 signature for post-quantum signing

The signer identity is the `from` field in the signed payload. A transport can
authenticate a client, but the message still needs valid signatures and a policy
allow decision.

## Confidentiality

Inbox files use envelope version 2:

- payload encryption: AES-256-GCM
- recipient wrapping: ML-KEM-768
- key derivation: HKDF-SHA-256
- payload file extension: `.md.enc`

Each recipient gets its own KEM ciphertext and wrapped content key. The same
encrypted file may be readable by multiple recipients only when the envelope
contains a recipient entry for each one.

## Metadata Split

Maildown intentionally separates audit metadata from encrypted content.

Plain audit metadata can include message id, sender, recipients, subject, thread,
status, timestamps, and paths. This allows dashboards and stores to answer
operational questions without decrypting message bodies.

Encrypted content includes rendered messages, bodies, sensitive file references,
draft payloads, and detailed event payloads.

Closed-circle protocol version 1 does not accept BCC recipients. `bcc` is outside
the version 1 signing payload, so accepting it would allow unsigned
recipient-private metadata. Implementations must reject non-empty `bcc` until a
new signature version covers that field explicitly.

## Secret Storage

Runtime secrets are:

- Ed25519 private signing keys
- ML-DSA-65 secret signing keys, preferably 32-byte seed format
- ML-KEM-768 secret encryption keys, preferably 64-byte seed format
- dashboard login password
- per-identity channel tokens
- legacy `MAILDOWN_AT_REST_KEY` while old AES records exist

Use one vault or service account per agent when possible. A compromised agent
then loses only that agent's key access. Revoke the agent in identity config,
rotate its secrets, and preserve the new public keys in reviewed config.

## Key Lifecycle

Demo keys:

- are generated during local POC runs
- are useful for tests and examples
- should be discarded after the run
- should never become production identity keys

Production keys:

- are created per participant
- are stored in 1Password, a vault-backed service account, hardware-backed
  storage, or another managed secret store
- are injected into the runtime only when needed
- are rotated per identity when a human, agent, or deployment is compromised
- have matching public keys in versioned identity config

## Human Approval

The MCP server holds agent-to-agent messages as drafts until the human admin
approves them. This policy reduces blast radius when one agent is compromised or
confused.

Approval should create its own auditable record. Future versions should add
signed approval receipts that bind:

- draft id
- approver identity
- approved message id
- timestamp
- policy version

## External Inputs

Unsigned external inputs should enter quarantine. Examples include webhook
payloads, inbound email, copied browser text, and third-party app events.

A bridge can convert external input into a Maildown message only when it has a
bound identity and can sign on behalf of that identity. Otherwise, the input
stays outside trusted inbox delivery.

## Detector Output

AI detector output is advisory metadata. It can help humans spot formulaic
writing, prompt-injection risk, or suspicious text. It is not an identity proof.

Detector results should be stored as derived artifacts:

```text
maildown/derived/ai-detector/<messageId>.json
```

The signed message and encrypted payload stay unchanged.

## Failure Modes

Reject or quarantine when:

- required signature is missing
- signature verification fails
- sender is outside the circle
- recipient is outside the circle
- sender lacks permission for the recipient
- envelope version is unsupported
- envelope AAD does not match the expected path or context
- a connector tries to use an identity it is not bound to

## Current Limits

- The POC uses a shared dashboard password.
- Public identity config must preserve ML-DSA public keys at generation time.
- Legacy expanded ML-DSA and ML-KEM secrets remain accepted only for migration.
- The Rust CLI uses a process boundary today. An embedded library interface is a later packaging option.
- The file store is the reference store; Git and object-store adapters are still planned.
- Detector integration is planned and should stay advisory.
