# warehouse — guide
> **Class:** `guide` · audience: someone opening the shared catalog to import advisories or
> scan a fleet. The *what* is in [`warehouse-design.md`](warehouse-design.md); the *why* in
> [`warehouse-reasoning.md`](warehouse-reasoning.md). Needs `--features warehouse`.
## f0 — open, import, scan (CLI)
```sh
# import an advisory generation (see feeds-guide.md for the feed flags)
modgunn warehouse import --warehouse <root> --rev 2026-07-04-a --osv advisories/osv/
# full fleet scan: every repo's sbom_components × the latest advisories
modgunn warehouse scan --warehouse <root>
# incremental CDC scan: only components added since a snapshot; prints next watermark
modgunn warehouse scan --warehouse <root> --since 4211
# what's imported
modgunn warehouse stats --warehouse <root> --ecosystem cargo
# persist an A23 package-behaviour analysis (verdict ledger), then read it back
modgunn behavior --path package.json --warehouse <root> # append the vector + verdict
modgunn warehouse behavior-log --warehouse <root> # latest per (ecosystem, subject)
modgunn warehouse behavior-log --warehouse <root> --ecosystem npm --subject left-pad-helper
```
`scan` prints one line per vulnerable dependency (`repo name version advisory_id severity`)
then a summary with the high/critical count and, for `--since`, the `next --since <watermark>`
to poll from. Add `--vex <dir>` to drop consumer-suppressed advisories, `--sql` to use the
DataFusion pushdown (needs `warehouse-sql`; degrades to in-memory otherwise).
## f0 — in code
```rust
use modgunn::warehouse::Warehouse;
use std::path::Path;
let wh = Warehouse::open(Path::new(root))?; // or open_read_only when a server holds the lock
wh.ensure_modgunn_tables()?;
wh.append_advisories("2026-07-04-a", &advisories)?; // a new generation
let db = wh.load_advisory_db(Some("cargo"))?; // latest db_rev, one ecosystem
let findings = wh.scan_fleet()?; // Vec<FleetFinding>
// A23 behaviour verdict ledger — persist a parsed PackageBehavior, read it back.
let behavior = modgunn::behavior::parse_npm_package(package_json)?;
wh.append_package_behavior("npm", &behavior)?; // vector + derived verdict, append-only
let records = wh.load_package_behaviors()?; // Vec<BehaviorRecord>, latest per (eco, subject)
assert_eq!(records[0].behavior, behavior); // round-trips field-identical
```
## f2 — incremental refresh loop
```rust
// remember the last watermark; poll cheaply, only scan the delta
let (findings, next) = wh.scan_sbom_delta(Some(last_watermark))?;
// … act on findings …
last_watermark = next.unwrap_or(last_watermark);
```
Use `wh.commit_seq()?` as an even cheaper "did anything change at all?" poll before calling
`scan_sbom_delta`. `wh.table_delta("cve_advisories", from)?` gives the raw changed rows for any
owned table.
## f3 — capturing an Engine B snapshot (feature `scip` + `warehouse`)
```rust
wh.capture_world_snapshot(&snapshot)?; // world_snapshots + dep_edges + scip_symbols
wh.record_behavior_delta_findings(&delta)?; // behavior_deltas rows
```
## Gotchas
- If a running `nornir-server` holds the redb write lock, use `Warehouse::open_read_only` — it
copies the catalog aside and reads a consistent snapshot without contending.
- `scan_fleet` assumes `cargo` for SBOM rows until nornir adds an ecosystem column
(`DEFAULT_ECOSYSTEM`); cross-ecosystem fleets will mis-match until then.
- A missing `sbom_components` table is not an error — it just means "no watermark" / an empty
fleet, so a fresh warehouse scans cleanly.
- Imports and a live server writer are single-writer coordinated — schedule imports when the
server is down, or import through the read-only-safe path.