# Watch Mode

Census watch mode provides continuous filesystem monitoring with automatic attestation. Files that are created, modified, moved, or deleted are detected in real time and processed in batches.

## Architecture

Watch mode uses a **producer-consumer** pattern:

- **Producer**: `watchdog` Observer thread receives OS-level filesystem events (inotify on Linux, FSEvents on macOS, ReadDirectoryChangesW on Windows) and adds them to a thread-safe pending dict.
- **Consumer**: The main thread periodically drains the dict, hashes changed files, attests new hashes via the CertiSigma API, and saves results to the SQLite manifest.

Debouncing prevents redundant processing when editors write multiple times per save.

## Installation

```bash
pip install certisigma-census[watch]
```

The `watchdog` dependency is only required for watch mode. All other Census features work without it.

## Usage

```bash
# Basic: watch a directory with default settings
census watch /path/to/files --source "hr-inventory"

# Dry run — hash and index only, no API calls
census watch /data --dry-run

# Custom debounce and batch interval
census watch /data --debounce 5.0 --batch-interval 60

# Skip baseline scan (only watch for new changes)
census watch /data --no-scan-on-start

# Handle deletions by removing from manifest
census watch /data --on-delete remove

# NFS/CIFS mount (use polling instead of native events)
census watch /mnt/share --polling --poll-interval 10
```

## Options

| Option | Default | Description |
|--------|---------|-------------|
| `--debounce SECS` | 2.0 | Quiet period before processing events |
| `--batch-interval SECS` | 30.0 | Max time between attestation batches |
| `--scan-on-start / --no-scan-on-start` | on | Full baseline scan before watching |
| `--on-delete ignore\|mark\|remove` | ignore | Action on file deletion |
| `--polling` | off | Use PollingObserver (for network mounts) |
| `--poll-interval SECS` | 5.0 | Polling interval (with `--polling`) |
| `--source LABEL` | none | Source label for attestations |
| `--manifest PATH` | `.census-manifest.db` | Path to SQLite manifest |
| `--dry-run` | off | Hash only, no attestation |
| `--include/--exclude/--min-size/--max-size` | — | Same filters as `census scan` |

## Event types

| Event | Action |
|-------|--------|
| **Created** | Hash file, add to manifest, attest |
| **Modified** | Re-hash, update manifest (old entry removed), attest |
| **Moved** | Hash at new path, add to manifest, attest |
| **Deleted** | Depends on `--on-delete` policy |

## Delete policies

- **`ignore`** (default): Deleted files remain in the manifest. Recommended for forensic integrity — the attestation record proves the file once existed.
- **`mark`**: Log the deletion but keep the entry. Useful for audit trails.
- **`remove`**: Delete the entry from the manifest. Use when the manifest should reflect current state only.

## Debounce and batching

Events are accumulated in a pending dict and processed when:
1. The debounce period (`--debounce`) elapses after the last event, OR
2. The batch interval (`--batch-interval`) is reached

This prevents processing the same file multiple times when an editor writes it repeatedly during a save operation.

## Baseline scan

By default (`--scan-on-start`), watch mode performs a full directory scan before starting the observer. This ensures:
- Any files created between the last scan and watch start are captured
- The manifest is complete before monitoring begins
- Resume semantics apply (unchanged files are skipped)

Disable with `--no-scan-on-start` if the manifest is already current.

## Security considerations

- **Symlink escape prevention**: Events for paths that resolve outside the watched root are silently rejected.
- **Queue overflow warning**: If the pending queue exceeds 10,000 events, a warning is logged. This may indicate misconfiguration (watching too broad a directory) or an event storm.
- **Graceful shutdown**: SIGINT (Ctrl+C) and SIGTERM trigger a clean shutdown — pending events are processed and the manifest is saved.

## Network mounts (NFS/CIFS)

Native OS events (inotify, FSEvents) do not work on network mounts. Use the `--polling` flag:

```bash
census watch /mnt/nas/sensitive --polling --poll-interval 10
```

The PollingObserver scans the directory tree at the configured interval. It is slower and more resource-intensive than native events, but works on any filesystem.

## inotify limits (Linux)

If you get an error about inotify watch limits, increase them:

```bash
# Temporary (until reboot)
sudo sysctl -w fs.inotify.max_user_watches=524288

# Permanent
echo 'fs.inotify.max_user_watches=524288' | sudo tee -a /etc/sysctl.conf
```

## Running as a service

### systemd (Linux)

```ini
[Unit]
Description=CertiSigma Census Watch
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/census watch /data/sensitive --source production
Environment=CERTISIGMA_API_KEY=cs_...
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target
```

### launchd (macOS)

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>ch.certisigma.census.watch</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/census</string>
        <string>watch</string>
        <string>/data/sensitive</string>
        <string>--source</string>
        <string>production</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>CERTISIGMA_API_KEY</key>
        <string>cs_...</string>
    </dict>
    <key>KeepAlive</key>
    <true/>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
```
