File-manager integration

Wusel integrates with the graphical file manager on two independent channels, both fail-soft (the mount works without either). For the design behind them see the Architecture; this page is the practical build and install guide, per file manager.

The two channels

Sidebar entry + sync status

libcloudproviders (D-Bus). Built into the daemon — no extra component to compile. Needs the session bus and a one-time system registration per machine (desktop install-provider). Consumed by GNOME Files; KDE/others do not consume it.

Per-file emblems + context-menu actions

a native file-manager extension (one per file manager, see below). It reads each file’s state from the FUSE xattr user.wusel.state and draws emblems / offers "make offline / free space". No scripting runtime.

The state xattr (the shared contract)

Every integration reads one extended attribute, so you can inspect it directly and any file manager’s plugin reads the same thing:

getfattr -n user.wusel.state ~/Wusel/Photos/Birdie.jpg
Value Meaning

online-only

No local copy yet; opening it fetches from the server (and caches).

cached

Opened before: a whole copy is in the LRU cache, but evictable.

pinned

Kept offline on purpose (pinned, or under a pinned dir/root).

pinned-stale

Pinned, but the copy we keep is out of date: the server has moved on.

modified

A local edit not yet uploaded.

Staleness is shown for pinned files only. For an ordinary cached file it just means the next read goes live, which is what a VFS does all day; a pin promises the file is there when the server is not, so an outdated copy is a promise half-kept and worth showing before someone opens it.

Beside it sits an explicit Update now action — in the context menu when a pinned-stale file is selected, and as wusel update <path> on the command line. It fetches the current version in place: deliberately not "unpin, then pin again", which would drop the eviction marker first, so a re-download that failed would leave the file outdated and unprotected.

Whether the user has to ask at all is [sync] refresh_pinnedmanual (emblem only), ask (one notification when the backlog grows, naming the first file and counting the rest) or auto (fetch by itself, but only over an unmetered connection). See the policy for what happens when the connection’s cost cannot be established.

The engine guarantees this read is local and network-free, so a file manager may query it for every visible file. Directories report a value only when pinned.

The attribute describes where a file is, not what it is — it is only meaningful inside the mount. Linux copies extended attributes along with the file, so a copy made outside ~/Wusel carries a dead value, and every reader must establish that the path is on a Wusel mount before trusting it. See Known limitations.

GNOME / Nautilus

A native C libnautilus-extension module in integration/nautilus/ (source
Makefile + README). It adds a per-file emblem and the context-menu actions Make Available Offline / Free Up Space (which call wusel pin/unpin).

libnautilus-extension has no hook for file activation (double-click) — Nautilus opens files via their MIME default, regardless of folder, so a mount cannot special-case it. Wusel therefore never overrides double-click; a file in ~/Wusel opens like any other. The planned collaborative open (wusel open) is a context-menu action, not a double-click override.

Every state gets a distinct, always-visible emblem (the OneDrive model). Current Adwaita has pruned its stock emblem set, so the extension ships its own icons (installed into the hicolor theme):

State Emblem

online-only

A cloud — lives on the server, not downloaded.

cached

A cloud with a check — downloaded now, but still evictable.

pinned

A green check — kept offline, always available.

pinned-stale

A green check with a warning — kept offline, but out of date.

modified

An up-arrow — a local edit awaiting upload.

Table 1. Prerequisites (compiler + Nautilus & GLib development files)
Distribution Packages

Fedora

sudo dnf install nautilus-devel glib2-devel gcc make

Debian / Ubuntu

sudo apt install libnautilus-extension-dev libglib2.0-dev gcc make

Build and install:

cd integration/nautilus
make
sudo make install     # the .so into the extension dir, emblem SVGs into hicolor
nautilus -q           # restart Nautilus so it loads the extension

make install also installs the emblem icons and refreshes the icon cache. make uninstall removes both. wusel must be on PATH for the menu actions.

The .so is tied to the Nautilus major version (extensions-4/ for Nautilus 4 / GTK4); it is built against the target’s own Nautilus. The distribution package builds and installs it — today that is the Fedora RPM — so end users install the package and it works: no make, no scripting runtime, no version/venv surface. This "native, not Python" choice is deliberate.

Emblem refresh (live, no F5): a menu pin/unpin re-reads the file when the command finishes (invalidate_extension_info). A background hydration (opening a file → cached) is pushed by the daemon: it emits a D-Bus signal at.itbh.Wusel.FileChanged(path), which the extension subscribes to and turns into the same re-read. The daemon reaches the session bus even when started over SSH (it falls back to $XDG_RUNTIME_DIR/bus).

Known limitation — live refresh is best-effort

The push refreshes reliably once the signal fires, i.e. when hydration completes — so a large or queued download delays it, and a very light open that reads below the hydration floor never triggers hydration (so no refresh) until the file is opened more. The emblem is always correct on the next view reload (F5). Making it fully immediate (e.g. show a transient "syncing" emblem the moment an open starts, and parallelize hydration) is a future improvement.

Menu labels follow the session locale (built-in de/en; proper gettext is a roadmap item).

Desktop indexers (default: excluded)

Since opening a file caches it, letting GNOME Tracker/LocalSearch index the mount would hydrate everything it walks — a traffic storm. So exclude_from_indexers is on by default: the FUSE root serves synthetic, local-only .trackerignore/.nomedia markers (never uploaded to Nextcloud) that Tracker honours to skip the whole subtree. Opt back in with [desktop] exclude_from_indexers = false in config.toml. KDE Baloo ignores such markers — exclude ~/Wusel in its own settings instead.

The markers cover indexers, not previews: a thumbnailer reads far enough into a file to hydrate it, and the desktop offers no way to decline a preview for a local mount. See Known limitations.

Desktop search (GNOME)

Instead of letting a local indexer walk the mount (a download storm, since opening a file caches it), the GNOME Activities overview searches Nextcloud directly: wusel search-provider is a D-Bus service (org.gnome.Shell.SearchProvider2) that GNOME Shell activates on demand and answers from the server’s Unified Search (/ocs/v2.php/search/providers/files/ search) — including server-side full text where the fulltextsearch app is present. Clicking a result opens the file locally in the mount when the path resolves, else the Nextcloud web page. Registration lives in integration/gnome-search/ (install.sh, system-wide); it needs no running mount (it loads the account credentials itself). A KDE KRunner equivalent is a later addition.

Other file managers (planned)

All read the same user.wusel.state xattr; only the presentation plugin differs — there is no single mechanism across desktops. Each of them owes the same scope check: decide that the path is on a Wusel mount before reading the attribute’s meaning off it, because a copy carries the value out of the mount (Known limitations).

  • KDE Dolphin: a C++ KOverlayIconPlugin (emblems) built against KIO/KF6, plus declarative KDE ServiceMenus (.desktop files, no code) for the actions. Dolphin does not consume libcloudproviders, so account-level sync status is not surfaced in the file manager there — per-file emblems and freedesktop notifications carry it instead.

  • Nemo / Thunar: their own extension interfaces, reading the same xattr.