# Scanner report schema `omarchy-security-scan` writes one JSON object to stdout. Report schema version 1 is retained with additive fields; persistent state has migrated to version 2. - `schemaVersion` (`1`), `generatedAt` (UTC ISO-8601), `profile`, `durationMs`. - `summary`: `status` (`critical`, `warning`, `notice`, `good`, `unknown`), severity counts, `unavailable`, `failed`, `incomplete`, `actionable`, `new`, and `total`. Severity counts include suppressed findings; `actionable` counts unsuppressed warnings/critical findings. `new` excludes suppressed/good items. A good observation cannot make an incomplete scan report `good`. - `coverage`: counts of `completed`, `unavailable`, `failed`, `not-applicable`. - `checks`: one record per declared check, with `id`, `available`, `status`, sanitized `detail`, and `canResolve`. `available` is true only for `completed`. `canResolve` distinguishes confirmed prerequisite absence from a missing tool. - `findings`: stable `id`, owning `checkId`, `category`, `severity`, `confidence`, `title`, `summary`, `detail`, sanitized `evidence`, `recommendation`, optional `command`, `firstSeen`, `lastSeen`, `occurrenceStartedAt`, `changedAt`, `isNew`, `lifecycle` (`new` or `present`), `eventToken`, `acknowledged`, `snoozedUntil`, and `suppressed`. - `history`: minimal records for resolved findings and prior findings whose check did not complete (`lifecycle: unverified`). Includes identity, title, category, severity, owning check, first/last observation and resolution timestamps. - `dataFreshness`: last successful online check time for `updates` and `arch-audit`. Automatic scans never refresh these sources. - `stateError`: nonempty when persistence could not be read or written. ## Lifecycle and review The first stateful scan establishes a baseline without notifications. New or changed findings remain new across scans until reviewed or resolved. An acknowledgment or snooze applies to the observed fact fingerprint. Fact changes, severity increases, and reappearance after resolution clear suppression. A snooze expires at its UTC timestamp; the panel converts the selected local date into the end of that day. Profile changes keep finding IDs stable. Only a completed owning check, or authoritative absence of its prerequisite (`not-applicable`), can resolve a missing finding. Failed/unavailable checks preserve previous records. Uninstalling an optional checker does not resolve its prior findings; those remain unverified and make the report incomplete. Positive observations can still be reported by a partially completed check, such as a partial Docker inspection. Review mutations require `--action acknowledge|snooze|restore`, `--finding ID`, and the report's `--token EVENT_TOKEN`. Snoozes additionally require `--until ISO_TIMESTAMP`. A stale token fails without changing the decision. All stateful scans and mutations acquire the same nonblocking file lock. A concurrent caller receives a failed report and may retry. Failed scanner processes do not replace the widget's last successful report. ## Local persistence and online data `state.json` stores finding IDs, generic titles, category/severity/check metadata, timestamps, review decisions, notification tokens, and observation fingerprints. It does not store finding summaries, evidence, suggested commands, raw package output, addresses, keys, or journal lines. At most 100 resolved records are kept; active/unverified records are preserved. Existing version-1 timestamps migrate; historical titles unavailable in the old schema use the finding ID. Corrupt history is reported and left untouched rather than silently reset. `data-cache.json` stores only counts, fingerprints, and successful refresh times. Data older than 24 hours is unavailable and cannot resolve an earlier finding. `--refresh-data` explicitly runs network-capable `checkupdates` and optional `arch-audit`; failure preserves the prior cache and timestamp. `--no-state` disables history writes and notifications, but an explicit `--refresh-data` still writes this data cache. Docker probes always use a local Unix socket, ignoring remote Docker contexts. JSON files are replaced atomically with mode `0600`. Notifications are opt-in (`--notify`), aggregate only new unsuppressed warning/critical findings, contain no evidence, and have a persistent 15-minute cooldown. Successful deliveries are deduplicated by event token. Failed deliveries and findings deferred by cooldown remain eligible. This is best-effort delivery: a crash between desktop delivery and saving its token can cause a repeated notification.