Cache/API implementation (#36), phase 2.
Depends on the decrypt-fields unit. New module.
Goal
The on-disk JSON metadata store the library reads and writes: one metadata.json holding everything the server told us, loaded whole into RAM,
rewritten whole (temp-file + fsync + rename) only when a refresh changed
something.
Scope
metadata.json schema (owner-approved: JSON, never SQLite): userID, schemaVersion, the collections-list cursor, and per-collection records
(including each album's file-diff cursor sinceTime, decrypted collection
key, type, name, magic layers, updationTime, isShared) and per-membership
file records ((collectionID, fileID), updationTime, metadata + magic
layers, fileHeader/thumbHeader, decrypted file key, contentHash, fileSize/thumbSize).
Load: parse whole file, build in-memory Maps for id lookups. Missing/corrupt
file is not an error — treat as empty (it is a cache; the refresh re-fetches).
Save: temp-file + fsync-before-rename + dir-fsync-after (reuse the exported
atomic writer). Single rename covers cursors and the data they describe so
they can never disagree.
Sensitivity: it holds decrypted titles, GPS, and keys — create the directory 0700 and the file 0600, same as session.json.
No lock file (owner ruling). No sync() in any public surface.
Definition of done
Round-trip: write then read reproduces the model; id Maps correct.
Corrupt/missing file loads as empty without throwing.
New module under src/library/ (name the file plainly, e.g. store.ts). Reuse
the fsync atomic writer from the download-plumbing unit. This unit stores; the
refresh unit populates.
Dispatch notes: TDD; no scripted edits; no interactive questions; plain language
only. Squash subject ends (closes #<this issue>). End every message with Model: opus-4-8.
Model: opus-4-8
Cache/API implementation (https://git.eeqj.de/sneak/quak/issues/36), phase 2.
Depends on the decrypt-fields unit. New module.
## Goal
The on-disk JSON metadata store the library reads and writes: one
`metadata.json` holding everything the server told us, loaded whole into RAM,
rewritten whole (temp-file + fsync + rename) only when a refresh changed
something.
## Scope
- `metadata.json` schema (owner-approved: JSON, never SQLite): `userID`,
`schemaVersion`, the collections-list cursor, and per-collection records
(including each album's file-diff cursor `sinceTime`, decrypted collection
key, type, name, magic layers, `updationTime`, `isShared`) and per-membership
file records (`(collectionID, fileID)`, `updationTime`, metadata + magic
layers, `fileHeader`/`thumbHeader`, decrypted file key, `contentHash`,
`fileSize`/`thumbSize`).
- Load: parse whole file, build in-memory `Map`s for id lookups. Missing/corrupt
file is not an error — treat as empty (it is a cache; the refresh re-fetches).
- Save: temp-file + fsync-before-rename + dir-fsync-after (reuse the exported
atomic writer). Single rename covers cursors and the data they describe so
they can never disagree.
- Sensitivity: it holds decrypted titles, GPS, and keys — create the directory
`0700` and the file `0600`, same as `session.json`.
- No lock file (owner ruling). No `sync()` in any public surface.
## Definition of done
- Round-trip: write then read reproduces the model; id `Map`s correct.
- Corrupt/missing file loads as empty without throwing.
- Permissions `0700`/`0600` asserted; atomic write path tested.
- `make check` green.
## Grounding
New module under `src/library/` (name the file plainly, e.g. `store.ts`). Reuse
the fsync atomic writer from the download-plumbing unit. This unit stores; the
refresh unit populates.
Dispatch notes: TDD; no scripted edits; no interactive questions; plain language
only. Squash subject ends ` (closes #<this issue>)`. End every message with
`Model: opus-4-8`.
Model: opus-4-8
Implemented in #58 (branch issue-41-json-metadata-store, base next).
MetadataStore (src/library/store.ts): one metadata.json holding userID, schemaVersion, the collections cursor, and the decrypted collection and file records. Loaded whole into RAM with Maps for id lookups; rewritten whole through the download layer's exported fsync atomic writer. Binary keys are base64 on disk. Missing/corrupt/wrong-schema files load empty. Directory 0700, file 0600; no lock file, no sync() in public surfaces. File records are keyed per membership (collectionID, fileID). This unit only stores; the refresh unit (#42) populates it.
make check green (298 tests, 10 new).
Model: opus-4-8
Implemented in https://git.eeqj.de/sneak/quak/pulls/58 (branch `issue-41-json-metadata-store`, base `next`).
`MetadataStore` (`src/library/store.ts`): one `metadata.json` holding `userID`, `schemaVersion`, the collections cursor, and the decrypted collection and file records. Loaded whole into RAM with `Map`s for id lookups; rewritten whole through the download layer's exported fsync atomic writer. Binary keys are base64 on disk. Missing/corrupt/wrong-schema files load empty. Directory `0700`, file `0600`; no lock file, no `sync()` in public surfaces. File records are keyed per membership `(collectionID, fileID)`. This unit only stores; the refresh unit (https://git.eeqj.de/sneak/quak/issues/42) populates it.
`make check` green (298 tests, 10 new).
Model: opus-4-8
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Cache/API implementation (#36), phase 2.
Depends on the decrypt-fields unit. New module.
Goal
The on-disk JSON metadata store the library reads and writes: one
metadata.jsonholding everything the server told us, loaded whole into RAM,rewritten whole (temp-file + fsync + rename) only when a refresh changed
something.
Scope
metadata.jsonschema (owner-approved: JSON, never SQLite):userID,schemaVersion, the collections-list cursor, and per-collection records(including each album's file-diff cursor
sinceTime, decrypted collectionkey, type, name, magic layers,
updationTime,isShared) and per-membershipfile records (
(collectionID, fileID),updationTime, metadata + magiclayers,
fileHeader/thumbHeader, decrypted file key,contentHash,fileSize/thumbSize).Maps for id lookups. Missing/corruptfile is not an error — treat as empty (it is a cache; the refresh re-fetches).
atomic writer). Single rename covers cursors and the data they describe so
they can never disagree.
0700and the file0600, same assession.json.sync()in any public surface.Definition of done
Maps correct.0700/0600asserted; atomic write path tested.make checkgreen.Grounding
New module under
src/library/(name the file plainly, e.g.store.ts). Reusethe fsync atomic writer from the download-plumbing unit. This unit stores; the
refresh unit populates.
Dispatch notes: TDD; no scripted edits; no interactive questions; plain language
only. Squash subject ends
(closes #<this issue>). End every message withModel: opus-4-8.Model: opus-4-8
Implemented in #58 (branch
issue-41-json-metadata-store, basenext).MetadataStore(src/library/store.ts): onemetadata.jsonholdinguserID,schemaVersion, the collections cursor, and the decrypted collection and file records. Loaded whole into RAM withMaps for id lookups; rewritten whole through the download layer's exported fsync atomic writer. Binary keys are base64 on disk. Missing/corrupt/wrong-schema files load empty. Directory0700, file0600; no lock file, nosync()in public surfaces. File records are keyed per membership(collectionID, fileID). This unit only stores; the refresh unit (#42) populates it.make checkgreen (298 tests, 10 new).Model: opus-4-8