# Manifest

The manifest is a local SQLite database that maps SHA-256 hashes to file paths and metadata. It is the bridge between CertiSigma attestations (hash-only, server-side) and the local filesystem (path + size + mtime, client-side).

**The manifest never leaves the client.** CertiSigma sees only hashes.

## Location

By default, Census writes the manifest to `<scanned_directory>/.census-manifest.db`. Override with `--manifest /path/to/output.db`.

## Storage format

Starting with Census v0.3.0, the manifest uses SQLite with WAL (Write-Ahead Logging) mode:

- **Indexed lookups**: O(log n) by hash or path (vs O(n) with JSON)
- **Incremental writes**: INSERT/UPDATE per entry instead of full-file rewrites
- **Concurrent-safe**: WAL mode allows concurrent reads during writes
- **Schema versioned**: `PRAGMA user_version = 2` tracks the schema version

## Schema (version 2)

```sql
CREATE TABLE meta (
    key   TEXT PRIMARY KEY,
    value TEXT NOT NULL
);

CREATE TABLE entries (
    hash_hex       TEXT PRIMARY KEY,
    path           TEXT NOT NULL,
    size           INTEGER NOT NULL,
    mtime          REAL NOT NULL,
    attested       INTEGER NOT NULL DEFAULT 0,
    attestation_id TEXT,
    attested_at    TEXT
);

CREATE INDEX idx_entries_path ON entries(path);
CREATE INDEX idx_entries_attested ON entries(attested);
```

### Meta table

| Key | Value |
|-----|-------|
| `root_dir` | Absolute path of the scanned directory |
| `created_at` | Unix timestamp of manifest creation |
| `updated_at` | Unix timestamp of last modification |

### Entry fields

| Column | Type | Description |
|--------|------|-------------|
| `hash_hex` | TEXT (PK) | SHA-256 hash (64 hex characters) |
| `path` | TEXT | Relative path from `root_dir` |
| `size` | INTEGER | File size in bytes |
| `mtime` | REAL | File modification time (Unix timestamp) |
| `attested` | INTEGER | 0 = pending, 1 = attested |
| `attestation_id` | TEXT | CertiSigma attestation ID (null if pending) |
| `attested_at` | TEXT | ISO 8601 timestamp of attestation (null if pending) |

## Migration from JSON (v1)

Census automatically migrates legacy JSON manifests to SQLite:

1. On `Manifest.load("manifest.json")`, Census detects the JSON format
2. Creates a new `manifest.db` alongside the JSON file
3. Imports all entries preserving attestation state
4. Renames `manifest.json` to `manifest.json.bak`
5. Returns the SQLite-backed manifest

The CLI also transparently resolves paths: `census status manifest.json` will find `manifest.db` if the JSON file no longer exists.

## Status command

```bash
census status /path/to/.census-manifest.db
```

Output:

```
Manifest: /data/.census-manifest.db
Root:     /data/sensitive-files
Total:    1523
Attested: 1500
Pending:  23
```

## Export command

Export the manifest contents as CSV or JSON for use in other tools.

```bash
# Export as CSV (default)
census export manifest.db --output inventory.csv

# Export as JSON
census export manifest.db --format json --output inventory.json

# Pipe CSV to stdout
census export manifest.db | head -5
```

CSV columns: `hash_hex`, `path`, `size`, `mtime`, `attested`, `attestation_id`, `attested_at`.

## Merge behavior (resume)

When resuming a scan (`--resume`), the existing manifest is merged with new scan results:

- **New files** are added.
- **Unchanged files** (same path, size, mtime) keep their existing hash and attestation state.
- **Modified files** (different size or mtime) are re-hashed; attestation state is preserved if the hash is unchanged.

See [scanning.md](scanning.md) for resume details.

## Performance characteristics

| Manifest size | Add entry | Lookup by hash | Lookup by path |
|---------------|-----------|----------------|----------------|
| 1,000 entries | <1ms | <1ms | <1ms |
| 100,000 entries | <1ms | <1ms | <1ms |
| 1,000,000 entries | <1ms | <1ms | <1ms |

SQLite indexes ensure constant-time lookups regardless of manifest size. WAL mode ensures writes do not block reads.
