We were unable to construct the commit group for this pull request: Author does not currently possess a GitClear subscription seat.

Local API hardening: stdin secret, gateway downloads, and local-link approval #2379

Merged
requilence opened 11:03am on September 15, 2026 wanted to merge 15 commits into anyproto/anytype-ts develop from
local-link-approval

Pull Request Overview

  • Opened on September 15, 2026
  • Status Merged
  • Commit count 15 with first commit July 28, 2026

Total Delta

0 Total Diff Delta

Open Days

Open 4 weekdays

Test Delta

0 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Data pending calculation for pull request

Author avatar

Local API hardening: stdin secret, gateway downloads, and local-link approval

Two related pieces of local API hardening, plus the local-link approval flow this branch started as.

Local API secret (heart's Part 1)

The account-bootstrap RPCs let any process that reached 127.0.0.1 re-key the wallet and mint a Full-scope token. Main now mints 32 CSPRNG bytes per launch, writes secret <base64url> as the first line on the helper's stdin before it can serve, and the dispatcher attaches local-api-secret to every call β€” heart requires it on the bootstrap subset and ignores it elsewhere, so one rule is correct everywhere.

Verified against a gate-enabled helper: with the secret, gated calls answer 0 and startup takes 3.2s; without it, UNAUTHENTICATED "missing or invalid local api secret" and a 5s permissive stall.

Web mode drops ANYTYPE_PARENT_LIFELINE instead β€” a browser page can never hold the parent secret β€” so its cleanup now also runs on SIGHUP, on normal exit, and when vite exits on its own.

File downloads through the gateway (heart's Part 2)

Bytes travel over the gateway, not the RPC layer, so heart has no process behind a download and emits no Event.Process.*. The renderer owns that progress and feeds the same sidebar rows import/export use: one row per user action, combining its files, cancellable, each file keeping its own name/bytes/state behind a hover list. Transfers are serialized in main β€” electron-dl attaches a will-download listener per call, so concurrent ones report each other's bytes β€” which also keeps a batch off the gateway's shared budget while H2 is open.

Three corrections ride along:
- Saving an image fetched a thumbnail. /image/{id}?width=0 is not "no resizing": heart returns the first variant at least that wide, i.e. the smallest. Saving now always uses /file/{id} with ?attachment=1.
- Opening is extension-gated. Clicking a file block downloaded and opened whatever it was β€” a .tgz left an unpacked tree in Downloads. Only viewable types (documents, images, audio, video) are handed to the system handler; everything else is revealed in the file manager with the file selected.
- FileDownload is no longer used on desktop. It writes only inside heart's temp scope now, so Open file takes the gateway path too (heart's H5 can proceed; mobile keeps the RPC).

Downloading the same file twice reuses the copy already on disk instead of writing doc (1).pdf. Identity is the file object's own checksum: electron/ts/checksum.ts reproduces heart's recipe (sha256 + an encrypted/plain marker byte, base32 hex-alphabet, unpadded), pinned by tests against vectors generated from core/files.checksum. A ledger of where each object last landed makes the repeat case cost one stat; content decides everything else, and a file that does not match is never overwritten.

Local-link approval

Pairing requests and API keys are approved in a main-process queue before a code is minted, with space-scoped grants.

Testing

Unit tests throughout (secret delivery and ordering, download queue and reuse, checksum vectors, ledger, renderer store, the renderer→main request seam). bun run typecheck and bun run lint clean.

E2E for the download flows is drafted in anytype-desktop-suite (specs/sidebar/file-download-progress.md) but not committed and never run β€” it needs the Electron fixture, which no test in that suite uses yet.

Still open, heart-side

H2 per-/file/ concurrency budget (the reason transfers are serialized), H4 gateway authentication (now the widest unauthenticated local door), and flipping the secret gate from permissive to fail-closed now that the client ships it.

PR was closed without comments.