Skip to content

Add stored transfers: Send as link, stored-link receive and History revoke - #19

Merged
bakirgdev merged 8 commits into
mainfrom
1-0/p10-stored-transfers
Oct 14, 2026
Merged

bakirgdev merged 8 commits into
mainfrom
1-0/p10-stored-transfers

Conversation

@bakirgdev

@bakirgdev bakirgdev commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Intent

Add stored transfers: upload files to a croc store server and share a link (with QR, Copy, Share), receive a stored link, and revoke from History. After this PR a sender can use "Send as link" without the receiver being online, and revoke it later.

Requirements

  • R18-R20: crocmobile/store.go (StartStoreSend, StartStoreReceive, RevokeStored), CrocKit startStoreSend/startStoreReceive/revokeStored, TransferController store paths, Send as link toggle, link result screen, History Revoke.
  • R12d: receipts only in the Keychain via StoredTransferVault; History keeps host, id, expiry and revoked date; codeHint stores only the host for stored links.
  • R12e: https enforced for server and links (localhost exempt) in Go and in TransferLink.
  • R13: Go and Swift tests for each layer.
  • R19: TransferRecord additions are additive; HistoryUpgradeTests opens a store written with 0.9.9's schema.
  • R21: stored-transfer server setting with inline validation and Reset.
  • R29 (partly): new views; Accessibility Inspector pass moved to owner step O12.

Plan deviations

  • The seeded-history proof is a test over a legacy-schema class, not a build of tag v0.9.9 (no 0.9.9 build with a persistent harness store exists).
  • Interop revoke cases 14a/14c allow 2 downloads (14d allows 3) so a download before the revoke does not consume the transfer; plan step 39 still says "1 download" and should read "the downloads each case needs, with one left at revoke time".

Review

Two reviews (HEAD 7127f4f, then 8bad980). First: Important 1 (I1, the revoke harness scenario could pass without revoke working), fixed in 8bad980 and mutation-checked. Second: Important 0, Nit 24 (5 listed, 19 unlisted). None blocks; open ones are in the review notes (SwiftData write after row deletion during revoke, Clear All dropping the only Revoke path for a live upload, docs copy "no upload to someone else's cloud" and the 100-download default).

Verification

Re-run in the ship session at HEAD 8bad980:

  • scripts/format-swift.sh --lint: exit 0
  • go build, go vet, golangci-lint run ./...: 0 issues.; go tool govulncheck -show verbose ./...: 0 vulnerabilities in called code; go test -race ./...: ok
  • scripts/build-xcframework.sh: exit 0; swift test --package-path CrocKit: 25 tests in 5 suites passed
  • xcodebuild test -scheme CrocApp -destination 'platform=macOS': TEST SUCCEEDED; xcodebuild build generic iOS and iOS Simulator: rc 0, no warnings from our sources
  • scripts/verify-interop.sh (local relay): ALL INTEROP CHECKS PASSED, including the store scenarios (CLI upload, wrapper download, revoke, download fails)
  • scripts/verify-app-mac.sh: rc 0, MAC-STORE-OK
  • scripts/verify-app-sim.sh: SIM-INTEROP-OK; scripts/verify-share-sim.sh: SHARE-SIM-OK

Not verified: iPhone background upload, QR scans, Universal Clipboard (O8); Accessibility Inspector and VoiceOver on the P10 views (O12); public-pool interop scenarios; stored-receive Keep Both/Replace through the Receive view; the SwiftData trap in review N1.

CI follow-up

The first CI run failed interop (local relay): Homebrew's croc bottle was 11.5.0, which has no croc store, so scenario 14c could not run. The job now installs the CLI with go install at the version in crocmobile/go.mod (11.5.4); docs/knowledge/tooling.md updated. Second run: all jobs green.

…h croc's store client

crocmobile gains StartStoreSend, StartStoreReceive and RevokeStored over
storeclient, sharing the transfer lock, with OnStored for the receipt and
storeLimit/storeExpired failure codes. Keep Both is settled in the manifest
because storeclient's install rename replaces. CrocKit exposes the three calls
and TransferEvent.stored; croctest gains store modes and a loopback store
server, and verify-interop.sh scenario 14 covers CLI interop both ways, revoke
and the file policies.
…s from the controller

StoredTransferVault holds each upload's receipt (token and link) in one
Keychain item, purges expired ones at launch, and keeps earlier receipts
when a write fails. TransferController gains startStoreSend,
startStoreReceive, revoke and Phase.stored; history records carry the
upload id and expiry as optional fields, and a stored link's hint is only
its host. A stored transfer never offers resume: a background expiry says
the upload was interrupted and to try again. AppSettings gains the
stored-transfer server, and harness runs use an in-memory vault.
…rio, a 0.9.9 history upgrade test and the docs page
… revoke fails scenario 14

Scenarios 14a and 14c uploaded with one download and used it before revoking. The store server then answered revoke with 410, which the wrapper and the CLI both count as revoked, so 14b and 14c passed with revoke broken. Two downloads leave one at revoke time. Checked with revoke disabled in RevokeStored (14b fails) and with the CLI revoke skipped (14c fails).
…e Homebrew's bottle lags and has no croc store
@bakirgdev
bakirgdev merged commit ca07331 into main Oct 14, 2026
15 checks passed
@bakirgdev
bakirgdev deleted the 1-0/p10-stored-transfers branch October 14, 2026 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant