10852 Commits
Author SHA1 Message Date
Lucas WintherandClaude Opus 5.5 fbf2d565e8 docs: bring the Proton Drive handoff up to date after the final review
ci / protondrive (push) Successful in 23m29s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 15:12:51 +01:00
Lucas WintherandClaude Opus 5.5 944b360054 protondrive: follow the renamed Proton code constant in the fake's tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:55:58 +01:00
Lucas WintherandClaude Opus 5.5 ad3552e2e5 docs: note the Proton Drive block size cache in the architecture
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:48:33 +01:00
Lucas WintherandClaude Opus 5.5 bfa9b7122b protondrive: tighten tests and docs from the final review, log the rest
Timing bounds in parallel tests leave room for a slow race runner, the
block 502 test checks its fault fired, refusedCommit's 408 exclusion is
pinned, the user docs explain the "server doesn't show it yet" error and
the conformance checklist matches the current draft and trust rules.
Every other minor from the review is listed in docs/TODO.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:48:17 +01:00
Lucas WintherandClaude Opus 5.5 d3aee2af19 protondrive: tidy small review findings
Log lines no longer cut a name inside a UTF-8 sequence, the "superseded
upload" error says the file's state rather than claiming a revision is
active, a test-only block decrypter moves to the tests, a one-line alias
of encryptPassphrase goes, the upload godoc is rewrapped and the fake's
server struct is gofmt clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:46:52 +01:00
Lucas WintherandClaude Opus 5.5 30cfcac416 protondrive: retry a token refresh that failed with a server error or timeout
When the access token expired and the refresh request got a 5xx, 429 or
408, the operation failed instead of being retried, and a 408 even ended
the session as if the refresh token had been rejected. Such failures are
now retried like any other call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:45:39 +01:00
Lucas WintherandClaude Opus 5.5 634a2423eb protondrive: make the tests honour RCLONE_CONFIG whichever tests run
Only fstest.Initialise, run by the integration test, applied
RCLONE_CONFIG, so a filtered test run read the default rclone config,
which may hold a real Proton remote. TestMain now applies it first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:44:18 +01:00
Lucas WintherandClaude Opus 5.5 b5da26169a ci: give the Proton Drive race suite room on a slow runner and pin its tools
The race suite ran under Go's default 10 minute limit, which a slow
runner would hit; it now has 40 minutes in a 60 minute job. Go is the
1.27 line the code was verified with, not go.mod's minimum, so
govulncheck checks a patched standard library, and govulncheck is
pinned to v1.9.0 so a new release can't break the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:41:48 +01:00
Lucas WintherandClaude Opus 5.5 b4a23a31ed protondrive: log Proton's announced drive requirements at debug level
Proton announces requirements it will enforce, such as a minimum client
version, in x-pm-drive-requirements response headers. They are now
logged at debug level whenever they change, so they show in -vv bug
reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:41:19 +01:00
Lucas WintherandClaude Opus 5.5 5dbf5b04c6 protondrive: stop telling users to upgrade rclone on unrelated Proton errors
Proton's generic "requirements not met" code 2000, which it returns for
a node changed since it was read (as when a lost rename response is
resent), aborted the whole run with "upgrade rclone". It is now an
ordinary error that isn't retried. Code 2028 (a feature disabled,
rate-limited or blocked) is retried, and the codes Proton actually uses
for a rejected or missing app version, 5003 and 5001, now give the
upgrade message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:40:13 +01:00
Lucas WintherandClaude Opus 5.5 788a711dc9 protondrive: make multi-thread downloads of files without block sizes linear
A file whose extended attributes don't record its block sizes, as some
other clients write, was read from the first block for every ranged
read, so a multi-thread copy, which opens each chunk as a range,
downloaded data quadratically: about 8.5 GiB for a 1 GiB file. The sizes
of blocks already read are now remembered per Fs (bounded to 16
revisions and 65536 blocks), so later chunks start at the right block.
A block list that disagrees with what was learnt is an integrity error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:37:40 +01:00
Lucas WintherandClaude Opus 5.5 674b538ca5 protondrive: keep the draft client ID stable for remotes configured from the environment
A remote set up with environment variables or a connection string can't
save its draft client ID, so it got a new random one on every run, and a
draft left by an earlier run then blocked uploading that name as if
another client were uploading it. When the ID can't be saved it is now
derived from the remote name and the account's volume, so it is the same
on every run. A remote in the config file still gets a random ID, saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:32:14 +01:00
Lucas WintherandClaude Opus 5.5 94eedc942f protondrive: ignore modification times and hashes the owner didn't sign
Extended attributes whose signature failed or was missing were used, so
a server could set a file's modification time, size and SHA-1 at will:
for example make `rclone sync --update` skip every changed file. Without
ignore_signature_errors they are now not used and an ERROR is logged:
the modification time is the server's, there is no SHA-1, and the size
comes from the recorded block sizes, which reads check. The file stays
readable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:29:40 +01:00
Lucas WintherandClaude Opus 5.5 67e4cfeb7a protondrive: never reuse the session key of an unverified name on rename or move
Renaming or moving a node whose name signature failed reused the old
name's session key, which a server that made that name knows, so it could
read the new name. The new name now gets a new session key unless the
old name's signature verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:26:02 +01:00
Lucas WintherandClaude Opus 5.5 b3f5685e62 protondrive: refuse writing into a folder whose name hash key is unsigned
A folder's hash key whose signature failed or was missing was only
logged, and the names of everything written into the folder were then
hashed with it, so a server which made the key could test guesses at
those names. Creating, uploading, renaming, moving or copying into such
a folder is now refused unless ignore_signature_errors is set, when an
ERROR is logged once per folder. Reading is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:24:56 +01:00
Lucas WintherandClaude Opus 5.5 db7afafc08 protondrive: refuse node keys whose Argon2 parameters would exhaust memory
A node or share key from the server was unlocked with whatever Argon2
parameters it carried, so a crafted key could make every listing
including it allocate up to 2 TiB and crash rclone. Node and share keys
locked with Argon2 beyond 4 passes, parallelism 8 or 128 MiB are now
refused before the key derivation runs, as an integrity error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:23:50 +01:00
Lucas WintherandClaude Opus 5.5 e95317b52a protondrive: refuse files whose content key signature is missing
A server could replace a file's content key with one it made, without a
signature: updates were then encrypted with a key the server knows, and
ranged reads returned whatever it encrypted under that key. A missing or
invalid content key signature now makes the file unverified, like an
unverified node key: reads and updates are refused unless
ignore_signature_errors is set, when an ERROR is logged once per file and
operation. The fake gains corruptions for this and for the hash key,
name and extended attribute checks that follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:21:14 +01:00
Lucas WintherandClaude Opus 5.5 ea8d09ce43 docs: say which Proton Drive test files can't go upstream without the fake
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 14:10:53 +01:00
Lucas WintherandClaude Opus 5.5 29ad269444 docs: record the Proton Drive live verification results
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 13:53:13 +01:00
Lucas WintherandClaude Opus 5.5 b440df2e1f docs: note the conflict-cleanup delete after an unknown upload outcome
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:33:37 +01:00
Lucas WintherandClaude Opus 5.5 61733fce6e protondrive: add re-keyed fixtures of files written by Proton's web app and CLI
Captured from a throwaway test account on 2026-11-12 and re-keyed onto
test-only keys; no key, passphrase, ID or email of the account is in them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:32:19 +01:00
Lucas WintherandClaude Opus 5.5 f8e398fce1 protondrive: skip the live integration test during a golden capture
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:31:02 +01:00
Lucas WintherandClaude Opus 5.5 be00536078 protondrive: never delete an upload's draft unless Proton refused the commit
After a failed commit the client read the file back and deleted the
draft if the read showed it uncommitted. Proton's server can take a
moment to show a commit, so a stale read could send a delete against a
file or revision which had in fact been committed.

