Repository navigation
Store pairing history chunks on the identity service's disk (GRYT-1591) - #48
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 4 of the build plan in
docs/pairing-design.md(Gryt-chat/crypto). The pairing relay from #45 can now hold history chunks, so core's history transfer (Gryt-chat/core#33) has somewhere to send them. Task: GRYT-1591.All of it is review-required (
packages/auth/**). Nothing here touches Keycloak.What it does
Three endpoints under
/api/v1/pairing/sessions/:id/chunks/:n. They match core's relay client and its fake:PUT, from A only, and only after the reveal. The body is{"body": "<base64url>"}, up to 2 MiB once decoded. Answers201 {}. A slot that's already used gets409 exists, unless it's the same bytes again, which counts as a retry and gets201.GET, from N only. Answers{"body"}, or404 not_found. The second fetch deletes the chunk.DELETE, from N only. Answers204, even when the chunk's already gone.Chunks are saved as raw bytes under
$GRYT_IDENTITY_DATA_DIR/pairing/<session>/<n>, with0600files in0700folders. They're never parsed and never logged. The logs only get a count and a MiB total once a minute, plus refusals by reason.A chunk goes when N deletes it or fetches it twice, when its session closes or expires (an hour after the claim at most), or an hour after upload. The sweep checks all three, and every minute it also removes any folder that doesn't belong to a live session.
Limits. The first four are environment variables, in MiB:
507 session_full507 full507 disk_low429 rate_limited413 too_large400 invalid_slotCore's sender treats any
507as the end of what fits and marks the history as cut short, which is the design's behaviour. Free space comes fromstatfson the chunk folder. For a named volume that's the Docker disk, the same one Postgres is on.What happens on a restart mid-transfer
The sessions live in memory, so they're gone. At startup the service empties
data/pairing/, removing only folders named like a session id, soca-key.pemnext to it stays. After that, every call on the old session gets404 not_found, because the relay has no memory of it and can't answer410.By then keys and sign-in are already done, because history comes after N commits. So on the phone or desktop:
PUTgets a 404. That isn't a 507, so the sender throws and A's history send ends.That's the design's "anyone halfway through starts again". The test
a restart wipes the chunks of sessions it no longer knows, and nothing elsecovers the relay's half of it.What to look at
ChunkStore.putinchunks.ts, and the order of its checks. The caps are reserved before the firstawait, so two uploads at once can't both squeeze under the same limit. If the free-space check or the write fails, the reservation goes back. If the session ends while a write is in flight, the file gets removed after the write. I'm least sure about this part.^[0-9A-HJKMNP-TV-Z]{26}$directly underdata/pairing, and the folder is always<data dir>/pairing, not configurable, so a bad setting can't point it at the CA key.409 exists. Core's sender only retries after a network error, when the firstPUTmay well have landed, so a 409 there would end the history send for no reason.end()now drops the session's chunks, andsweep()returns a promise so the tests can wait for the disk.Tests
identity/test/chunks.test.ts, 13 tests, against a real temp folder and a faked free-space check. They cover upload, fetch and delete, the second fetch deleting a chunk, the retry, sides and states, a token from another session (401), session close, expiry and the one-hour sweep, each cap, the free-space refusal, the per-address limit, the restart wipe, the env parsing and the body limit.yarn buildandyarn testpass, with all 24 tests green.I also ran core's
src/pairing/history.test.ts(core main 446c63f) against this relay, built and serving on localhost, with only the relay swapped out. Five of its seven tests apply, and all five pass: 5,000 messages over 1,202 chunk uploads and 1,202 fetches, with nothing left on disk at the end. The other two need a relay that tampers with chunks, so they only make sense with the fake. Two of the test's assertions depend on the fake's timing rather than on the relay. Those are in GRYT-1594.Deploying
The relay from #45 isn't deployed yet, so this deploys both. It's Sivert's step. On dev.lan, in
packages/auth:git pullGRYT_IMPORT_REALM=0in.envdf -hand make sure there's room for the build plus the 5 GiB floordocker compose -f docker-compose.keycloak.yml -p auth up -d --no-deps --build identityThere's no new volume. The chunks go on
gryt-identity-data, which is already mounted at/app/dataand owned by thegrytuser from the Dockerfile. The fourGRYT_PAIRING_CHUNKS_*variables have their defaults in compose, so.envonly needs them to change a limit.GRYT_PAIRING_TRUSTED_PROXIESfrom #45 still has to be set, or the 1 GiB a day is shared by everyone behind the tunnel.🤖 Generated with Claude Code