Commit Graph
100 Commits
Author SHA1 Message Date
nvrl cf0d495f8c fix(fuse): join the worker pool before the runtime goes away, and log deep queues
Teardown flagged the FUSE worker pool closed but never joined it, so the
process could drop the tokio runtime while a worker was still inside a
job — which is where 2026-09-18's "A Tokio 1.x context was found, but it
is being shutdown" panic came from. `Workers::stop_and_join` now waits
for the jobs in flight, bounded at 10 s so a worker wedged on the network
cannot hold the stop the user asked for; what is still stuck is named in
the log and shutdown continues around it.

The queues stay unbounded — the only caller that could feel backpressure
is fuser's dispatch loop, and blocking that is the stall the pool exists
to avoid — but a lane now logs its depth every 256 jobs, so a queue that
keeps climbing is visible before it is reported as a slow mount.

Documents the hang as B91, with the journal evidence, what made it
undiagnosable, and the candidates the evidence does not yet separate.
2026-09-19 14:49:31 +02:00
nvrl bee15ee7cf feat(fuse,packaging): watch the daemon for stalls and answer the systemd watchdog
A supervisor thread ticks every 15 s and does three things: it WARNs
about a worker that has held one job for two minutes, a control request
that never returned, or a lane that has not completed anything while its
queue is non-empty; it samples resident size and the pending-op count
into the log every five minutes; and it answers the systemd watchdog.

The watchdog ping is deliberately not a heartbeat from a thread that
does nothing else — that would keep the unit "healthy" through exactly
the hang it is meant to catch. It goes out only after a real round trip
over the control socket, so it means "a client can still talk to this
daemon", which is the property that failed on 2026-09-18.

`sd_notify` is spoken directly rather than through libsystemd (READY,
WATCHDOG, STOPPING), including the abstract-namespace address; off
systemd every call is a no-op.

The unit gains `NotifyAccess=all` (rather than `Type=notify`, which
would fail the unit on the start timeout when no login is stored yet),
`WatchdogSec=120`, `Restart=always` — a hang is not a failing exit code
— and `MemoryHigh`/`MemoryMax` as a backstop against the 5.2 G growth
the hung run showed.
2026-09-19 14:45:15 +02:00
nvrl f24ac8a429 feat(fuse,core,cli): report what the daemon is doing, for the next hang
On 2026-09-18 the daemon hung: 43 minutes of CPU over 20 hours, 5.2 GiB
resident, silent from 17:10 until a manual restart the next day, and no
panic. Nothing in the journal said what it had been waiting on, and
nothing in the daemon could be asked.

- The worker pool publishes per-thread state: whether the thread is busy,
  how long it has been in that state, and the label of the job in hand.
  Every FUSE handler that hands work to the pool now names its job
  (`read`, `lookup`, `rename`), so a stuck worker is named rather than
  merely counted. Queue depths and per-lane completion counts are
  mirrored outside the queue lock.
- `Request::Diagnostics` / `pdfs diagnostics` reports those, plus
  resident size, the control handler count against its limit, the
  in-flight control requests with their ages, and the pending-op count.
- Built to answer while the daemon is wedged: atomics and `try_lock`
  only, and the one field that needs the database reads it on a throwaway
  thread with a 500 ms deadline.
2026-09-19 14:40:26 +02:00
nvrl f78f663740 feat(core,fuse): restore the whole trashed tree, and bump the SDK to 0.6.5
The trash is a flat list of uids, but a delete has a shape: a folder, and
often items inside it that were trashed separately beforehand. Restoring
one of those in isolation is not what it means — a folder came back
without the contents deleted with it, and a file came back inside a
folder that is still trashed, which is to say nowhere the user can see.

- Schema 29: `trash.parent_uid`, filled by the trash refresh from the
  node's parent, plus an index for the walk.
- `expand_restore` takes the connected piece of the trashed tree around
  what was asked for (every trashed descendant, every trashed ancestor)
  and orders it into waves, shallowest first: the server cannot put a
  node back under a parent that is still trashed. A uid the listing does
  not know about is restored on its own, because the listing is
  materialised in chunks and can be stale.