The draft is now deleted only when the server refused the commit with a
4xx API error on a single attempt which was not resent; any other
failure leaves it and reports that the upload may have succeeded. A
cancelled upload whose commit succeeded stops waiting for the server to
show it at once. The fake server's read lag can also serve a file
without an active revision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:30:07 +01:00
Lucas WintherandClaude Opus 5.5 1b2fcbd212 protondrive: make the golden capture fail closed on foreign keys
The capture names its folder with -capture-remote and refuses -remote,
so a mistyped -run can't start the integration tests against the
account. Every key written has a placeholder user ID; a node key holding
another user's email, or a signature by or intended for a key not being
replaced, fails the capture without naming it. Salt notations are
written only where the original had one.

TestGoldenFixtures now also walks every key, key packet and signature of
a fixture, inside encrypted messages too, and checks folder modification
times; tampered fixtures are shown to fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:19:05 +01:00
Lucas WintherandClaude Opus 5.5 af6fdd7899 protondrive: fix uploads failing while Proton catches up after a commit
Proton's server can take a moment to show a change: a file read back
just after its commit may be missing or still a draft with no active
revision. Uploads then failed with "upload committed, but the file now
has another revision (revision  is active ...)" or "object not found".

The read back after a commit, the read of a server-side copy and, with
enable_caching off, the read of a folder to write into now retry with
backoff for about 7 s while the change doesn't show. A committed upload
the server still doesn't show after that fails with a new "committed,
but the server doesn't show it yet" error, deletes nothing and is not
retried at once. The fake server gains SetReadLag to model the lag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 11:57:02 +01:00
Lucas WintherandClaude Opus 5.5 a2dc6f71f1 protondrive: test against re-keyed files written by Proton's own clients
TestGoldenCapture (-capture DIR -capture-label NAME -remote NAME:PATH)
captures a folder another client wrote, re-keyed onto keys made for the
tests: blocks and signature-free SEIPD packets kept byte for byte, key
packets re-wrapped, signatures re-made with the same parameters (names,
hash keys and extended attributes encrypted again with their own session
keys, as their signatures are embedded), IDs and emails replaced.
TestGoldenFixtures reads every fixture in testdata/golden through the
backend from the fake server with every signature checked.

The fake server can now seed stored folders and keeps the times of
seeded nodes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 11:35:36 +01:00
Lucas WintherandClaude Opus 5.5 502a32b5f3 protondrive: name the SDK commit the client was last checked against
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:30:31 +01:00
Lucas WintherandClaude Opus 5.5 22b9c4fdc3 protondrive: fix ranged reads of files which don't record block sizes
A file written by an old client without recorded block sizes was read
in ranges by assuming 4 MiB blocks, so a range past the first block could
return the wrong bytes, or too few, without an error. Such a range is now
read from the first block, discarding the bytes before it, with every
block still checked against its hash.

Also identify the SDK the conformance checklist was checked against by
commit, and align the enable_caching help with the docs page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:19:53 +01:00
Lucas WintherandClaude Opus 5.5 7be50f9e22 protondrive: update docs for the rewritten backend
Rewrite the hand-written parts of the Proton Drive docs page for the
native client: browser and password sign-in with a transcript taken from
the current code, the third-party notice, what is verified on upload and
download, the ranged-read caveat, the trust rules and the
ignore_signature_errors option, the two protocol limits, the --dump
warning, filename rules, drafts, trash and limits. The generated options
section is left for make backenddocs.

Describe everything ignore_signature_errors allows in its help text,
update the feature list and score in protondrive.yaml, and add the SDK
conformance checklist and fork notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:06:19 +01:00
Lucas WintherandClaude Opus 5.5 c1464a39ab docs: mark Task 19 done in the Proton Drive handoff
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 09:54:41 +01:00
Lucas WintherandClaude Opus 5.5 fd7a0dc98d chore: allow golangci-lint run in the Claude Code settings
The owner added the rule; docs/PERMISSIONS.md already listed it in the
Allow tier. Also record that the force-with-lease deny rule misses the
`--force-with-lease=` form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 09:53:08 +01:00
Lucas WintherandClaude Opus 5.5 cf7577dc1d protondrive: drop Proton-API-Bridge and go-proton-api
The old libraries were only used by the differential crypto tests. Those
go, and TestFrozenFixtures moves into crypto_internal_test.go without its
-update branch, so files the old library wrote stay checked through the
frozen fixtures in testdata/oldlib. go mod tidy removes the two libraries
and the modules only they pulled in; relvacode/iso8601 leaves go.mod.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 09:51:43 +01:00
Lucas WintherandClaude Opus 5.5 b667a1ab0f docs: record the owner's answers, the history rewrite and Task 18 in the Proton Drive handoff
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 09:29:46 +01:00
Lucas WintherandClaude Opus 5.5 5afbe9e4fa protondrive: run the wider suites against the fake server in fork CI
Add the TestProtonDriveFake init.d script, which builds and starts
protonfake on port 28642 and passes on the session it prints, and a
test_all entry for it, so fs/operations, fs/sync, vfs and cmd/bisync
run against the fake server. Five tests that need a second Fs of the
same account to see what the first wrote are ignored, as each Fs
caches separately, and cmd/gitannex is skipped as for every init.d
remote; both are in docs/TODO.md.

