Commit Graph

312 Commits

Author SHA1 Message Date
4f09e474c5 test(core): add saved devices production release gate 2026-08-12 17:19:03 +02:00
2c941dc623 refactor(core): remove experimental saved device contract 2026-08-12 17:00:28 +02:00
5b57917f52 refactor(bindings): use production saved device APIs 2026-08-12 16:48:20 +02:00
5ffd86afbc feat(core): promote protected saved device APIs 2026-08-12 16:38:21 +02:00
67a557af7c feat(core): expose durable targeted transfer lifecycle 2026-08-12 16:11:53 +02:00
32d69b9771 fix(core): complete targeted transfers without invitation state 2026-08-12 15:32:06 +02:00
644c9bfda3 fix(core): isolate targeted transfers and persist peer names 2026-08-12 15:06:01 +02:00
dac8232324 fix(apple): realign UI layer with regenerated core bindings
- map the new VnidropError cases (DeviceUnavailable, OfferTimeout,
  RelayPolicyIncompatible, ProtocolIncompatible) to existing catalog keys
- drop the stale .map(\.share) now that sharePickedFiles returns Share
- remove the duplicate .transfersChanged pattern in the signal switch
- replace the deprecated String(cString:) sysctl decode
- close the CoreGateway protocol declaration
2026-08-12 11:34:49 +02:00
ebdff3df4b fix(desktop): unblock protected-core startup snackbars
Run Secret Service IO on spawn_blocking so Linux zbus cannot nest Tokio
runtimes during init, and wait for core initialize before experimental
saved-device coordinators refresh.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 21:40:17 +02:00
b9884b566a feat(shared): enable experimental saved-devices on desktop
Surface Settings → Experimental on Windows/Linux Compose, keep generic
Desktop hosts gated off, and assert path-based targeted receive when no
output sink is available.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 19:51:48 +02:00
37cc238888 fix(android): unblock protected-core startup on Android
Use flock for profile locks, hash secret record filenames under NAME_MAX,
and dismiss the starting overlay after the first init attempt.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 19:46:14 +02:00
eabc2754a7 feat(shared): Android experimental saved-devices KMP UI
Gate pairing and targeted transfers behind Settings → Experimental, with
CoreGateway wake-ups, in-flow prompts, and one Android dogfood round-trip.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 18:09:37 +02:00
edf7426672 chore: drop design-section cross-refs from core comments
Keep module docs self-describing without pointing at DESIGN §N.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 17:03:42 +02:00
d91820b867 feat(core): id-centric targeted approve/receive with output sinks
Stop returning grant strings across UniFFI; approve yields typed outcomes and
pull/resume use transfer id plus path or ReceiveOutputSink. Document the
pairing/targeted event catalog and cover Android MediaStore-style sink contracts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 16:50:10 +02:00
3b7fd3d468 refactor(core): deepen domain stores and invitation-only persistence
Peel relationships, eligibility, and secrets off the shared pool into
AppDataStores adapters, split pairing service/protocol, and move the
invitation Repository into its own module so open_all owns schemas.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 15:51:35 +02:00
5c66c2ca65 refactor(core): open AppDataStores instead of exporting SqlitePool
Introduce persistence::open_all so domain stores (invitation, targeted,
blocked) are constructed once. Extract TargetedTransferStore and own it on
CoreInner; soft-close raw pool access for new callers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 08:50:16 +02:00
a2385912c2 refactor(core): remove prototype contact paths for experimental saved devices
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 06:05:31 +02:00
0a6ecb5c3b merge: ticket 14 Apple saved-device core contract
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:39:40 +02:00
9c7697c90b feat(apple): exercise saved-device core contract harness
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:39:10 +02:00
039d3f942b merge: ticket 17 Linux saved-device core contract
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:13:00 +02:00
1e601e118a feat(linux): exercise saved-device core contract harness
Prove Secret Service-backed identity restart, the public saved-device
lifecycle, fault isolation, event recovery, and binding hygiene without
product UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:12:20 +02:00
e79aba7bcf merge: ticket 16 Windows saved-device core contract
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:11:51 +02:00
f603f5fb8d feat(windows): exercise saved-device core contract harness
Prove the DPAPI-backed Windows bridge can drive the full public saved-device
and targeted-transfer contract via an injectable API fake on non-Windows hosts
and real DPAPI under cfg(windows), without product UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:11:26 +02:00
0c317efe59 feat(android): exercise saved-device core contract harness
Prove Keystore-backed Android secret storage can drive the full public
saved-device and targeted-transfer contract without product UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 05:10:04 +02:00
8e8cab9b24 feat(core): add event revisions and saved-device local labels
Platform contract harnesses need monotonic event revisions for at-least-once
dedup and a typed rename API that survives listing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 04:47:28 +02:00
89c2206f0f feat(core): harden saved-device control plane against hostile peers
Bound pending offers, enforce pre-approval limits and max_saved_devices,
add per-identity cooldowns, silently reject invalid traffic, and redact
sensitive values from production events and errors without inventing
accepted-transfer quotas.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 04:33:38 +02:00
a01d8328c9 merge: integrate resumable transfers with network profile compatibility
Combine ticket 11 resume/cancel/delete/idempotency with ticket 12 relay
profiles, protocol floors, and typed offer outcomes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 04:02:07 +02:00
8798edf0f2 feat(core): make targeted transfers resumable and idempotent
Preserve approved transfer auth and state across restart, resume without
re-approval, and keep cancel/delete from leaving usable orphan authorization.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:56:46 +02:00
4a83b18d42 feat(core): honor network profiles and protocol floors for saved devices
Targeted offers and pairing now validate relay-policy compatibility and
reject protocol downgrades with typed unavailable/timeout/incompatibility
errors, without falling back to ordinary shares.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:54:13 +02:00
e2cfad7fb3 fix(core): tear down shares on forget/block and fail-closed blocks
Cancel targeted protocol shares synchronously before await, and treat block
lookup errors as denied so store failures cannot admit blocked peers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:35:18 +02:00
22c842acb1 merge: integrate targeted transfers with revoke/forget/block
Combine ticket 09 lifecycle APIs with ticket 10 targeted-transfer protocol
and wire forget/block to real cancel_targeted_transfers_for_peer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:30:33 +02:00
11fae16391 feat(core): revoke, forget, block, and rotate device relationships
Give saved-device owners immediate local control over grants and identity-wide
denies, with minimal tombstones for replay rejection and a ticket-10 hook for
targeted-transfer cancellation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:27:28 +02:00
f4cec1415c feat(core): complete one approved targeted transfer between saved devices
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:22:38 +02:00
193fe7c757 feat(core): save devices through mutual consent and grants
Establish PendingOutgoing/PendingIncoming relationships over a token-bound
pairing protocol, exchange directional grants with challenge-response proofs
and a final ack before Saved, and merge simultaneous initiations without a
second prompt.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 03:03:43 +02:00
04a31e8a24 feat(core): create pairing eligibility after completed transfers
Add the experimental eligibility control plane so either endpoint of a
fully completed authenticated invitation transfer can start one single-use
pairing attempt within 24 hours, with secrets held in custody and invalid
requests rejected silently.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 02:34:18 +02:00
e740942f63 chore: ignore bin directory in git repository 2026-08-09 21:24:25 +02:00
eefedd0cb0 fix(core): harden protected identity startup 2026-08-09 20:25:16 +02:00
429987785e feat(core): protect secrets on native platforms 2026-08-09 19:34:34 +02:00
931b297321 test(core): harden secret custody recovery 2026-08-09 18:44:58 +02:00
1cd09a2ec3 feat(core): add recoverable secret custody 2026-08-09 18:37:54 +02:00
b128457137 docs(core): clarify experimental domain versions 2026-08-09 18:07:03 +02:00
564b86c28c feat(core): establish saved device domain seam 2026-08-09 18:01:02 +02:00
0ec0daef2a docs: redesign saved device foundation 2026-08-09 18:00:48 +02:00
c852a68f28 fix(apple): correct the device id row on the detail screen
The fingerprint row reused the no-name placeholder as its label, so it read
"A nearby device — aaab1c58", and it duplicated the device id shown
directly beneath it. Now one selectable full id, which is what someone
comparing devices actually needs.
2026-08-07 10:59:00 +02:00
225ff9ad22 fix: stop the device picker hanging on an offer
Two causes. The connect step had no timeout, so an unreachable device was
retried indefinitely instead of falling through to hold-for-later; it now
gives up after 15s and holds the offer as designed.

