# Setup and Operation Guide — PMP Reference Node
This guide covers installing the reference node, what it puts on disk, a tour of
every CLI command, running the demo and tests, and troubleshooting.
## 1. Requirements
* **Python 3.10 or newer.** The code uses modern typing and the standard-library
`zoneinfo`/`datetime` machinery; older interpreters are not supported.
* **No external services.** The node is entirely local: storage is SQLite (via
the standard library), and cryptography is provided by the `cryptography`
package (Ed25519), declared in `pyproject.toml`.
* Linux, macOS, and WSL are tested targets. Native Windows should work but key
files cannot be permission-restricted the same way (see §7).
## 2. Installation
From a clone of the repository:
```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]" # dev extra adds pytest
```
This installs the `pmp` console script. You can equivalently invoke everything
as `python -m pmp …`.
Verify the install:
```bash
pmp --help
```
## 3. What a node is on disk
A node is just a directory. `pmp init --node-dir
` creates:
```
/
keys/ # node identity keys (Ed25519)
node.key # private key — mode 0600, never leaves the machine
node.pub # public key — safe to share; identifies this author
oplog.db # SQLite database holding the append-only operation log
node.json # node metadata: node id, schema/wire-format version,
# creation time
```
Everything the node knows is in this directory. Back it up by copying the
directory; destroy the node by deleting it. There is no hidden global state —
two nodes in two directories are fully independent identities.
> **The private key is the node's identity.** Anyone holding `node.key` can sign
> operations as this node. Treat the directory like a password store.
## 4. CLI tour
All commands accept `--node-dir ` (the examples below assume
`--node-dir ./avery-node`). Run any command with `--help` for its full options.
### `pmp init`
Creates the node directory, generates the Ed25519 keypair, initializes the
empty log, and writes node metadata. Refuses to overwrite an existing node.
```bash
pmp init --node-dir ./avery-node
```
### `pmp import `
Runs an import adapter against a source and appends one signed `evidence`
operation per evidence item. Adapters shipped in this milestone:
```bash
pmp import calendar samples/calendar/avery-personal.ics --node-dir ./avery-node
pmp import notes samples/notes --node-dir ./avery-node # file or directory
pmp import photos samples/photos/avery-photos.json --node-dir ./avery-node
```
Import is **idempotent**: each evidence item carries a content-derived external
ID, and the node skips items whose identical content has already been ingested.
The command reports how many items were imported and how many were skipped as
duplicates. Re-running the same import is always safe.
### `pmp log`
Lists operations in append order: sequence number, operation ID (truncated),
type, source/adapter, and a one-line summary. Useful filters are available
(e.g. limiting count or filtering by adapter — see `pmp log --help`).
### `pmp show `
Prints a single operation in full as pretty-printed canonical JSON: header
(author, sequence, previous-operation hash, timestamp), payload (the evidence
body), provenance block, and signature. Operation IDs may be given truncated as
long as the prefix is unambiguous.
### `pmp verify`
Re-verifies the entire log:
1. every operation's canonical hash matches its stored operation ID;
2. every signature verifies against the author's public key;
3. the per-author hash chain is intact (each operation's `prev` matches the
hash of the previous operation, sequence numbers are contiguous from 0).
Exits non-zero and names the first offending operation if anything fails. Run
this after restoring a node from backup or whenever you suspect corruption.
### `pmp export`
Streams the log as **canonical JSON Lines** to stdout (one operation per line,
in append order). This is the portable interchange form defined by the
milestone-2 wire format — another implementation can verify the stream with
nothing but the spec and the node's public key.
```bash
pmp export --node-dir ./avery-node > avery.oplog.jsonl
```
### `pmp info`
Prints node identity (node ID / public key), wire-format version, operation
count, and storage path — handy when juggling multiple nodes.
## 5. End-to-end demo
```bash
bash scripts/demo.sh
```
The script creates a fresh node in a temporary directory, imports all sample
datasets for the fictional user Avery Reyes, lists the log, shows one full
operation, verifies the chain, re-runs an import to demonstrate idempotency,
and exports the log to JSONL. Read the script — it is short and is the best
"executable documentation" of the CLI.
## 6. Running the tests
```bash
pytest # whole suite
pytest tests/test_oplog.py -q # one module
pytest -k idempotent -q # by keyword
```
The suite needs no network and writes only to pytest-managed temp directories.
It includes tamper tests that mutate stored operations and assert that
`pmp verify` (and the underlying library call) detects the corruption — if you
change storage or canonicalization, those tests are your tripwire.
## 7. Troubleshooting
* **`pmp: command not found`** — the virtualenv isn't activated, or the package
was installed without `-e`. Use `python -m pmp …` as a fallback.
* **"node directory already exists"** on `init` — `init` never overwrites.
Choose a new directory or delete the old one deliberately.
* **Verification failure after copying a node** — copy the whole directory, not
just `oplog.db`; verification needs `keys/node.pub`. If `verify` reports a
hash mismatch, the database was modified outside the API; restore from backup
(append-only means an untampered backup is always a valid prefix).
* **Windows key permissions** — on POSIX systems the private key is written
mode `0600`. On native Windows this chmod is best-effort; rely on filesystem
ACLs or run under WSL if this matters to you.
* **Time zones in ICS imports** — events are normalized to UTC where the ICS
provides zone information; floating times are preserved as such in the
evidence payload and flagged, so downstream derivation can decide. If your
calendar export uses exotic `VTIMEZONE` definitions, inspect the resulting
evidence with `pmp show` to confirm normalization did what you expect.
## 8. Where to go next
* Writing a new source adapter: [`adapter-authoring.md`](adapter-authoring.md)
* Understanding and auditing the log: [`log-inspection.md`](log-inspection.md)
* Wire format and schema reference: milestone-2 documents in `docs/`