Date: 2026-10-02, after sign-off runs S1-S5 on the real system (S6, the
soak, deferred). Three reviewers (AI agents in their own clones, own
sc builds and throwaway stores; the real scd, store and /etc were
not touched): security, data integrity across store + watcher + CLI, and
operations (long-run behaviour, standing in for the deferred soak).
Claude reproduced each finding before acting on it.
Gate: no high-severity finding. Two medium findings: one fixed, one deferred. The rest are low and deferred (list below).
Held up:
kill -9s of the watcher (mid-batch, mid-rescan, in a
3,000-file burst) and a kill -9 of sc restore between rename and
commit. The store stayed consistent every time.did not exist. The M1 → M2
migration and sc-m1 on the migrated store both work.protected_hardlinks). Restore through a symlinked parent,
over a directory, or of a deleted row onto a directory is refused.
Journal lines are quoted. A 200,000-name .ssh costs 11 MB and 0.9 s
per rescan.| Area | Finding | Severity | Status |
|---|---|---|---|
| watch | A user renaming ~/.ssh away and back every second pushed the settle delay out each time, so no rescan except the startup one ever ran: no hourly rescan, overflow recovery, SIGHUP, passwd or dirty-bound recovery. Reproduced: 6 rescans in 20 s became 0. |
medium | fixed 9cee575 |
| docs | journalctl -u scd without sudo prints -- No entries -- for a normal user. There are no uninstall steps. WORKLOG suggested changes.db.m1-backup for rollback, which would drop all M2 rows (and sc-m1 already reads the migrated store). The recovery guide points at /usr/local/sbin/sc with no fallback. |
medium | step 17 |
| watch | Nothing rate-limits a system file that a program rewrites constantly: at 1 change/s that is about 86k rows, 350 MB and 86k journal lines a day, until the 256 MiB floor. No writer like that exists on this VM. | medium | fixed 064bf68 (20 rows, then one per 5 min) |
| scope | snapd’s /etc/systemd/user/*.wants/snap.* links were recorded at tier 2. |
low | fixed 3349449 |
| store | A digest row’s id is sha256(path, ts, sha256(content))[:6] and is logged, so a journal reader can test guesses at a low-entropy fingerprint-only secret. Reproduced. |
low-medium | fixed 4ec437a (random id) |
| watch | renameat2(RENAME_EXCHANGE) of two watched directories leaves one unwatched until the hourly rescan. No stock 24.04 command does this. Reproduced. |
low-medium | fixed c0cfe77 |
| watch | A root moved away (mv ~/.ssh ~/.ssh.old) keeps its files’ newest rows as present; when a new one appears, history goes to changed with no deleted row. Reproduced. |
low | fixed bd8fad9 |
| watch | After an overflow, an old watch on a moved-away directory is never removed; its events can be filed under the new path or unwatch a new subdirectory. | low | fixed 465b30d |
| watch | Home-file rate limits count rows, not stored objects. A user rewriting their 4 .ssh files can leave orphan objects. Reproduced only partly: 1 orphan in 65 s here, 36 for the reviewer. |
low | fixed 2a8b686 |
| watch | A startup race (swapping ~/.ssh for a symlink at the right moment) can fail one start, which systemd restarts. |
low | fixed 5b57aaf |
| scope | Digest rules match names, so root-made copies such as /etc/ssh/old/ssh_host_ed25519_key are stored with content. Reproduced. |
low | deferred: M3 (content-based secret detection) |
| watch | Directory listings in memory only grow (watcher.go:691-696), against plan 6.4. Near zero here, since temp names are filtered first. |
low | fixed d76a295 |
| ops | At the free-space floor scd logs once and then says nothing more. After 5 failed starts the unit stays failed silently. | low | deferred: M4 (sc status) |
| docs | MILESTONES says M2’s watcher clears stale .NAME.sc-tmp-* files at start; no code does this, and those names are excluded. |
low | step 17 corrects the doc; reported, not removed, 7463e5d |
| docs | Retention (nothing is pruned, 256 MiB floor) is only in the plan; README and PROJECT_LOG status lines are stale. | low | step 17 |
| m3 | A file created while scd is down is a first seen row with no journal line; after a kill before the baseline summary, not even counted (from S5). |
note | M3 existence-flag alerts |
/etc/passwd (chfn, passwd), and each change
asks for a rescan of the home roots (at most one per 10 s) and stores a
passwd blob. This is not tested on the real system; it is bounded by
the rescan gap.