- `Core::restore` restores wave by wave, still streaming per-node
  outcomes, so one node the server refuses leaves the rest restored.

The SDK bump brings the 6cbf2f44 port: permanent API errors are no
longer retried on block transfers, expired block URLs are re-minted on
the whole-file and public-link download paths, and a server-supplied
Retry-After is capped at 120 s.
2026-09-19 14:31:40 +02:00
nvrl 470948c191 chore(version) bumped to 1.10.2 2026-09-16 23:05:09 +02:00
nvrl b095b9f6b0 fix(core,fuse): never stat a foreign mount from the local-file indexer
The "This computer" indexer walks $HOME, and `default_excludes` only kept it
out of the Drive mountpoint and our own state/cache dirs. A home directory
that also holds third-party FUSE mounts (sshfs, rclone) got walked into.

`scan` does ask for `same_file_system(true)`, which looks sufficient and is
not: `ignore` implements it as `is_same_file_system` -> `device_num(path)` ->
`path.metadata()`, run after the directory is queued. Learning that a mount is
foreign requires stat'ing it first, and against a mount whose server is gone
that stat never returns.

The consequence is worse than a stalled scan. The blocked thread sits in
uninterruptible FUSE wait, so it ignores SIGKILL: the daemon can never exit,
systemd's stop times out, and the unreapable process keeps the cache.db lock
the next start needs, turning one dead mount into a permanent restart loop.
No userspace recovery exists once a thread is in that state, so the fix has to
be to never enter it.

Exclude every mountpoint strictly below the scan root, read by path from
/proc/self/mounts (octal-unescaped, so a path with a space still matches).
`filter_entry` runs before the device stat, so the walker never touches them.
Re-read per scan rather than cached at startup: a mount that appeared since
the daemon started is the likeliest to be half-alive.

This does not change what is indexed — a nested mount is a different device,
so it was already going to be skipped. Only the stat in front of that decision
goes away.

Refs: docs/BUGS.md B90
2026-09-16 23:05:06 +02:00
nvrl f4d6580cd3 chore(version) bumped to 1.10.1 2026-09-15 15:13:22 +02:00
nvrl b2f62f70e1 chore(deps,core,gui): upgrade dependencies to latest
- keyring 3 -> keyring-core 1 + dbus-secret-service-keyring-store 1
  (crypto-rust). Same libdbus Secret Service backend; with no target the
  store matches items keyring 3 wrote, so stored sessions survive.
- ksni 0.2 -> 0.3 via the blocking API, with assume_sni_available so a
  tray that starts before the SNI watcher waits instead of failing. Drops
  the clap 2 / atty chain (Dependabot atty advisory).
- gtk4 0.11, libadwaita 0.9, webkit6 0.6, rusqlite 0.40, fuser 0.18,
  directories 6, sha1/sha2 0.11, base64 0.23.
2026-09-15 15:07:00 +02:00
nvrl dcb2ab7fac chore(version) bumped to 1.10.0 2026-09-15 13:59:24 +02:00
nvrl eb56f4a1e0 feat(fuse,core,gui,cli): one-request move+rename drain, show who shared an item
- A queued rename that also changes folder now lands as one `move-multiple`
  request carrying the target name instead of rename-then-move, so there is
  no half-applied state and no second call for stale name requirements to
  hit (B46 follow-up). A destination name collision moves under a conflict
  name in one step; same-folder renames still use the rename endpoint.
- Shared with me shows the inviter and invite date (GUI subtitle, `pdfs
  shared-with-me`), with a warning when the invitation signature does not
  verify against the claimed inviter's keys. `DirEntry` gains `shared_by`,
  `shared_at` and `shared_by_unverified`, all serde-defaulted.
- Bump `proton-sdk` / `proton-drive-rs` to 0.6.3 (anonymous-node moves,
  `TrashTime` trashed detection).
