# Bulk Scan

Bulk scan detects data exfiltration at scale by scanning suspect files against your organization's attested inventory. It uses the `/scan` endpoint, which accepts up to 50,000 hashes per call — orders of magnitude faster than individual verification.

## Basic usage

```bash
# Scan a suspect drive against org inventory
census bulk-scan /mnt/suspect-drive

# With manifest cross-reference for original paths
census bulk-scan /suspect --manifest inventory.db

# Machine-readable output
census bulk-scan /suspect --json

# Save results to file
census bulk-scan /suspect --output results.json
```

Requires an API key with `scan` scope.

## Exit codes

| Code | Meaning |
|------|---------|
| `0` | No matches — suspect files are not in the organization's inventory |
| `1` | Matches found — potential data exfiltration detected |

Override with `--exit-zero` for report-only mode (see [CI/CD integration](#cicd-integration) below).

## Dry run

Hash files locally without making any API call. Useful to estimate cost and validate filters before a real scan.

```bash
census bulk-scan /data --dry-run
census bulk-scan /data --dry-run --json
```

Output includes file count, unique hash count, and number of API calls that would be needed.

## Source labeling

Tag scans in the audit log with a source label for traceability:

```bash
census bulk-scan /suspect --source incident-2026-042
```

The label appears in both the JSON output and the audit log.

## CI/CD integration

### Report-only mode (`--exit-zero`)

Always exit 0, regardless of whether matches are found. The scan still runs and output is produced, but the pipeline does not fail. Useful for SARIF upload or monitoring workflows.

```bash
census bulk-scan /artifacts --exit-zero --json > results.json
```

### Summary mode (`--summary`)

Show only aggregate counts, no individual match details. Keeps CI logs concise.

```bash
census bulk-scan /artifacts --summary
```

Combine both for a minimal CI step:

```bash
census bulk-scan /artifacts --summary --exit-zero
```

## Manifest cross-reference

When `--manifest` is provided, matched files are enriched with the original path from the inventory. This answers "where did this file come from?" in the original filesystem.

```bash
census bulk-scan /suspect --manifest /path/to/.census-manifest.db --json
```

Each match in the output includes an `inventory_path` field when the hash is found in the manifest.

## File filters

All filter options from `scan` are available:

```bash
census bulk-scan /suspect --include "*.pdf" --exclude "*.tmp" --min-size 1K --max-size 100M
```

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

## Parallel hashing

Use multiple CPU cores for hashing:

```bash
census bulk-scan /large-dataset --workers 8
```

Workers are clamped to the system maximum. Hashing uses `ProcessPoolExecutor` with `as_completed()` for optimal throughput.

## Chunking

Hashes are sent in chunks of up to 50,000 per API call. For datasets exceeding this limit, multiple API calls are made automatically with retry on transient failures.

See [retry-and-resilience.md](retry-and-resilience.md) for retry behavior.

## Output format

JSON output (via `--json` or `--output results.json`) includes:

```json
{
  "directory": "/mnt/suspect",
  "total_files": 15000,
  "total_scanned": 12500,
  "matched_files": 3,
  "matched_hashes": 2,
  "unmatched_hashes": 12498,
  "scan_ids": ["sc_001", "sc_002"],
  "matches": [
    {
      "hash_hex": "a1b2c3...",
      "suspect_path": "leaked-doc.pdf",
      "first_seen": "2026-01-15T10:00:00Z",
      "source": "finance",
      "inventory_path": "hr/quarterly-report.pdf"
    }
  ]
}
```
