# Local guide: `plugmem-host`
## Role
`plugmem-host` is the native `std` layer around `plugmem-core`. It owns filesystem paths, generation files, manifest publication, append-only journals, locks, mmap read-only access, settings, embedders, checkpointing, verification, and salvage recovery.
The public writer is `Database`; the zero-copy reader is `ReadOnlyDatabase`. Core logic must remain in `plugmem-core`; this crate coordinates it with durable storage and process-level concurrency.
## File and generation model
`FileStorage` keeps the published snapshot generations immutable and appends mutations to a journal. A manifest points to the current generation. Checkpoint writes a fresh image to a temporary generation, flushes according to `FsyncPolicy`, publishes it atomically, and clears the journal. Opening performs crash cleanup for unpublished/orphan staging files.
Default maintenance thresholds are 1024 operations or a 4 MiB journal, unless configured through `DatabaseBuilder`/`Settings`. `maintain_every_forgets` is optional and physically purges tombstones/builds indexes when enabled.
Never overwrite a published generation in place. Preserve the temp-write, fsync, rename, manifest, and garbage-collection ordering when changing persistence code.
## Writer, reader, and mmap rules
`Database` provides read-your-writes through its in-process engine and shared/exclusive locking. Readers can run concurrently with a writer according to the lock policy. `ReadOnlyDatabase` pins an immutable checkpoint generation and maps it without copying the large pools; it requires a checkpointed database with an empty journal at open.
Read-only handles are snapshot-isolated. They do not move to a newer generation automatically; call `refresh()` and check the returned boolean. `generation()` is the freshness marker. Do not promise read-your-writes from a read-only handle.
The only unsafe operations here are the inherent `memmap2::Mmap::map` calls. `self_cell` keeps the mmap owner alive while the borrowed `Memory` points into it. The file is pinned/locked and the generation is immutable, so a concurrent truncate cannot invalidate the mapping. Keep unsafe blocks narrow and preserve the lifetime/lock proof in the adjacent safety comment.
## Integrity and recovery
- `verify()` checks content-level invariants such as valid stored text and fact/vector consistency.
- `scrub()` checks stored section hashes and the whole snapshot incrementally with a byte budget; it is a resumable integrity scan.
- `recover()` salvages content-corrupt facts into a different destination, compacts survivors, and leaves the source untouched.
- Structural snapshot damage is not a salvage case; return the typed error and restore from backup.
Do not conflate `verify`, `scrub`, and `recover`. Tests should cover torn journal tails, corrupted content, bad structure, generation publication, and destination safety.
## Embedders and settings
`Embedder` is a `Send` trait. `NullEmbedder` disables automatic vectors; `OpenAiCompatEmbedder` talks to an OpenAI-compatible HTTP endpoint and must be kept outside the engine lock where possible. Network/model latency is not storage latency.
The optional `config` feature parses TOML settings for engine, embedder, maintenance, and shared wrapper configuration. The optional `counters` feature is for deterministic performance measurements; it changes synchronization behavior and must not be enabled for normal concurrent-reader use.
## Verification commands
```bash
cargo test -p plugmem-host
cargo test -p plugmem-host --all-features
cargo test -p plugmem-host --test concurrency
cargo run --release -p plugmem-host --example integrity
cargo run --release -p plugmem-host --example recall_ollama
cargo bench -p plugmem-host --bench integrity
```
When changing persistence, also run the core snapshot/journal tests and inspect the generated files with `verify`/`scrub`. Use a temporary directory for tests; never point destructive recovery tests at a user database.