2026-09-15 13:59:13 +02:00
nvrl c28803b037 chore(changelog) updated changelog for 1.9.2 and 1.9.3 2026-09-08 14:58:48 +02:00
nvrl cd8474954b chore(version) bumped to 1.9.3 2026-09-01 17:00:43 +02:00
nvrl d7e07d042d Merge pull request #22 from kacperpaczos/fix/login-poll-backoff
fix(cli): back off while waiting for a stored session
2026-09-01 16:38:29 +02:00
nvrl 45852082c0 fix(cli,core): wake the parked login-wait daemon on sign-in
The login backoff had no wake-up path. A unit started at boot sits in
wait_for_session and reaches the ceiling within ~90s; the user then signs
in and `enable --now` is a no-op on an already-active unit, so the daemon
kept sleeping and the mount appeared up to a full interval late.

- service::enable_start now runs `enable` + `restart` instead of
  `enable --now`, so the GUI login and the tray's Connect interrupt the
  sleep immediately and still start a unit that is down.
- `pdfs login` restarts the unit when it is already active. It stays out
  of `enable` on purpose, so a headless setup driving `pdfs daemon` by
  hand is left alone.
- Lower the backoff ceiling from 60s to 15s. It is still an 80% cut in
  keyring/D-Bus wakeups (28,800/day -> 5,760/day) and it bounds the
  worst-case latency for paths that never touch systemd.

