# Integrity Check

Census compares the current state of files against a manifest baseline to detect tampering, unauthorized modifications, missing files, or unauthorized additions.

This is a **100% local operation** — no API calls, no API key needed. Only SHA-256 hashes are computed and compared against the stored baselines.

## Basic usage

```bash
# Check integrity against a manifest
census integrity manifest.db

# Machine-readable JSON output
census integrity manifest.db --json

# Fail with exit code 1 on any discrepancy
census integrity manifest.db --strict

# Save results to CSV or JSON
census integrity manifest.db --output results.csv
census integrity manifest.db --output results.json
```

## Output

```
Integrity check: /data/hr (1500 files in manifest)

  Unchanged:  1497
  Modified:   2
  Missing:    1
  New:        3

Modified files:
  hr/quarterly-report.pdf  expected: a1b2...  actual: f4e5...  (size: 5120 -> 5248)
  hr/contact-list.xlsx     expected: c3d4...  actual: 9a8b...

Missing files:
  hr/termination-letter.docx

New files (not in manifest):
  hr/unauthorized-export.zip
  hr/temp/draft.docx
  hr/.backup/old-file.dat

Result: INTEGRITY VIOLATION — 6 discrepancies found
```

## Exit codes

| Code | Meaning |
|------|---------|
| `0` | All files match baseline (or `--strict` not used) |
| `1` | Discrepancies found (only with `--strict`) |

Without `--strict`, the command always exits `0` and reports discrepancies in the output. With `--strict`, exit code `1` enables scripted checks:

```bash
census integrity manifest.db --strict && echo "clean" || echo "tampering detected"
```

## Detection categories

| Category | Description |
|----------|-------------|
| **Unchanged** | File exists on disk with the same SHA-256 hash as in the manifest |
| **Modified** | File exists but its SHA-256 hash differs from the baseline |
| **Missing** | Entry exists in manifest but file is no longer on disk |
| **New** | File exists on disk but is not in the manifest |
| **Error** | File could not be read (permissions, I/O error) |

## Comparison with similar tools

Census integrity check is modeled after established file integrity monitoring (FIM) tools:

| Tool | Scope | API dependency |
|------|-------|---------------|
| Census integrity | Local hash comparison | None |
| Wazuh syscheck | Real-time FIM agent | Wazuh manager |
| AIDE | Offline hash database | None |
| Tripwire | Host-based FIM | Tripwire server |

Census adds cryptographic attestation on top: files verified by `integrity` can also be verified against the CertiSigma registry to prove they were inventoried at a specific point in time.

## Differential mode (suppress known findings)

Reduce noise in scheduled runs by only reporting **new** findings:

```bash
# First run: creates state file, reports all findings
census integrity manifest.db --since state.json --write-state state.json

# Second run: only NEW findings since last run
census integrity manifest.db --since state.json --write-state state.json

# Auto-sidecar (state saved as manifest.db.integrity-state)
census integrity manifest.db --since auto --write-state auto
```

### How it works

1. `--since` loads the previous state file (findings from the last run).
2. `integrity_check` runs the **full** hash comparison as usual.
3. Findings already present in the state (same path + same fingerprint) are **suppressed**.
4. `--write-state` saves the current **full** state (not filtered) for the next run.

### Forensic metadata per finding

Each finding in the state file tracks:

- **`first_seen`**: ISO-8601 timestamp of when the finding first appeared (carried forward across runs).
- **`occurrences`**: how many consecutive runs the finding has persisted.

No competitor (AIDE, Tripwire, Wazuh, Osquery, Samhain) provides per-finding `first_seen` timestamps.

### Re-surfacing

A finding is re-reported when its fingerprint changes:

- **Modified file**: hash changes again (new modification) → re-reported.
- **Missing file**: file returns then disappears again → re-reported.
- **New file**: content changes → re-reported.

### Cron example (JSONL + differential)

```bash
census integrity /data/manifest.db \
  --format jsonl \
  --since auto --write-state auto \
  --strict \
  | jq -c 'select(._summary != true)' \
  >> /var/log/census-integrity.jsonl
```

### JSON output with differential

```json
{
  "root_dir": "/data/hr",
  "total_in_manifest": 1500,
  "unchanged": 1497,
  "modified": [],
  "missing": [],
  "new_files": [],
  "errors": [],
  "differential": true,
  "suppressed": 6,
  "new_findings": 0,
  "state_path": "/data/manifest.db.integrity-state",
  "census_version": "1.6.0",
  "elapsed_seconds": 2.34
}
```

## JSON output format

```json
{
  "root_dir": "/data/hr",
  "total_in_manifest": 1500,
  "total_checked": 1503,
  "unchanged": 1497,
  "modified": [
    {
      "path": "hr/quarterly-report.pdf",
      "expected_hash": "a1b2c3...",
      "actual_hash": "f4e5d6...",
      "expected_size": 5120,
      "actual_size": 5248
    }
  ],
  "missing": ["hr/termination-letter.docx"],
  "new_files": ["hr/unauthorized-export.zip"],
  "errors": []
}
```