Add a Gitea Actions workflow that builds, vets, lints, race-tests,
fuzzes and vulnerability-checks the backend, and re-verify the agent
permission table on the host toolchain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 09:23:26 +01:00
Lucas WintherandClaude Fable 5.2 65840bdc57 docs: record open questions and credential locations in the Proton Drive handoff
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 21:24:07 +01:00
Lucas WintherandClaude Fable 5.2 4282540e02 docs: correct the Proton Drive handoff after a cold read
build / windows (push) Skipped
build / other_os (push) Skipped
build / mac_amd64 (push) Skipped
build / mac_arm64 (push) Skipped
build / linux (push) Skipped
build / go1.26 (push) Skipped
build / linux_386 (push) Skipped
build / lint (push) Skipped
build / android-all (push) Skipped
Build & Push Docker Images / Build Docker Image for linux/386 (push) Skipped
Build & Push Docker Images / Build Docker Image for linux/amd64 (push) Skipped
Build & Push Docker Images / Build Docker Image for linux/arm/v6 (push) Skipped
Build & Push Docker Images / Build Docker Image for linux/arm/v7 (push) Skipped
Build & Push Docker Images / Build Docker Image for linux/arm64 (push) Skipped
Build & Push Docker Images / Merge & Push Final Docker Image (push) Waiting for needed jobs
A reader with only the repository walked the handoff and the remaining
plan tasks against the code. Mark the plan and spec lines that later
rulings superseded, add corrections for Tasks 18 to 21, fix stale TODO
and PERMISSIONS entries, ignore the skill workspace, point AGENTS.md at
the handoff and take the test account usernames out of it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 21:21:43 +01:00
Lucas WintherandClaude Fable 5.2 31f6aafd91 docs: hand off the paused Proton Drive backend rewrite
Development of plan 001 is paused after Task 17 of 21. Record the state,
the rulings made during execution, the test setup and how to resume in
docs/plans/001-protondrive-tier1-HANDOFF.md, commit the execution ledger,
task briefs, implementer reports and reviews, and mark the plan and spec
with their current status.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 21:09:43 +01:00
Lucas WintherandClaude Opus 5.5 9c90998f76 protondrive: correct the memory a full read may hold and note what remains unbounded
The comment and spec put the block metadata a full read holds at a few
hundred bytes a block; with realistic tokens and URLs the cap allows
about 250-350 MiB, and those fields and API response bodies aren't
bounded at all. TODO also asks the live check to confirm the
root-of-trust rule on a drive made by Proton's own apps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:46:18 +01:00
Lucas WintherandClaude Opus 5.5 0cd3af4295 protondrive: return only the bytes asked for when a range covers the whole file
A range covering the whole file was read as a plain full read, so a
file whose recorded size understated its content returned more bytes
than the range asked for. It is now checked as a full read but stops
at the end of the range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:45:54 +01:00
Lucas WintherandClaude Opus 5.5 8666b66038 protondrive: stop at a folder the server reports as its own ancestor
Fetching a file or folder fetches its folders up to one already known,
and a server reporting folders as each other's parents made that
recurse until rclone crashed. A cycle, or folders nested more than 2048
deep, is now an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:44:19 +01:00
Lucas WintherandClaude Opus 5.5 52a44e41ce protondrive: log an error for each unverified file or folder used with ignore_signature_errors
With ignore_signature_errors set, reading a file, writing into a folder,
or moving, copying or renaming either when its key isn't verified went
ahead without a word. Each now logs an error, once per file or folder
and operation, saying what was done and why the key isn't trusted - and
that what is moved or copied out is trusted from then on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:42:14 +01:00
Lucas WintherandClaude Opus 5.5 15bc60ead9 protondrive: verify your own address in any letter case with your own keys
The check that the drive is shared and signed by your own address
ignored letter case, but keys were looked up by exact address, so a
server naming your address in capitals could serve keys of its own for
it. Addresses are now matched ignoring ASCII case only, everywhere, and
your own address in any such case is always verified with your own
keys, never ones the server supplies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:40:43 +01:00
Lucas WintherandClaude Opus 5.5 c28077174e protondrive: check warm listings within the listing budget test
The warm listing budget, with caching on and off, reuses the folders
TestBudgetList seeds instead of seeding its own, which saves about 15s
under -race. The assertions are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:30:05 +01:00
Lucas WintherandClaude Opus 5.5 16480e3d80 protondrive: refuse to write names which aren't valid UTF-8
With an encoding which lets invalid UTF-8 through, rclone could create
a file or folder whose name Proton's clients, and rclone itself, then
refuse to read, so it vanished from listings. Such names are now
refused when writing.