Also corrects the flat-3s-poll figure in the doc comment: 86400/3 is
28,800 checks a day, not 48,000.
2026-09-01 16:28:25 +02:00
nvrl 99ad3838e1 fix(GALLERY) fixed takeout date conversion + repair command 2026-08-28 13:06:50 +02:00
nvrl 16ad6548c8 chore(DOCS) updated changelog 2026-08-21 23:32:06 +02:00
nvrl 847ece17d7 version bumped to 1.9.1 2026-08-21 22:48:28 +02:00
nvrl 50d2acdaba Merge pull request #20 from emptyname-org/feature/files-thumbnails-statusbar
feat(gui): add file thumbnails and a browser status bar
2026-08-21 22:44:58 +02:00
nvrl 5176dd91e1 fixed icon 2026-08-21 04:13:11 +02:00
nvrl e88903d5ff chore(branding) added branding, best practice to repo 2026-08-21 04:09:58 +02:00
nvrl 8bd9481902 fix(ui) improved ux 2026-08-18 12:50:49 +02:00
nvrl b2d5870834 fix(ui) improved ux 2026-08-18 12:06:28 +02:00
nvrl 078774f6a1 release(1.8.2) 2026-08-17 00:49:24 +02:00
nvrl 326a9b4e92 chore(docs) updated changelog + bug states 2026-08-17 00:04:54 +02:00
nvrl 78dc72a34d release(1.8.1) 2026-08-16 23:33:43 +02:00
nvrl 438b2b463e chore(docs) updated changelog 2026-08-16 22:03:55 +02:00
nvrl 4cd49168c4 fix(drain) failed attempts 2026-08-16 22:02:27 +02:00
nvrl 481c3fe0c7 version bump + bug docs 2026-08-16 21:34:55 +02:00
nvrl 098da2e97a fix(log) log unreachable nodes 2026-08-16 21:34:12 +02:00
nvrl d03d3eac3f fix(drive) locks + 1.7.1 2026-08-16 20:41:03 +02:00
nvrl 433e206203 chore(changelog) 2026-08-16 00:54:55 +02:00
nvrl 35418eea3f fix(perf) read-only connection pool, States doesn't write to db, search optimized 2026-08-16 00:33:27 +02:00
nvrl e48c4bb4b2 fix(fs) block fetch, sequential prefetch, in memory LRU 2026-08-15 23:44:13 +02:00
nvrl 7ad9231858 fix(fs) locks, vacuum, recover fsynced writes 2026-08-15 19:17:56 +02:00
nvrl e396c6b4d6 fix(fs) performance, locks 2026-08-15 18:49:07 +02:00
nvrl b37ab78451 version bump 2026-08-12 16:16:16 +02:00
nvrl 6e3d2c335e improved search prompt 2026-08-12 16:15:27 +02:00
nvrl 14b6df7bce fix(prompt) cached file open instead of actualy files 2026-08-11 14:22:13 +02:00
nvrl 4d76462e62 chore(versio) bumped to 1.4.0 2026-08-10 15:35:59 +02:00
nvrl a5e03ad8da persistent entity cache, streaming batche 2026-08-10 15:22:04 +02:00
nvrl 2336ad9e87 added album to photos, renamed gallery view 2026-07-31 20:31:25 +02:00
nvrl d988236bfa wired new locations into gui, fuse acceptance with new permission based mounts 2026-07-31 19:37:12 +02:00
nvrl d78ddeab8e cargo fmt 2026-07-31 10:18:30 +02:00
nvrl b04cce3a23 fixed B78 2026-07-31 10:15:13 +02:00
nvrl 7e8403936b fixed permission error on on-demand mounts 2026-07-31 09:46:51 +02:00
nvrl 866a174c7a fixed rpm signing 2026-07-30 02:55:15 +02:00
nvrl 352117e806 fixed rpm signing 2026-07-30 02:23:49 +02:00
nvrl 4b5666eacb added .deb signing + version bump 2026-07-30 02:13:07 +02:00
nvrl 9bbd8eb982 fixed a migration bug where ProtonDrive becomse unwritable 2026-07-30 02:11:23 +02:00
nvrl 77198ed261 version bumped to 1.2 2026-07-29 21:36:24 +02:00
nvrl 0d79c46729 version bumped to 1.2 2026-07-29 21:30:10 +02:00
nvrl a8210ce8fd implemented shared with me + verification 2026-07-29 21:29:07 +02:00
nvrl 551e14b644 implemented shared id permissions 2026-07-29 19:26:57 +02:00
nvrl 7de667b386 implemented primary mount shared with me 2026-07-29 17:04:42 +02:00
nvrl 295bbeadac implemented share_access, POSIX read-only mode 2026-07-29 14:57:56 +02:00
nvrlandClaude Opus 5 32817870a5 chore(packaging): bump PKGBUILD pkgver to 1.1.1
Version bump in b93e41e updated Cargo.toml only, so the release
workflow's tag/manifest/PKGBUILD agreement check failed on v1.1.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEWKS8smdPfLMZacgDXjAR
2026-07-27 03:25:56 +02:00
nvrl b93e41efc5 version bump to 1.1.1 2026-07-26 20:18:45 +02:00
nvrl 082ef8522a fixed refresh pull bug for trash 2026-07-26 20:17:59 +02:00
nvrl 2e95655244 Merge pull request #19 from narrrl/release/1.1.0
release 1.1.0
2026-07-26 19:03:00 +02:00
nvrl 942cb9f583 fixed concurrency bugs + performance 2026-07-26 19:01:48 +02:00
nvrl 8470662c54 fixed race conditions + extendet test suite 2026-07-26 15:56:58 +02:00
nvrl a091c2f34f implemented sweep mode and better conflict handling 2026-07-26 02:06:58 +02:00
nvrlandClaude Opus 5 bca0fe34bc fix(fuse,core): key conflict detection on revision id, stop transient-name seals (B69, B70)
B69 — spurious `(sync-conflict)` copies came from `(mtime, size)` being used as a
revision identity. The server re-stamps a sealed revision with its own commit
time, so the drain diverted a write into a conflict copy of its own base; and
`plan_file` classified a no-baseline (local, remote) pair as `Conflict`, forking
every pre-existing file on first reconcile.

