mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-11-07 09:45:36 +00:00
fix(sync): atomic install, stored-row validation, no local artifacts from remote manifests
From the full-repo Codex audit: - Install moved the database to a backup, then renamed the snapshot in; a crash between the two left no database, and the next sync deleted the backup first. Replace it with a single rename (old file or new, never neither), and restore a backup an older version left behind. - Validation trusted the release's row_count metadata, so a snapshot with an empty or missing holdings table passed and replaced good data. Also count the rows stored for the latest release. - A manifest fetched over HTTP(S) could name a local download_url such as /dev/zero, read without a size limit. Remote manifests must give an HTTP(S) artifact; local reads take regular files only, within the size limit, and manifests are capped at 1 MiB. - A same-date build with a different row count replaced the local file even when it did not list it in replaces_sqlite_sha256, so a replayed older build could undo a corrected one. Same-date rebuilds now go only through replaces_sqlite_sha256, which the publisher fills. Verified against the published snapshot: fresh install, no-op re-sync, and forced reinstall all succeed with no leftover files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
parent
4cf86ce1ff
commit
523e1409d3
2 changed files with 190 additions and 53 deletions
|
|
@ -62,7 +62,8 @@ Semantics:
|
|||
(`ownership-snapshot-YYYY-MM-DD-<sha256>.sqlite`), so consumers must always
|
||||
follow `download_url` and validate it with `sqlite_sha256` and `size_bytes`.
|
||||
- `sqlite_sha256` and `size_bytes` are validated before install.
|
||||
- `latest_*` and `release_count` are validated against the downloaded SQLite contents before replacement.
|
||||
- `latest_*` and `release_count` are validated against the downloaded SQLite contents before replacement, including the number of holdings rows actually stored for the latest release.
|
||||
- A manifest fetched over `http(s)://` must give an `http(s)://` `download_url`; only a local manifest may point at a local file. Local files must be regular files, and manifests are limited to 1 MiB.
|
||||
- `replaces_sqlite_sha256` (optional, omitted when empty) lists earlier builds
|
||||
published for the same `latest_as_of_date` that this snapshot supersedes, for
|
||||
example after a parser fix. Clients up to v0.2.x ignore it.
|
||||
|
|
@ -78,9 +79,11 @@ Without `--force`, sync behaves conservatively:
|
|||
- local DB file is byte-identical to a build in `replaces_sqlite_sha256`: replace
|
||||
(queries never write to a synced file, so this means it was not modified;
|
||||
committed changes still in the `-wal` file are checkpointed before hashing)
|
||||
- local latest release/date/count matches snapshot: no-op
|
||||
- local latest release/date/count matches snapshot: no-op (a same-date build
|
||||
with a different row count replaces the local file only when it lists that
|
||||
file in `replaces_sqlite_sha256`)
|
||||
- local latest release matches but has fewer releases than snapshot: replace to fill missing history
|
||||
- local DB newer than snapshot: no-op and keep local data
|
||||
- local DB diverges at the same latest date: no-op and require `--force`
|
||||
|
||||
Replacement is staged through a temp file and validated before the local DB is swapped in.
|
||||
Replacement is staged through a temp file and validated before the local DB is swapped in with a single rename, so an interrupted sync leaves either the old database or the new one.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue