Commit Graph
100 Commits
Author SHA1 Message Date
nvrl d3f4090772 feat(http): log the raw response code of failed API requests
An envelope code that ResponseCode does not know deserializes to Unknown, so
the error a caller sees has lost the number Proton sent. Log path, HTTP status
and the raw code at debug level before mapping it.
2026-10-08 15:02:57 +02:00
nvrl 325c326c49 feat(account): hold key passphrases as wiped secrets and report a locked account as KeysLocked
A wrong mailbox password surfaced as InvalidOperation("no active user key could
be unlocked"), which a caller could only recognise by its text. It is now
ProtonError::KeysLocked, and AccountClient::unlock() checks a secret with a
single core/v4/users request instead of loading every address.

Derived passphrases, the mailbox password and the cache encryption key were
plain Vec<u8>s, cloned freely and never wiped. They are Zeroizing now, and the
passphrase map is KeyPassphrases: wiped on drop, redacted in Debug, with
derive_secret() for a client that wants a key only an unlocked account can
reproduce. with_key_passphrases takes it.

Breaking: ProtonError gains a variant, key_passphrases() and
with_key_passphrases() change type, and derive_key_passphrase returns
Zeroizing<Vec<u8>>.
2026-10-07 02:24:51 +02:00
nvrl 909eefd06e chore(VER) bumped to 0.7.4 2026-10-06 23:49:58 +02:00
nvrl 32014b1f07 feat(drive,photos): report recently accessed nodes
Port upstream 0aabea59: report_recently_accessed on ProtonDriveClient and
ProtonPhotosClient, batches of 50. Also read the ThirdParty/Sdk flags from
f257c20e on link and revision DTOs. Pin bumped to df3a717b.
2026-10-06 23:49:58 +02:00
nvrl 7950eff337 chore(deps) upgraded dependencies 2026-10-06 14:10:09 +02:00
nvrl 771bc443fa chore(VER) bumped to 0.7.3 2026-10-06 13:59:12 +02:00
nvrl 6eb8b206a7 feat(account,session): resume from derived key passphrases and report second-factor methods
`AccountClient::key_passphrases` is now public, and
`AccountClient::with_key_passphrases` / `ProtonDriveClient::with_key_passphrases`
build a client from the per-key passphrases alone. A client that persists a
session can store those instead of the mailbox password, which on a
single-password account is the login password itself.

`ProtonApiSession::second_factor_methods` exposes the login's 2FA bitmask
(TOTP, FIDO2), so a client without WebAuthn can tell an account that only has
a security key apart from a wrong code.
2026-10-06 13:59:10 +02:00
nvrl 1bd5fe8a07 chore(VER) bumped to 0.7.2 2026-10-01 18:07:46 +02:00
nvrl 1477fe85d5 fix(drive): give each share root its own parent key in batch enumeration
enumerate_nodes and enumerate_nodes_light cached parent keys per batch by
the link's parent id. Every parentless link in a volume landed on None, so
when several share roots were enumerated together, the first root's share
key was used for the rest and they were dropped with "missing key".

Name a parent key by where it comes from: Parent(folder) for a link with a
parent, Root(link) for a parentless one. The batch cache and the
parent-key single-flight now both use that identity. The single-flight
previously keyed a root and its children on the same link id, although a
root resolves to its share key and its children to its node key, so two
concurrent cold loads could hand one of them the wrong key.

Fixes #2
2026-10-01 18:06:58 +02:00
nvrl 4a0453c66f chore(VER) bumped to 0.7.1 2026-09-29 21:14:35 +02:00
nvrl a01e3a5cbd fix(http): keep idle connections alive and bound the connect phase
Every client built by the SDK now shares one builder. It sends TCP
keepalives and HTTP/2 PING frames every 15 seconds, also while the
connection is idle, and drops a connection whose PING goes unanswered for
10 seconds. A connection that a NAT or middlebox silently dropped is then
noticed before a request waits on it for the full request timeout.

Connecting is capped at 15 seconds, or the request timeout if that is
shorter, so an unreachable host fails fast and the retry loop moves on.
2026-09-29 21:14:35 +02:00
nvrl ce491ba519 chore(deps) updated 2026-09-28 01:15:55 +02:00
nvrl 9036c35697 chore(VER) bumped to 0.7.0 2026-09-28 01:07:12 +02:00
nvrl 1068ddf5d0 feat(drive): read a photo's location from its extended attributes
Other Proton clients write a `Location` object with `Latitude` and
`Longitude` into a photo's encrypted extended attributes. The decoder
dropped it. It is now decoded and exposed as `PhotoProperties::location`,
a `PhotoLocation` in WGS 84 decimal degrees.

Coordinates that are missing, not finite, or off the globe give no
location rather than a wrong one. The new field defaults on
deserialization, so nodes cached before this change still load.

`PhotoProperties` gains a public field, which breaks code that builds it
with a struct literal.
2026-09-28 01:06:59 +02:00
nvrl 917bad2ef4 chore(sync): bump upstream pin to f28a93ce 2026-09-27 12:12:48 +02:00
nvrl 7422036f65 fix(scripts): upstream-sync reads short pins and detects rewritten history
- read the SHA from the Pinned line (7-40 chars) instead of requiring a
  full 40-char SHA the script itself never printed; the old grep died
  silently under pipefail
- print the full head SHA to pin
- require the pin to be an ancestor of origin/main, since a rewritten-away
  pin still resolves locally until gc
- --diffs now prints patches, not just stats
- --all no longer reports dropped noise
- accept submodule/worktree checkouts where .git is a file
2026-09-27 12:10:33 +02:00
nvrl f7054c3c3b chore(VER) bumped to 0.6.6 2026-09-23 03:15:25 +02:00
nvrl 931a58794b feat(drive): album rename, delete and remove photos
Port AlbumsManager.updateAlbum (name only), deleteAlbum and removePhotos
from the TypeScript SDK: PUT photos/volumes/{vid}/albums/{lid},
DELETE ...?DeleteAlbumPhotos= and POST .../remove-multiple in batches of
ten.
2026-09-23 03:15:10 +02:00
nvrl 1f76f33d49 chore(VER) bumped to 0.6.5 2026-09-19 14:05:22 +02:00
nvrl 1ca28c5848 feat(drive,sdk): port upstream C# SDK up to 6cbf2f44
Port 6cbf2f44 (stop retrying permanent API errors on block transfers)
and 20822176 (small file upload interop); 6ce47922 is a no-op here.

- Add ProtonApiError::is_retriable / ProtonError::is_retriable mirroring
  C# RetryPolicy.IsRetriable: 4xx is permanent except 404, 408 and 429,
  with envelope codes able to override the HTTP status.
- Gate upload_one_block's retry loop on it so a permanent rejection of a
  revision fails immediately instead of being replayed three times.
- Extract BlockTargets from RevisionReader so the whole-file download
  path, the public link download and the reader all re-mint expired
  block URLs through one generation-deduplicated refresh.
- Fall back from the small upload to the block path on AlreadyExists
  only; the small upload is not idempotent, so everything else keeps
  propagating.
- Bound a server-supplied Retry-After by RetryPolicy::max_retry_after
  (default 120 s) so a long rate-limit answer cannot look like a hang.
2026-09-19 14:05:19 +02:00
nvrl 4f129b778a chore(VER) bumped to 0.6.4 2026-09-15 15:07:21 +02:00
nvrl bf36bbdf74 chore(deps): bump lru to 0.18 and base64 to 0.23
lru < 0.16.3 has an IterMut Stacked Borrows violation (GHSA advisory
flagged by Dependabot on downstream proton-drive-linux).
2026-09-15 15:06:56 +02:00
nvrl 6f052e1912 chore(VER) bumped to 0.6.3 2026-09-15 13:54:33 +02:00
nvrl d72065418b feat(drive,sdk): port upstream C# SDK up to ce000edd
- Moves go through `move-multiple` in batches of 150, with an optional
  target name per node (`move_nodes_streaming` / `NodeMoveItem`), a single
  `InvalidRequirements` retry against the server's current name hash, and
  passphrase signing for anonymously created nodes. A batch rejected as a
  whole keeps its API error code on every node.
- Share memberships carry invite time and inviter email, and the inviter's
  `drive.share-member.inviter` signature is verified as non-fatal metadata
  (`verify_detached_with_context`).
- `Node` gains `direct_role` and `share_id`.
- Trashed detection reads `TrashTime` as well as the legacy `Trashed`.

Breaking: new public struct fields on `Node` and `ShareMembership`.
2026-09-15 13:54:04 +02:00
nvrl 81844cb9da chore(VER) bumped to 0.6.2 2026-08-28 12:00:05 +02:00
nvrl aa17b52692 feat(photos): save photos to the timeline
Ports upstream C# e0e198e2 (SavePhotosToTimelineAsync, itself the JS
saveToTimeline). Photos already on our photos volume are moved into the
timeline root via PUT photos/volumes/{vid}/links/transfer-multiple,
batched so no request carries more than ten links and a photo is never
split from its related photos; photos on another volume — from an album
shared with us — are copied via POST volumes/{vid}/links/{lid}/copy, one
request each. That closes the cross-volume copy hole (TS copyPhoto).