- `Baseline` carries `revision_id` (from the SDK's new `active_revision_id`),
  captured at write-open and preserved across supersede/rebaseline.
  `revision_changed` trusts the id and only falls back to `(mtime, size)` for
  old sidecars.
- Planner gains `FilePlan::AdoptBaseline`: equal-size no-baseline pairs are
  recorded, not conflicted. Divergent sizes stay a real conflict.
- New `sweep.rs` (`pdfs-conflict-sweep`, 5-min cadence) removes conflict copies
  proven identical by size + `content_sha1` and surfaces divergent ones once as
  an `ActivityKind::Conflict` entry. Never removes what it cannot prove.

B70 — browsers downloading into an on-demand mount had their in-flight temps
sealed as canonical revisions, exiling the finished file to a conflict copy.
`syncignore::is_transient_name` now recognises download/scratch temps, and the
on-demand `release`/`rename` paths hold them (`set_create_hold`) instead of
sealing until the name is finalized.

Workspace deps bumped to proton-sdk/proton-drive-rs 0.2.2 (registry, no patch).

Live validation against a real account still pending for both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8P42asymU158CPnhjRwri
2026-07-25 15:24:31 +02:00
nvrl 6df757006a updated documentation + readme 2026-07-22 15:29:34 +02:00
nvrl 6f70eca78e Merge pull request #18 from narrrl/release/1.0.0
release/1.0.0
2026-07-22 15:18:00 +02:00
nvrl 0d8a474442 version bump 2026-07-22 15:11:33 +02:00
nvrl 8be0f04b61 fixed fuse bugs from BUGS.md
- Durable scratch/staging publication and preservation on failure.
      - Transactional pending-operation supersession.
      - Conflict copies cannot overwrite older copies or lose the source.
      - Single-entry wipe protection and incomplete-scan aborts.
      - Failed local deletion retains its baseline.
      - Downloads are fsynced before baseline settlement.
      - Future database schemas are refused.
      - Event cursors advance only after successful invalidation and persistence.
      - IPC frame, timeout, and concurrency limits.
      - Atomic config saves; malformed configs are preserved.
      - Sensitive directories require correct ownership, real directories, and 0700.
      - Debian package now includes service and tray autostart files.
      - Release workflow enforces versions, tests, lint, formatting, and offline acceptance.
      - CI now runs offline filesystem acceptance.
2026-07-22 14:49:06 +02:00
nvrl d5165b36a2 Merge pull request #17 from narrrl/feature/testing
Feature/testing
2026-07-22 14:29:39 +02:00
nvrl a76f36bdff fixed fs bugs + compleance test for fuse fs 2026-07-22 14:24:40 +02:00
nvrl e9009dd47c Merge pull request #16 from narrrl/fix/prompt
Fix/prompt
2026-07-22 11:51:43 +02:00
nvrl d0b49d3c8c fixed a truncate defect 2026-07-22 11:50:58 +02:00
nvrl 25f578161d added more comprehensive testing 2026-07-22 11:43:17 +02:00
nvrl ea56818a80 prompt refactor 2026-07-22 11:27:57 +02:00
nvrl 39ab4aae48 prompt refactor 2026-07-22 09:50:42 +02:00
nvrl 6cba77d3cb Merge pull request #15 from narrrl/fix/ioctl-handler
Fix/ioctl handler
2026-07-22 03:07:30 +02:00
nvrl 26375885ed updated workflow 2026-07-22 03:04:59 +02:00
nvrl 544d0913e8 added fuse compliance testing 2026-07-22 03:04:40 +02:00
nvrl 714b3048dc Merge pull request #14 from narrrl/fix/ioctl-handler
filesystem fixes, critical + feature implementations
2026-07-22 02:30:57 +02:00
nvrl bef39f75cf sync refactor 2026-07-22 02:22:40 +02:00
nvrl 0a1fa95eb4 refactor 2026-07-22 02:17:19 +02:00
nvrl 242a28bb8f refactor from monolithic structure 2026-07-22 02:02:26 +02:00
nvrl bd17d506f8 fixed rm before online del, mirror bug 2026-07-22 01:52:10 +02:00
nvrl 1cd6a26be3 fix(fuse,core): remediate audit bugs B23, B26-B29, B33 and update BUGS tracking
- B23: Generate synthetic StagedWrite sidecars in rescue_scratch when sidecar is corrupted
- B26: Add open_count and unlinked state fields to defer node removal and cache eviction on unlinked open files until release
- B27: Discard orphaned write revisions for local UIDs unlinked prior to upload
- B28: Add emergency_evict to ContentCache for LRU blob eviction upon ENOSPC
- B29: Implement FUSE forget handler and forget_lookup in state to prune unreferenced inodes
- B33: Quarantine failing drain ops on HTTP 40x / max retries to unblock offline sync queue
- docs: Track audit items B18-B36 in docs/BUGS.md with fix details and verification results
2026-07-22 01:18:35 +02:00
nvrl fe75998cea fix(filesystem): resolve critical data loss, POSIX non-compliance, and deadlock vulnerabilities
- Standardize POSIX unlink (EISDIR) and rmdir (ENOTDIR/ENOTEMPTY) checks
- Defer local directory eviction in mode switch until FUSE mount verification succeeds
- Enforce fsync before atomic renames in ContentCache store operations
- Add directory ancestor loop check in rename (EINVAL)
- Implement punch_hole interval tracking in fallocate
- Purge un-created local node write revisions on unlink
- Fix control socket permission race with octal umask 0o77
- Add unit tests for state, DB, and FUSE POSIX logic (208/208 tests passing)
2026-07-22 00:23:55 +02:00
nvrl 9d55ffb9f6 fixed io lock with alloced files from for example aria2c 2026-07-21 23:23:20 +02:00
nvrl 2275004f79 impplemented fallocate 2026-07-21 22:39:35 +02:00
nvrl 914ff2b5ae added ioctl handler 2026-07-21 16:42:41 +02:00
nvrl 01677f2670 updated documentation on bugs/features 2026-07-21 10:35:53 +02:00
nvrl 3f9d36607a updated readme 2026-07-21 02:12:50 +02:00
nvrl f1399aa661 Merge pull request #13 from narrrl/feature/selective-sync
Feature/selective sync + diagnostics
2026-07-20 23:09:47 +02:00
nvrl a3d9d05777 added settings for this device + quota in settings 2026-07-20 23:08:23 +02:00
nvrl bf625bf8d9 added functionality to open shared files with you + fixed bug where mid transfer the SIGTERM signal can corrupt the mount 2026-07-20 22:30:16 +02:00
nvrl e0f0c7e0b7 added json output 2026-07-20 20:44:37 +02:00
nvrlandClaude Opus 4.8 a6a925667a add --json output to the query commands
Scripts and health checks had to scrape formatted text, which changes whenever
the display does.

--json is global and applies to status, ls, pins, sync list, devices list,
transfers, activity, and cache inspect. Action commands keep their human
output; a script that needs to know whether one worked has the exit code.

Two decisions worth recording:

The daemon's own reply is serialized rather than a hand-built parallel
structure, so new fields reach the JSON without anyone remembering to add them
-- but the variant tag is stripped first. Serializing CtlResponse directly
yields {"SyncFolders": {"items": [...]}}, which would make every caller write
jq '.SyncFolders.items[]' and couple it to the name of an internal enum
variant. Callers get {"items": [...]}.

A daemon-side error prints its payload and then fails the command. The first
cut exited 0 after printing the error body, which was caught by running
`pdfs --json ls NoSuchFolder`: a script would have read a failed lookup as
success. The body is still emitted so `kind` is available, but the exit code
is what `set -e` and `if pdfs ...` actually test.

status composes its object by hand instead, being the one query that merges
the local keyring with the daemon -- including the logged-in-but-no-daemon
case, which has no daemon reply to pass through at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JFzNQyb4TWrCRWcnegqWxY
2026-07-20 20:38:48 +02:00
nvrlandClaude Opus 4.8 e259016a00 checkpoint the WAL after the vacuum, not before
Verified against a live install: vacuum reported "34.3 MiB freed (170.3 MiB
-> 136.0 MiB), 0 WAL frame(s) checkpointed" while the WAL grew from 67 MiB to
143 MiB. Net disk use barely moved, and the frame count was zero because the
checkpoint ran before the VACUUM, when there was nothing to fold.