The picker also waited on the whole exchange, which includes a person on the
other device deciding — up to two minutes. It now closes on tap and reports
the outcome as a message, and a decline or an unanswered offer is shown as
information rather than an error, since the offer did arrive.
2026-08-07 10:49:32 +02:00
677fc3c6d5 fix(apple): make the device detail screen reachable
The Settings stack has a typed path of [SettingsSection], so a
NavigationLink carrying a String could never push onto it: tapping a device
in the list did nothing. Contact detail is now a SettingsSection case, and
the path maps it to the two-level push the way the bug report screen
already does.
2026-08-07 10:33:10 +02:00
3441280599 feat(apple): send a transfer to a device from the share panel
Send to a device now sits alongside the QR code, NFC, and export actions,
since an offer is another way to deliver the same invitation. Picking a
device pushes the existing transfer rather than re-sharing the files.

The picker lists only devices holding a live grant, so nothing offered there
can fail on tap, and it distinguishes accepted from waiting for that device
to open the app.

Also fixes the deprecated SF Symbol and the two Sendable warnings introduced
with the contacts screen: the sections now talk to the model directly rather
than storing view callbacks that a Binding setter has to convert.
2026-08-07 10:24:05 +02:00
d8587851a3 feat(core): offer an existing share to a remembered device
Another way to deliver an invitation the user already created, alongside the
QR code, rather than a second share of the same files: the ticket handed over
is the stored one and the transfer id is unchanged.

Only an active share can be offered. A stopped one no longer serves its
content, so handing out its ticket would promise nothing.
2026-08-07 10:14:53 +02:00
cba51ad504 docs(design): mark device history as implemented 2026-08-06 19:00:24 +02:00
0f70663263 feat(apple): collect transfers held for this device
Adds the opt-in foreground check and an explicit Check now, the waiting-to-
be-delivered list on the sender side, and honest reporting when a send could
not be delivered: a closed app is a delay, not a success nobody received.

The setting is off by default and its footer states that checking reveals
app-open times to remembered devices, since that is the reason it is a
setting at all.

Records in the design doc that this shipped as one global toggle rather than
the per-contact opt-in originally specified.
2026-08-06 19:00:04 +02:00