The re-encryption is what add_photos_to_album already did, so it moves
into a shared photo_target_payload: name re-encrypted to the target key
and signed by the photos membership address, name/content hashes under
the target hash key, node passphrase rewrapped so the original
NodePassphraseSignature survives.

A photo the server reports an incomplete group for is re-queued once
with the related photos it named; only its second answer becomes an
outcome, so outcomes arrive in completion order, not input order.

Also adds ProtonPhotosClient::enumerate_events (c8d03244's only
non-interop hunk) and bumps the upstream pin to c8d03244. Upstream
29e58378 (upload telemetry) and 0537d906 (NodeProvider refactor) are
deliberately not ported — see UPSTREAM_SYNC.md.
2026-08-27 15:58:06 +02:00
nvrl 61117fd7dc feature(photos) implemented dups, import from takeout 2026-08-18 01:30:45 +02:00
nvrl 0cf760362a fix(sdk) token mutex no longer spans the refresh 2026-08-16 18:53:29 +02:00
nvrl 9ec0582efe added chain with salts 2026-08-10 15:19:18 +02:00
nvrl 12bcf61424 version bump to 0.5.0 2026-08-10 14:11:38 +02:00
nvrl e378db6ec8 implemented enumeration for drive 2026-08-10 14:10:43 +02:00
nvrl 4fe7f02ebb version 0.4.0 2026-08-03 00:18:39 +02:00
nvrl 5e1f7b56e0 version bump to 0.3.2 2026-07-31 19:34:01 +02:00
nvrl 3616f15cfc validate_node_name, albums, photo tags 2026-07-31 19:32:59 +02:00
nvrl db66211a4d version bump 0.3.1 2026-07-28 20:11:54 +02:00
nvrl 9f48617b5b implemented context share, upload/write paths 2026-07-28 20:10:53 +02:00
nvrl e0cbc15d21 version bump to 0.3.0 2026-07-28 18:36:23 +02:00
nvrl d4bebaa2ac implemented share membership permission + shared with me enumeration 2026-07-28 18:35:36 +02:00
nvrl 5b38a85ccb Merge pull request #1 from narrrl/feat/node-active-revision-id
implemented revisions
2026-07-25 12:49:36 +02:00
nvrl ea59994250 implemented revisions 2026-07-25 12:48:41 +02:00
nvrl b683e73c23 perf(drive): encrypt content blocks concurrently 2026-07-25 00:25:54 +02:00
nvrl 9dd535b374 improved performance, concurrent upload, version 0.2.0 2026-07-24 20:29:15 +02:00
nvrl fcf4fc9fb0 version adjusted for drive 2026-07-22 03:05:49 +02:00
nvrl 0777c071b6 version bump to 0.1.12 2026-07-22 03:03:59 +02:00
nvrl 8c786bcf3a Photo duplicate detection, Atomic small uploads support buffered new files and revisions, Small uploads are disabled by default and enabled with .with_small_file_upload(true), Added authenticated multipart HTTP support with retry and token refresh 2026-07-22 03:03:25 +02:00
nvrl 14d69d7060 fmt 2026-07-20 00:07:30 +02:00
nvrl 684ecb2abb updated deps 2026-07-20 00:07:08 +02:00
nvrl 75a615dd28 added re captcha flow 2026-07-19 23:14:25 +02:00
nvrl 8c44768010 version bump 2026-07-19 19:40:27 +02:00
nvrl ac9bfc48b0 fixed orphaned docs 2026-07-19 14:27:34 +02:00
nvrl 24a99e6b9a public link consume, revision history, path helpers
Public links (public_link.rs, ported from the TS SDK — the C# SDK has no
equivalent): ProtonDrivePublicLinkClient lets a visitor with no Proton
account open someone else's share link. `info` reads pre-auth metadata,
`open` runs the SRP handshake against drive/urls/{token}/info + /auth and
unlocks the share key (bcrypt SharePasswordSalt -> key password -> the
SKESK'd SharePassphrase -> ShareKey). The read surface then reuses the
ordinary link/children/revision endpoints. Signature verification is
deliberately absent: an anonymous visitor cannot read core/v4/keys/all,
matching the TS SDK. Supporting primitives: http::get_unauthenticated and
crypto::decrypt_armored_with_password.

Revision history: enumerate_revisions / get_revision / restore_revision /
delete_revision, plus open_revision_at / download_revision{,_to} to read a
specific revision. Drafts are filtered on the wire value, since
RevisionState::from_raw maps 0 to Active. Three server behaviors found
live and worked around: ?NoBlockUrls=true still emits Blocks with null
BareURL, so get_revision deserializes into a DTO without that field; the
restore POST needs an empty object body, not null; and restore is async
(HTTP 202, non-1000 envelope) so the new active revision is not live on
return.

Path and hierarchy helpers: get_node_hierarchy, get_node_path,
get_node_by_path, create_folder_path. Names are encrypted and there is no
name-query endpoint, so lookup walks and decrypts one directory level per
segment.

Fix a silent-corruption path in block-size resolution. RevisionReader
accumulates block sizes to derive where each block starts in the
plaintext, so a vector that overstates any block but the last shifts every
block after it and the reader splices at the wrong offsets — full-length
reads of the wrong bytes, no error raised. resolve_block_sizes previously
guessed all-full blocks when both Common.BlockSizes and Common.Size were
unusable. It now errors for multi-block files and only assumes a full
block when there is exactly one (which cannot be misaligned). Regression
test in revision.rs::padded_block_sizes_shift_later_blocks.

Recover from stale upload drafts. An upload interrupted after the draft
node was created but before it was sealed leaves an unsealed draft, and
every later attempt at that name returns 422 AlreadyExists forever. Draft
creation now deletes the conflicting node and retries when the conflict is
an unsealed draft; by default only our own client's, with
upload_file_replacing_draft_from opting into any client's to recover
across a daemon restart (which rotates our client uid). A committed file
of that name is never touched.

Live tests for the public-link round trip, custom-password links, revision
history, and the path helpers.
2026-07-19 13:17:30 +02:00
nvrl f75ecb04b6 event manager, concurrency, optimizations 2026-07-17 22:30:32 +02:00
nvrl 0a15a8c86e revision handler, parallel block download + gzip 2026-07-17 15:57:00 +02:00
nvrl 0e335e35ec updated readme 2026-07-16 16:47:46 +02:00
nvrl 75f707719f fast node walk without decrypt + fixed crypto dub bug 2026-07-16 16:40:42 +02:00
nvrl 27a10598b8 sharing fix 2026-07-16 01:20:17 +02:00
nvrl 2fa9b90265 feature (public link retrievel) 2026-07-15 20:37:00 +02:00
nvrl 64f977edb3 feature (devices+sharing) + upgraded rand and aeds 2026-07-15 19:34:40 +02:00
nvrl 78567bf00e version bump to 0.1.5 2026-07-14 15:04:04 +02:00
nvrl 7c9d16b405 added device support + sharing 2026-07-14 15:03:06 +02:00
nvrl a24785d1a9 added persistent key salts 2026-07-13 10:26:12 +02:00
nvrl 2e3c23803e added token refresh callback 2026-07-13 02:18:59 +02:00
nvrl de6b61256e version bump to 0.1.4 2026-07-10 16:00:59 +02:00
nvrl 0d07fdb084 added device support 2026-07-10 15:59:56 +02:00
nvrl 6291c6f478 Export PhotoUploadMetadata and bump version to 0.1.3 2026-06-30 23:34:04 +02:00
nvrl e8de3b09cc fmt 2026-06-30 04:34:29 +02:00
nvrl 8039793010 added badges + github templates 2026-06-30 04:33:31 +02:00
nvrl 800fe7b612 added ci 2026-06-30 04:26:37 +02:00
nvrl ec7de59851 updated readme 2026-06-30 04:21:04 +02:00
nvrl 0221a654c2 version bump 2026-06-30 03:30:48 +02:00
nvrl f702e56110 clippy + fmt 2026-06-30 03:29:40 +02:00
nvrl 9ec205f217 fixed photo deserial 2026-06-30 03:29:25 +02:00
nvrl 2716be77ef version bump 2026-06-28 17:12:12 +02:00
nvrl c7e8467fe2 added range + live test 2026-06-28 16:14:37 +02:00
nvrl 643f5a2c3b renamed for crate 2026-06-28 11:47:54 +02:00
nvrl c98ec2d55d cargo fmt + clippy 2026-06-28 11:41:51 +02:00
nvrl 70e573ebe1 bumped deps + fixed deprecations + session for live tests 2026-06-28 11:39:57 +02:00
nvrl b9742e122a added license, author, metadata 2026-06-28 10:33:59 +02:00
nvrl 20fe359162 photo, volume op tests + fixes 2026-06-28 10:25:54 +02:00
nvrl 52b594e524 added file tests + fixed client move_node 2026-06-27 01:12:37 +02:00
nvrl 1ae5555e01 added .env to gitignore 2026-06-26 20:48:11 +02:00
nvrl e2f5ab7ad6 fmt + Clippy 2026-06-26 20:47:06 +02:00
nvrl ec590fd52b fixed invalid manifest signature in pgp 2026-06-26 20:46:20 +02:00
nvrl 35d9d8f3dd dto 2026-06-26 20:00:34 +02:00
nvrl 1ac1b1e9a2 ExtendedAttributes ModificationTime + Plural EnumerateThumbnails + missing event scopes 2026-06-26 17:37:51 +02:00
nvrl 8eb7b8f56f implemented missing drive endpoints 2026-06-26 16:01:35 +02:00
nvrl 4c3058c618 fixed x-pm-appversion for test login 2026-06-26 14:21:09 +02:00
nvrl e15f2d3111 enumeration returns NodeUids (enumerate_folder_children_node_uids / enumerate_trash_node_uids), caller materializes via enumerate_nodes. noise dropped: BOM/deps/kt-enum-order/cs-account-refactor 2026-06-26 14:15:54 +02:00
nvrl 5685923034 upstream changelog for port 2026-06-26 14:06:50 +02:00
nvrl 2400e28e4a Update README.md 2026-06-25 17:34:11 +02:00
nvrl 69c09dac24 removed hidden-line markers from doc 2026-06-25 17:27:47 +02:00
nvrl b0eeb8cf0f updated readme + example 2026-06-25 17:26:02 +02:00
nvrl 5d4e20d4c2 feat(sdk/telemetry): implemented client telemetry 2026-06-25 15:55:45 +02:00
nvrl 20d9ed66c8 feat(drive/client): implement Photos client and integrate caching, telemetry, and events 2026-06-25 15:54:03 +02:00
nvrl d67c294a4b feat(drive/models): implement caching, sync events, and photos models 2026-06-25 15:54:03 +02:00
nvrl fdfab3df3f feat(sdk/account): support resolving public keys for authorship verification 2026-06-25 15:54:03 +02:00
nvrl edab8ff8e9 feat(sdk/crypto): add signature-verification primitives and keyring support 2026-06-25 15:54:03 +02:00
nvrl 1d889daa8c feat(sdk/cache): add generic persistent and encrypted cache primitives 2026-06-25 15:54:03 +02:00
nvrl 945ba614a5 feat(sdk/telemetry): introduce pluggable telemetry and instrumentation 2026-06-25 15:54:03 +02:00
nvrl d2f44d511c feat(sdk/http): implement automatic retries with exponential backoff and jitter 2026-06-25 15:54:03 +02:00