Reordering is strictly better, but the commit message should not overstate it:
the live inflation happens because the daemon's other connections hold read
transactions and a TRUNCATE checkpoint waits on readers, so under a busy
daemon this can still move little. journal_size_limit stays the backstop --
the live WAL did fall back to its 64 MiB cap on its own afterwards.

The accompanying test deliberately does not assert on WAL size or frame count.
Measured both ways, a single-connection test reports frames=0 and a 0-byte WAL
under either ordering, because SQLite folds the log itself without a
concurrent reader to block it; an assertion there would pass on the bug and be
decoration. It asserts what is reliably true instead -- that a file-backed
vacuum reclaims space and leaves the database sound -- and the ordering
rationale plus the residual uncertainty are documented on vacuum() itself.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JFzNQyb4TWrCRWcnegqWxY
2026-07-20 20:32:18 +02:00
nvrlandClaude Opus 4.8 471f57b8e1 add cache inspect/vacuum and a diagnose self-check
A corrupt database or an oversized cache previously left the user with no
answer but "delete the state directory and re-sync", which for a large
account means re-downloading everything to fix a problem that may have been
a few stale rows.

pdfs diagnose reports on paths, keyring, daemon, and mountpoint, and exits
non-zero if any check fails so it works in a health check. Every check is
written to run with no daemon, because that is the situation it gets run in:
a stopped daemon is reported as a finding rather than aborting the report.
Writability is tested by writing, since permission bits say nothing about a
full disk or a read-only remount.

