fix(core): atomic restore with aliases, snapshot export, hardened backups
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a047020d54
commit
66b155876f
8 files changed
+424
-182
No files matched your search
+8
-2
@@ -16,12 +16,18 @@ No index: `grep -n '^## ' docs/DECISIONS.md` lists every entry (date, title, lin
|
||||
(the importing user owns everything). `works.jsonl` adds `private` and `externalIds`.
|
||||
- **Deleted rows are included** with their `deletedAt`, and `fieldSources`/timestamps are kept, so a restore is lossless.
|
||||
- **No secrets.** No users row beyond username/display name, no sessions, tokens, connections, credentials or source snapshots.
|
||||
- **Merges and aliases.** `works.jsonl` rows carry `mergedIntoId` (the survivor and ancestors are exported too) and
|
||||
`work_aliases.jsonl` holds the user's `{ fromWorkId, toWorkId }` rows. Both are additive to v1. Restore re-creates
|
||||
tombstones (for works it creates) and aliases, so a later write to the old work still resolves to the moved entry.
|
||||
- **Export is one snapshot:** all reads run in a single transaction (repeatable read on Postgres).
|
||||
- **Restore** (`importUserBundle`) needs an account with no entries (`account_not_empty`) and `format === "sttracker-export/v1"`
|
||||
(`unsupported_format`). Works with external IDs are re-matched (else created shared); works without become private.
|
||||
Ids are remapped (UUIDs inside history `before`/`after` and custom-field value keys too). Rows are inserted directly, so no
|
||||
new history is written. Works merged away at export time are exported as the tombstone row without `mergedIntoId`.
|
||||
new history is written. The import validates first (attachment mime/size/sha256, rating 0-100, status, visibility, 2 GB
|
||||
uncompressed cap) and writes all rows, attachments included, in one transaction; errors are
|
||||
`invalid_attachment`, `corrupt_bundle`, `bundle_too_large`, `conflict`. Works merged away at export time are exported as the tombstone row without `mergedIntoId`.
|
||||
- **Backups** write `<dataDir>/backups/<ISO with ':'->'-'>/<username>.zip` per user plus `sttracker.db` (`VACUUM INTO`, SQLite only),
|
||||
and prune to the newest `keep` (default 14).
|
||||
are built in `<stamp>.tmp` then renamed (modes 0700/0600), and prune to the newest `keep` (default 14).
|
||||
**Consequences:** Any change to these files after release needs `v2` and a migration path for old bundles.
|
||||
|
||||
## 2026-10-30: User layer: per-user moves merge entries, keep aliases, and roll the library up by hierarchy
|
||||
|
||||
Reference in new issue
Block a user