Found by FuzzValidateName.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:28:21 +01:00
Lucas WintherandClaude Opus 5.5 18b4f7d1d1 protondrive: don't list a file as empty when its recorded size is missing or invalid
A file whose extended attributes had a SHA-1 but a missing, negative or
malformed Size was listed with size 0, so copying it failed with a size
mismatch. Its size is now the sum of its recorded block sizes, if they
are usable, and otherwise unknown, as for a file without extended
attributes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:27:33 +01:00
Lucas WintherandClaude Opus 5.5 f89be42eaf protondrive: don't cache a folder listing read while the folder changed
A listing whose pages were read before a file or folder was made in,
copied into or trashed from the folder cached what it read when it
finished, overwriting the change. As new files are added to a cached
listing rather than dropping it, the stale listing then lasted: a file
just uploaded stayed missing, and a trashed one stayed listed, until
something else dropped the listing.

The node cache now counts changes to the children of each folder being
listed, and a listing is cached only if none happened since its first
page was read. The count is kept only while a listing is in progress.

The fake server gains a Hold fault, which answers a request with the
response made on arrival only once released, to test this without
sleeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:25:35 +01:00
Lucas WintherandClaude Opus 5.5 e7e43095d8 protondrive: restore the config storage even when rclone's config is in memory
The password test's cleanup restored the config path before the
storage, and setting the storage does nothing while the path is empty,
so with an in-memory config the storage was left behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:10:25 +01:00
Lucas WintherandClaude Opus 5.5 2a1a8f046e protondrive: restore rclone's config storage after the password test
TestPasswordStoredObscured installed the config file storage for its
temporary config and left it in place for the tests after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:03:39 +01:00
Lucas WintherandClaude Opus 5.5 202b64cf1e protondrive: cap the number of blocks a file may list
A full read holds the list of a file's blocks until it has checked the
signed manifest, and how long that list could grow was bounded only by
a size the server sends unsigned. A file listing more than 2^19 blocks
(2 TiB in 4 MiB blocks) is now an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:03:18 +01:00
Lucas WintherandClaude Opus 5.5 58fde59f34 protondrive: check the signed block list when a range asks for the whole file
A request for the whole file as a range, as "bytes=0-" from serve http
or webdav clients, was read as a ranged read, which can't check the
file's signed block list. It is now read as a full read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:01:02 +01:00