pdfs cache inspect reports database size, reclaimable space, per-table row
counts, and cache usage; --deep adds SQLite's integrity check, opt-in because
it reads every page. The report distinguishes "checked and clean" from "not
checked" rather than letting an empty problem list imply a clean bill of
health it never gave.

pdfs cache vacuum checkpoints the WAL and compacts the database.

Deliberately no orphaned-cache-row pruning: ContentCache::reconcile already
rebuilds the index from what is on disk at every open, so a second pass here
would duplicate it against a worse view of the truth. The WAL is likewise
left to journal_size_limit, which already caps it -- a periodic checkpoint
would paper over a problem that no longer exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JFzNQyb4TWrCRWcnegqWxY
2026-07-20 19:54:42 +02:00
nvrlandClaude Opus 4.8 3ade754ea2 add .pdfsignore selective sync for mirror folders
Mirror folders walked and uploaded every path under their root, so syncing a
project directory pushed node_modules/, target/, and .git/ to Drive -- quota
spent on files nobody wants there, and enough API traffic to invite rate
limiting.

Rules come from a .pdfsignore (or .protonignore) at the folder root, unioned
with an ignore_patterns list in config.json that defaults to the usual build,
VCS, and editor-leftover globs. Both are re-read at the start of every pass,
so edits apply without restarting the daemon.

The filter deliberately sits on the classification union in
classification_order(), not on walk_local alone. do_reconcile classifies
local | remote | baseline, so a file synced before a rule started matching it
is absent from local but present in the other two -- the "local deleted,
remote untouched" arm, which calls trash_nodes. Filtering only the walk would
have meant that adding a line to .pdfsignore silently trashed files on Drive
on the next pass. Ignored baseline rows are left in place so un-ignoring
re-adopts the existing remote file instead of re-uploading it.

guard_local_wipe takes a filtered baseline for the same reason: an ignored
path is absent from the walk by rule rather than by loss, and counting those
as missing would trip the guard on every pass once a rule covered the tree,
wedging that folder's sync permanently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JFzNQyb4TWrCRWcnegqWxY
2026-07-20 19:40:40 +02:00
nvrl 661ae3cf7a fixed incorrect versioning for rpm build 2026-07-20 17:06:36 +02:00
nvrl f803bf3cd7 fixed github ci 2026-07-20 15:06:17 +02:00
nvrl 084740ad95 version bump to 0.6.2 2026-07-20 14:54:13 +02:00