docs: document two-key privilege model
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+81
-31
@@ -13,7 +13,8 @@ This manual is for **using and administering** `emcli`. It assumes you have the
|
||||
## Contents
|
||||
|
||||
1. [Key concepts](#1-key-concepts)
|
||||
2. [Setup: the encryption key and database](#2-setup-the-encryption-key-and-database)
|
||||
2. [Setup: encryption keys and database](#2-setup-encryption-keys-and-database)
|
||||
- [Privilege model](#2a-privilege-model)
|
||||
3. [Quick start](#3-quick-start)
|
||||
4. [Adding accounts](#4-adding-accounts)
|
||||
- [Gmail (app password)](#gmail-app-password)
|
||||
@@ -32,10 +33,12 @@ This manual is for **using and administering** `emcli`. It assumes you have the
|
||||
## 1. Key concepts
|
||||
|
||||
**Two kinds of commands.**
|
||||
- **Admin commands** (`init`, `account`, `whitelist`, `config`, `audit`, `doctor`) are for *you*,
|
||||
the human. They print human-readable text or open an interactive form.
|
||||
- **Agent commands** (`list`, `get`, `search`, `ack`, `send`) are for the *agent*. They print one
|
||||
line of JSON and nothing else, so a program can consume them reliably.
|
||||
- **Admin commands** (`init`, `account`, `whitelist`, `config`, `audit`) require `EMCLI_ADMIN_KEY`
|
||||
and are for *you*, the human. They print human-readable text or open an interactive form.
|
||||
- **Agent commands** (`list`, `get`, `search`, `ack`, `send`, `doctor`) require `EMCLI_KEY` (or
|
||||
`EMCLI_ADMIN_KEY` as a superset) and are for the *agent*. They print one line of JSON and
|
||||
nothing else, so a program can consume them reliably. (`doctor` prints human-readable text but
|
||||
is authorised by the agent key — the agent or a human with either key can run it.)
|
||||
|
||||
**Accounts** are named (e.g. `gmail`, `work`). The agent refers to an account by name and never
|
||||
sees its password.
|
||||
@@ -61,50 +64,91 @@ acking is a deliberate, separate step.
|
||||
|
||||
---
|
||||
|
||||
## 2. Setup: the encryption key and database
|
||||
## 2. Setup: encryption keys and database
|
||||
|
||||
`emcli` reads two environment variables:
|
||||
`emcli` reads three environment variables:
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
|---|---|---|
|
||||
| `EMCLI_KEY` | **Required.** Base64-encoded 32-byte key (AES-256) used to encrypt passwords at rest. | none — commands fail without it |
|
||||
| `EMCLI_ADMIN_KEY` | **Required for admin.** Base64-encoded 32-byte key (AES-256). Authorises ALL commands. | none — admin commands fail without it |
|
||||
| `EMCLI_KEY` | **Required for agents.** Base64-encoded 32-byte key (AES-256). Authorises agent commands only. | none — agent commands fail without it |
|
||||
| `EMCLI_DB` | Path to the database file. | `~/.config/emcli/emcli.db` (Linux/macOS), `%AppData%\emcli\emcli.db` (Windows) |
|
||||
|
||||
**Generate a key once** and keep it safe (store it the way the program/orchestrator that launches
|
||||
`emcli` expects — e.g. a secrets manager or your shell profile):
|
||||
**Generate both keys once** and keep them safe:
|
||||
|
||||
```bash
|
||||
head -c 32 /dev/urandom | base64
|
||||
export EMCLI_ADMIN_KEY="$(head -c 32 /dev/urandom | base64)" # you (human) keep this
|
||||
export EMCLI_KEY="$(head -c 32 /dev/urandom | base64)" # the agent launcher gets ONLY this
|
||||
```
|
||||
|
||||
Set it in your environment before running any command:
|
||||
|
||||
```bash
|
||||
export EMCLI_KEY='paste-the-base64-key-here'
|
||||
```
|
||||
|
||||
> **Important:** the key encrypts your account passwords. If you lose it, the stored passwords
|
||||
> can't be decrypted and you'll have to re-add accounts. If you change it, the same applies.
|
||||
> `emcli` never falls back to plaintext — a missing or wrong key makes every command fail safely.
|
||||
> **Important:** the keys protect your account passwords via envelope encryption (see "Privilege
|
||||
> model" below). If you lose `EMCLI_ADMIN_KEY`, account secrets can't be decrypted and you'll have
|
||||
> to re-add accounts. `emcli` never falls back to plaintext — a missing or wrong key makes every
|
||||
> command fail safely.
|
||||
|
||||
Account passwords are stored **encrypted**; they never appear in command output, error messages,
|
||||
or the audit log.
|
||||
|
||||
---
|
||||
|
||||
## 2a. Privilege model
|
||||
|
||||
`emcli` enforces a two-role privilege split so a process holding only the agent key cannot
|
||||
reconfigure accounts, whitelists, or audit settings.
|
||||
|
||||
### The two keys
|
||||
|
||||
| Key | Holder | Authorises |
|
||||
|---|---|---|
|
||||
| `EMCLI_ADMIN_KEY` | Human / secrets manager | ALL commands (`account`, `whitelist`, `config`, `audit`, `init`, plus all agent commands) |
|
||||
| `EMCLI_KEY` | Agent orchestrator | Agent commands only (`list`, `get`, `search`, `ack`, `send`, `doctor`) |
|
||||
|
||||
`EMCLI_ADMIN_KEY` is a strict superset: a process with only the admin key can run agent commands
|
||||
too. A process with only `EMCLI_KEY` is refused with `emcli: this command requires EMCLI_ADMIN_KEY
|
||||
(admin privilege)` if it attempts an admin command.
|
||||
|
||||
### Envelope encryption (DEK)
|
||||
|
||||
`emcli init` generates a random data-encryption key (DEK) that seals all account secrets. The DEK
|
||||
is stored in the `settings` table in two sealed copies:
|
||||
|
||||
- `dek_wrap_admin` — the DEK encrypted under `EMCLI_ADMIN_KEY`.
|
||||
- `dek_wrap_agent` — the DEK encrypted under `EMCLI_KEY`.
|
||||
|
||||
The DEK is never written in cleartext. Admin commands unwrap the DEK from the admin slot only; they
|
||||
have no fallback to the agent slot. This means a process holding only `EMCLI_KEY` cannot unlock the
|
||||
DEK for an admin command, even if it somehow knows the agent key.
|
||||
|
||||
### Command → role table
|
||||
|
||||
| Command | Role required |
|
||||
|---|---|
|
||||
| `list`, `get`, `search`, `ack`, `send`, `doctor` | Agent (`EMCLI_KEY` or `EMCLI_ADMIN_KEY`) |
|
||||
| `account`, `whitelist`, `config`, `audit` | Admin (`EMCLI_ADMIN_KEY` required) |
|
||||
| `init` | Both keys required (writes both wrap slots) |
|
||||
|
||||
### Agent launcher guidance
|
||||
|
||||
Configure your agent's orchestrator with **only `EMCLI_KEY`**. Never give the orchestrator
|
||||
`EMCLI_ADMIN_KEY`. If the agent tries to run an admin command — even by mistake — `emcli` will
|
||||
refuse it at the key level, not just by convention.
|
||||
|
||||
---
|
||||
|
||||
## 3. Quick start
|
||||
|
||||
```bash
|
||||
# 1. Set your key (see section 2)
|
||||
export EMCLI_KEY='…'
|
||||
# 1. Generate and export both keys (see section 2)
|
||||
export EMCLI_ADMIN_KEY="$(head -c 32 /dev/urandom | base64)" # keep this yourself
|
||||
export EMCLI_KEY="$(head -c 32 /dev/urandom | base64)" # give only this to the agent
|
||||
|
||||
# 2. Create the database and add your first account (interactive form)
|
||||
emcli init
|
||||
|
||||
# 3. Check it connects and authenticates
|
||||
# 3. Check it connects and authenticates (agent key is enough for doctor)
|
||||
emcli doctor
|
||||
|
||||
# 4. The agent can now read
|
||||
# 4. The agent can now read (needs only EMCLI_KEY)
|
||||
emcli list --account gmail --folder INBOX --limit 10
|
||||
```
|
||||
|
||||
@@ -491,10 +535,16 @@ emcli config get audit_retention_days
|
||||
## 11. Troubleshooting
|
||||
|
||||
**"EMCLI_KEY is not set" / "must be base64 of exactly 32 bytes".** Set `EMCLI_KEY` to a valid
|
||||
base64-encoded 32-byte key (section 2). Every command that touches the database needs it.
|
||||
base64-encoded 32-byte key (section 2). Agent commands (`list`, `get`, `search`, `ack`, `send`,
|
||||
`doctor`) need this key.
|
||||
|
||||
**A command fails to decrypt / "wrong EMCLI_KEY?".** The key doesn't match the one used when the
|
||||
account was added. Restore the original key, or re-add the account with the current key.
|
||||
**"this command requires EMCLI_ADMIN_KEY (admin privilege)".** Set `EMCLI_ADMIN_KEY` (section 2).
|
||||
Admin commands (`account`, `whitelist`, `config`, `audit`, `init`) need this key; `EMCLI_KEY`
|
||||
alone is not enough for them.
|
||||
|
||||
**A command fails to decrypt / wrong key.** The key doesn't match the one used when the database
|
||||
was initialised. Restore the original key, or re-run `emcli init` (idempotent — it won't regenerate
|
||||
the DEK if one already exists) with both correct keys, then re-add any accounts if needed.
|
||||
|
||||
**`doctor` shows `IMAP FAIL` / `SMTP FAIL`.**
|
||||
- *Invalid credentials / authentication failed* — wrong username or password. For Gmail, make sure
|
||||
@@ -528,7 +578,7 @@ running non-interactively.
|
||||
emcli # or: emcli help / emcli --help — list all commands
|
||||
emcli <command> --help # usage and flags for one command
|
||||
|
||||
# Admin
|
||||
# Admin (requires EMCLI_ADMIN_KEY)
|
||||
emcli init # create DB + add first account (form)
|
||||
emcli account add [flags | none for form] # add an account
|
||||
emcli account list # list accounts (no secrets)
|
||||
@@ -537,10 +587,10 @@ emcli account remove --name N --yes # delete an account
|
||||
emcli whitelist in|out add|remove|list --account N [--address A]
|
||||
emcli config set|get <key> [value] # e.g. audit_retention_days
|
||||
emcli audit list [--account N] [--limit K]
|
||||
emcli doctor [--account N] # connectivity/auth check
|
||||
emcli version
|
||||
|
||||
# Agent (one line of JSON each)
|
||||
# Agent (requires EMCLI_KEY or EMCLI_ADMIN_KEY; one line of JSON each)
|
||||
emcli doctor [--account N] # connectivity/auth check
|
||||
emcli list --account N [--folder F] [--new] [--limit K] [--before U] [--since U]
|
||||
emcli get --account N [--folder F] --uid U
|
||||
emcli search --account N [--folder F] [--from A] [--subject-contains S] [--text S] [--since-date D] [--before-date D] [--limit K]
|
||||
@@ -548,4 +598,4 @@ emcli ack --account N [--folder F] --uid-list U1,U2,U3
|
||||
emcli send --account N --to A [--cc A] [--bcc A] --subject S --body B [--attach P]… [--reply-to U [--folder F]]
|
||||
```
|
||||
|
||||
Environment: `EMCLI_KEY` (required, base64 32-byte key), `EMCLI_DB` (optional DB path).
|
||||
Environment: `EMCLI_ADMIN_KEY` (required for admin commands, base64 32-byte key), `EMCLI_KEY` (required for agent commands, base64 32-byte key), `EMCLI_DB` (optional DB path).
|
||||
|
||||
Reference in New Issue
Block a user