App id (used everywhere): io.github.memergamer.LinuxFiles. Binary/launcher: files. Assembly: Files.dll
(apphost Files).
| Path | Purpose |
|---|---|
packaging/linux/io.github.memergamer.LinuxFiles.desktop |
Desktop entry (inode/directory, new-window action). |
packaging/linux/io.github.memergamer.LinuxFiles.metainfo.xml |
AppStream metadata. |
packaging/linux/icons/hicolor/<N>x<N>/apps/ |
Icons 16, 24, 32, 48, 64, 128, 256, 512 (PNG). |
packaging/linux/files |
Launcher: finds Files.dll (FILES_LIBDIR, next to itself, ../share/linuxfiles, /usr/lib/linuxfiles, ...) and execs the apphost if present, else dotnet Files.dll. |
packaging/linux/flatpak/ |
Flatpak manifest (repackages the publish tarball). |
packaging/linux/aur/linuxfiles-bin/PKGBUILD |
Guarded AUR binary and source recipe templates; generate checksum-pinned recipes before use. |
packaging/linux/appimage/build-appimage.sh |
AppImage builder; downloads pinned appimagetool + type2 runtime into .cache/tools. |
scripts/linux/build-flatpak.sh, gen-aur.sh |
Build the Flatpak bundle; render PKGBUILD + .SRCINFO with real checksums. |
scripts/linux/publish.sh |
dotnet publish to artifacts/linux-<rid>/. |
scripts/linux/install-local.sh, uninstall-local.sh |
User-local install into ~/.local. |
scripts/linux/install-host-root-helper.py |
Explicit companion host-helper install from a matching native release; does not alter sandbox permissions. |
scripts/linux/gen-icons.sh |
Regenerates the icons. |
flake.nix, scripts/linux/update-nix-flake.sh |
Nix flake (repackages the release tarball); the script bumps version and hashes. |
.github/workflows/package-linux.yml |
Manual / linux-v* tag: publish, AppImage, Flatpak, AUR recipe; tags also create a GitHub release. |
The repo has no SVG logo, so there is no scalable/ icon. Sizes 16/24/32/48/256 are copied from
Assets/AppTiles/Release/Square44x44Logo.targetsize-*.png; 64/128/512 are downscaled from the 600 px
Square150x150Logo.scale-400.png (so 512 is a downscale).
A proper SVG should replace these when design provides one.
Current source builds prefer native Wayland and retain X11 fallback. CI and scripts/linux/publish.sh fetch the pinned Uno fork release with build/fetch-uno-wayland.sh; contributors using dotnet build run that script first. The initial fork packages must be built, their hashes committed in LinuxFiles, and those exact bytes published before restore. Fetch/import fails until all eight reviewed pins exist. See the release asset contract, runtime overrides and build handoff. -p:UseUnoWayland=false or scripts/linux/publish.sh --x11 uses upstream X11 packages without fetching the fork. Alpha4 release assets and the current Nix application pin remain X11-only.
AUR binary and Nix packages use LinuxFiles release tarballs and require no Uno credentials/feed. Source AUR recipes declare the eight pinned Uno nupkgs, mark them noextract, and import them from $srcdir without downloads before publishing. AppImage uses host Wayland client/cursor/EGL and xkbcommon libraries; Flatpak uses its Freedesktop runtime and exposes both Wayland and X11 sockets. Validate both packaged hosts before publishing the next release and bumping Nix's application version/hashes. Previous package measurements and smoke checks below describe the older X11 builds.
scripts/linux/publish.sh # self-contained linux-x64 -> artifacts/linux-x64/
scripts/linux/publish.sh --rid linux-arm64 # arm64 (cross-publish; not tested)
scripts/linux/publish.sh --framework-dependent # needs a system dotnet 10 runtime
scripts/linux/publish.sh --tarball # also writes artifacts/files-<rid>.tar.gz
Trimming and AOT are explicitly disabled (PublishTrimmed=false, PublishAot=false). Measured on the
linux/l-pkg branch: self-contained linux-x64 is about 155 MB unpacked (276 files), 63 MB as .tar.gz. The publish
emits a few ILxxxx analyzer warnings (IL2026 for Microsoft.Xaml.Interactivity behaviours and
Assembly.GetType, IL2072 for Activator.CreateInstance/PageStackEntry, IL2122) plus many Uno0001
not-implemented warnings; these matter only if trimming/AOT is enabled later.
scripts/linux/install-local.sh [--from DIR] [--prefix ~/.local] copies the build to <prefix>/share/linuxfiles,
the launcher to <prefix>/bin/files, and installs the desktop entry (with an absolute Exec), metainfo and icons,
then runs update-desktop-database and gtk-update-icon-cache when present. uninstall-local.sh reverses it.
Uno's X11 host calls XSetClassHint but the class value was not verified at runtime here. The desktop entry assumes
StartupWMClass=Files (assembly/process name). Verify on a real X11 session with xprop WM_CLASS (click the window)
and adjust the .desktop if it differs; otherwise taskbar grouping and icon matching will fail.
All three consume the output of scripts/linux/publish.sh --tarball (artifacts/files-linux-x64.tar.gz), and
CI runs the same scripts (job package-linux-x64; on a linux-v* tag the release job attaches
files-linux-x64.tar.gz, files-packaging.tar.gz, Files-x86_64.AppImage and Files-x86_64.flatpak to a GitHub
release). Verified locally on Arch: all three build.
packaging/linux/appimage/build-appimage.sh [--arch x86_64|aarch64] [publish-dir] assembles an AppDir (AppRun sets
FILES_LIBDIR; the runtime is self-contained, host libs such as fontconfig, Wayland, xkbcommon and X11/GL/EGL come from the system) and runs
appimagetool. The tool (1.9.1) and the type2 runtime (20251108) are downloaded into the git-ignored .cache/tools
and verified against SHA-256 sums pinned in the script (they match the digests GitHub publishes for those release
assets). Nothing is installed system-wide; without FUSE the tool runs via APPIMAGE_EXTRACT_AND_RUN. Output:
artifacts/Files-<arch>.AppImage (about 61 MB). arm64 is wired (pinned sums) but only x86_64 was built.
Smoke run on the private Xvfb display (the Home page renders):
FILES_EXEC=artifacts/Files-x86_64.AppImage scripts/linux/headless-run.sh -s 25 -o /tmp/files-appimage
headless-run.sh honours FILES_EXEC (any launcher, including packaging/linux/files) instead of dotnet Files.dll;
for *.AppImage it sets APPIMAGE_EXTRACT_AND_RUN=1 and APPIMAGELAUNCHER_DISABLE=1 so a host AppImageLauncher
cannot pop up an integration dialog.
packaging/linux/flatpak/io.github.memergamer.LinuxFiles.yml, runtime org.freedesktop.Platform//25.08. The module
unpacks the self-contained publish tarball, so there is no .NET SDK, NuGet restore or network access in the build
(Flathub-friendly: for a release, point the file source at the release URL with its sha256).
scripts/linux/build-flatpak.sh [--bundle] runs flatpak-builder (needs the 25.08 Platform and Sdk installed) into
artifacts/flatpak-repo and optionally writes artifacts/Files-x86_64.flatpak; without flatpak-builder it only
validates the manifest structure. Built locally, installed from a throwaway FLATPAK_USER_DIR and run inside the sandbox on a private Xvfb.
XDG user folders (Quick access / Pinned): user-dirs.dirs is visible in the sandbox (flatpak bind-mounts the host
file at $XDG_CONFIG_HOME, which is remapped to ~/.var/app/<id>/config), $HOME is the real home, and
--filesystem=host exposes the folders, so no extra finish-arg is needed (xdg-* permissions would be redundant).
With a seeded real-path HOME the Desktop/Downloads/Documents/Pictures/Music/Videos pins appear, also when
user-dirs.dirs is missing (fallback to $HOME/<default names>). What does hide them is a HOME under /tmp:
flatpak gives the app a private /tmp, so a HOME seeded there looks empty (only the Recycle Bin is pinned). Seed
test homes outside /tmp. Defensive changes: LinuxUserDirectories honours XDG_<NAME>_DIR environment variables
before the file, and the first-run seed of pinned_folders.json is persisted only when at least one folder was
found, so a launch that could not see the folders retries instead of freezing an empty list.
finish-args rationale (each line is the minimum for a feature that exists in the code):
--share=ipc,--socket=wayland,--socket=x11,--device=dri: native Wayland with X11 fallback and graphics access. Keep unconditional X11 access soUNO_SKIA_HOST=x11works even when Wayland is available.--filesystem=host: a file manager needs the whole filesystem.--filesystem=xdg-run/gvfs,xdg-run/gvfs-fuseand--talk-name=org.gtk.vfs.*: mounted network/MTP locations through GVfs.--own-name=org.freedesktop.FileManager1: theShowFolders/ShowItems/ShowItemPropertiesservice (Files.Platform.Linux/DBus/FileManagerService.cs).--system-talk-name=org.freedesktop.UDisks2: drive listing, mount, unmount, eject.--talk-name=org.freedesktop.secrets: Secret Service credential store.--talk-name=org.freedesktop.Notifications: toasts.- Not granted:
org.freedesktop.Flatpak(flatpak-spawn --hostwould void the sandbox). The portal bus names (org.freedesktop.portal.*, used for the appearance setting) are always reachable. Opening files with other apps inside the sandbox therefore depends on the OpenURI/OpenFile portals; verify "Open with" behaviour when running the Flatpak for real.
packaging/linux/aur/linuxfiles-bin/PKGBUILD repackages the release tarballs (files-linux-x64.tar.gz plus
files-packaging.tar.gz, which carries the desktop entry, metainfo, icons, launcher and LICENSE-MIT) into
/usr/lib/linuxfiles + /usr/bin/files; x86_64 only. Both checked-in recipes are guarded templates:
they fail when sourced until scripts/linux/gen-aur.sh <version> <dir> [out] [binary|source]
generates a recipe with SHA-256 checksums and .SRCINFO. Binary mode (the default) uses the two release
assets above; source mode requires the exact GitHub tag archive saved as linuxfiles-<version>.tar.gz.
Obtain the input artifacts from a trusted release before generating checksums. CI generates the binary
recipe on tags and uploads it; the AUR remote is updated separately. AUR_BASE_URL=file:///dir can
point generated recipes at local inputs for offline testing. Native package builds and namcap validation
remain separate release checks.
First used for linux-v0.1.0-alpha1 (prerelease; install instructions are in .github/README.md, the release body is kept in docs/linux-port/release-notes/<version>.md). Create a draft release linux-vX.Y.Z[-pre] with the notes, optionally with hand-uploaded assets; CI replaces them with its own build.
- Undraft the release (GitHub UI, or
gh release edit linux-vX.Y.Z-pre -R MemerGamer/LinuxFiles --draft=false). Publishing creates the tag, which triggerspackage-linux.yml. Thereleasejob finds the existing release (drafts included) and runsgh release upload --clobberforfiles-linux-x64.tar.gz,files-packaging.tar.gz,Files-x86_64.AppImage,Files-x86_64.flatpak, a regeneratedSHA256SUMS, and the AURPKGBUILDandSRCINFO(the.SRCINFO; release assets cannot start with a dot). Notes and prerelease state are untouched. If no release exists for the tag, it creates one (prerelease when the version afterlinux-vcontains-).gen-aur.shvalidates the version strictly and keeps the real tag for the download URLs (_tag), whilepkgverdrops hyphens. Wait for the run to finish: the CI tarballs differ from the hand-built ones, so the checksums in the PKGBUILD only match the CI assets. - Download the CI's recipe and commit it in the AUR clone:
cd ~/Documents/aur/linuxfiles-bin gh release download linux-vX.Y.Z-pre -R MemerGamer/LinuxFiles -p PKGBUILD -p SRCINFO --clobber mv -f SRCINFO .SRCINFO git add PKGBUILD .SRCINFO && git commit -m "Update to X.Y.Z-pre" git push aur(from the same directory; the remote is the AURlinuxfiles-binrepo).
flake.nix (with flake.lock, pinned to nixos-unstable) exposes packages.x86_64-linux.{linuxfiles,default}, apps.x86_64-linux.default, overlays.default (adds pkgs.linuxfiles), and nixosModules.default. The package repackages the pinned release tarballs, patches all ELF files with autoPatchelfHook (including the retained Native AOT helper), and installs the app under $out/lib/linuxfiles. Runtime dlopen libraries are added to ELF runpaths rather than the launch environment, so child programs do not inherit Nix libraries. The unused CoreCLR LTTng tracing provider remains exempt from dependency checking.
Plain nix run and nix profile install retain .root-actions-disabled: all root actions remain off. Installing a profile cannot activate host polkit. NixOS administrators may opt in through their configuration flake with the compatible alpha4 runtime:
# inputs.linuxfiles.url = "github:MemerGamer/LinuxFiles";
# Add inputs.linuxfiles.nixosModules.default to nixosSystem.modules, then:
programs.linuxfiles = {
enable = true;
rootActions = true; # default: false
};The module selects the package's enableRootActions variant, removes its disable marker, enables security.polkit, and installs a generated action in share/polkit-1/actions. Its exec.path names exactly the invoked store helper, never a mutable profile alias. /etc/linuxfiles/root-actions is a root-controlled two-line deployment manifest (helper path, policy path, final newline). Discovery checks every manifest symlink traversal, immutable root-owned store entries, executable permissions, and the action's path and fresh auth_admin defaults. No user environment variable selects a helper or manifest. A changed helper path between preview and execution rejects the old confirmation; a malformed deployment never falls back to a native helper or a generic prompt.
pkexec uses /run/wrappers/bin before the system profile and /usr/bin//bin. The module enables security.polkit.enablePkexecWrapper when present; older nixpkgs uses security.wrappers.pkexec. Only pkexec is setuid, never the LinuxFiles helper. A session authentication agent and an administrator identity are still required. The narrow store exception accepts a root-owned sticky /nix/store with group write (1775), but rejects other write there, non-root ownership, and any writable entries beneath it. Native /usr helper/policy checks retain their existing restrictions.
Release integration: the current pin is 0.1.0-alpha8; like alpha4 to alpha7, it includes deployment-manifest and Nix elevation-wrapper discovery. Its passthru.supportsNixRootActions marker is true, so the module accepts rootActions = true. Overrides with a false or missing marker are still rejected by the runtime-capability assertion. For future releases, verify that both runtime changes are present before retaining the compatible-runtime marker.
Per release (after CI has attached SHA256SUMS; the flake's tag is linux-v<version>):
gh release download linux-vX.Y.Z[-pre] -R MemerGamer/LinuxFiles -p SHA256SUMS --clobber
scripts/linux/update-nix-flake.sh X.Y.Z[-pre] SHA256SUMS # rewrites version + both hashes in flake.nix
Commit flake.nix in a PR. It is deliberately not a CI step: the hashes only exist after the release job has uploaded the assets, and main takes changes through PRs. Run nix flake update separately to bump nixpkgs.
The earlier plain package passed nix flake check --no-build and nix build with nix-portable. The checks.x86_64-linux.root-actions-module check covers rejection of packages with a false or missing capability marker, opt-out defaults, and compatible-runtime module wiring using the pinned alpha4 package. When built, it also checks helper presence, marker state, and the generated policy path. Re-run nix flake check against the locked revision after the release bump; sandboxed DNS failures cannot establish NixOS evaluation or packaged loader startup. See the headless checklist below.
.github/workflows/nightly-linux.yml builds the tarball and AppImage on every push to main and replaces the
single rolling pre-release at https://github.com/MemerGamer/LinuxFiles/releases/tag/nightly (stable asset names
LinuxFiles-nightly-linux-x64.tar.gz and LinuxFiles-nightly-x86_64.AppImage; the Flatpak is only built for
linux-v* tags). Pull requests targeting main get the same files as workflow artifacts with a read-only token.
Try it: curl -LO https://github.com/MemerGamer/LinuxFiles/releases/download/nightly/LinuxFiles-nightly-x86_64.AppImage && chmod +x LinuxFiles-nightly-x86_64.AppImage && ./LinuxFiles-nightly-x86_64.AppImage
inode/directory: already in the desktop entry'sMimeType=. Users (or the installer's post-install hook) then runxdg-mime default io.github.memergamer.LinuxFiles.desktop inode/directory. Do not do this automatically from the package scripts.org.freedesktop.FileManager1: implementShowFolders,ShowItems,ShowItemPropertieson the session bus (Phase 6 task, inFiles.Platform.Linux). To make it activatable when the app is not running, install/usr/share/dbus-1/services/org.freedesktop.FileManager1.servicewithName=org.freedesktop.FileManager1,Exec=/usr/bin/files --dbus-activate(flag to be defined). The Flatpak equivalent is installed under/app/share/dbus-1/services/and covered by the--own-namefinish-arg already present.- Each package recipe above needs the
.servicefile added to its install step at that point.
- Real
WM_CLASSvalue on X11 (see above). - Whether an SVG logo can be supplied for
scalable/. - Runtime behaviour of the Flatpak (sandboxed run, portals) and a
namcaplint of the AUR package. - arm64 publishing has not been tested.
Native AUR packages install a root-owned Native AOT helper at /usr/lib/linuxfiles/files-elevation-helper and packaging/linux/io.github.memergamer.LinuxFiles.root-actions.policy under /usr/share/polkit-1/actions/. Install polkit 0.105 or newer with distributor security fixes to enable helper actions in root mode. The helper is never setuid; the action uses auth_admin without retaining authorization. AOT publishing requires clang and zlib development files.
For local installation, publish normally and opt in with scripts/linux/install-local.sh --install-root-helper. This separately runs sudo python3 scripts/linux/install-root-helper.py --from artifacts/linux-x64/elevation-helper --sha256 <published-helper-sha256>; the app stays in the user prefix. This is equivalent to sudo make install: trust the installer checkout and build output. The policy is embedded; a private root-owned helper snapshot is hashed and checked before installation. Before writing system files, the installer queries available /usr/bin/pacman -Qo, /usr/bin/dpkg-query -S, and /usr/bin/rpm -qf tools for ownership of /usr/lib/linuxfiles, the helper, and the policy. It refuses to overwrite package-owned paths: omit --install-root-helper and use the distro package's helper instead. Tarball users without package-owned paths keep the same /usr/lib/linuxfiles/files-elevation-helper and /usr/share/polkit-1/actions/ installation; the launcher and app's fixed helper/policy trust checks remain compatible. To inspect the package layout without privileges, run python3 scripts/linux/install-root-helper.py --from <published-helper-dir> --sha256 <published-helper-sha256> --destdir <temporary-staging-dir>. Remove the system helper and policy as administrator when uninstalling; the user-local uninstaller intentionally cannot remove system files.
AppImage and Flatpak omit the helper and Open in Root Mode desktop action, and retain their full elevation disable gates. A separately installed host helper is an administrator opt-in for native host integration; installing it does not enable root actions in either current format. No sandbox permission or runtime gate is changed here. Flatpak still has no org.freedesktop.Flatpak permission and no flatpak-spawn --host bridge. AppImage still refuses its user-controlled extraction directory for privileged helpers.
To provision only the host helper, verify and unpack the matching native tarball from the same release (the helper is not in the AppImage or Flatpak). Verify the tarball against the release SHA256SUMS, inspect the trusted installer checkout, and record the approved helper digest:
sha256sum -- /path/to/linux-x64/elevation-helper/files-elevation-helper
python3 scripts/linux/install-host-root-helper.py --from /path/to/linux-x64 --sha256 <approved-helper-sha256>The companion script invokes the existing hardened installer through literal argv and sudo. It installs only /usr/lib/linuxfiles/files-elevation-helper (0755, never setuid) and the fixed-path policy (0644), verifies a private snapshot and digest, and refuses package-owned destinations. NixOS users must use the module instead of this /usr layout. For inspection without privileges, add --destdir /tmp/linuxfiles-helper-stage. Re-run explicitly for each matching release update; remove those two system files as administrator when retiring a manually installed helper. No installer grants persistent polkit authorization or changes the user's desktop settings.
For root file operations today, run the matching native tarball/local install or enabled NixOS package. A future AppImage host discovery path or Flatpak transport needs separate implementation and review; a host helper alone is insufficient. See the elevation threat model for protocol, failure semantics and limitations.
Normal launches show only Root actions → Open in terminal as root, independently of helper installation; authentication happens through the terminal elevation tool. Launch files --root or choose Open in Root Mode from the desktop entry to additionally show Delete/Rename/Paste as root when the trusted helper is installed. Each action requests polkit authentication; the GUI remains unprivileged. Tabs and the title show (Root mode) only when helper actions are allowed by the package/disable gates. Each --root invocation opens its own process/window and bypasses normal single-instance forwarding, so an existing ordinary window never acquires root mode. New Window preserves --root. These separate processes never claim FileManager1.
For an actual uid-0 launch (sudo files), tabs/title show (Running as root), ordinary operations already run as root, and all helper/root-terminal actions are hidden. The normal terminal command remains available. Before app initialization, HOME and XDG config/data/cache/state are reset to root's account home/defaults; XDG_DATA_DIRS and XDG_CONFIG_DIRS are reset to /usr/local/share:/usr/share and /etc/xdg, and PATH to /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin. Inherited TERMINAL, EDITOR, VISUAL, BROWSER, SHELL, XDG_CURRENT_DESKTOP, user runtime/session-bus, GVfs and TMPDIR overrides are cleared. Unsafe root-owned directory configuration aborts startup before settings writes. This also protects the invoking user's settings when sudo preserves HOME or XDG variables. AppImage/Flatpak retain their full elevation disable gate, including the root-terminal item, even with --root, and show no root-mode indicator when helper actions are disabled.
Run app checks only with FILES_SANDBOX_DRIVES=scripts/linux/showcase-drives.txt scripts/linux/headless-run.sh (private Xvfb and throwaway HOME); never use the real display. The harness blocks the system bus, so authentication and privileged operations additionally require a disposable NixOS VM with private Xvfb, a controlled polkit agent, and disposable test files.
- Plain Nix launch and
--root: marker keeps all root actions and the root-mode indicator hidden. Enabled NixOSfiles --root: root-mode title/tabs, Delete/Rename/Paste menus and independent root-terminal action appear while GUI uid remains unprivileged; ordinary launch stays outside helper mode. - Packaged helper starts with the runner's cleared environment: its patched loader and dependencies work, and polkit selects the action whose
exec.pathexactly matches the displayed/invoked store helper. Root-terminal elevation selects the trusted Nix wrapper. - Confirmation shows the exact helper, sanitized paths/operation, stdin JSON summary and request SHA-256. Cancel/dismiss leaves files unchanged; approved delete, rename, copy and move on disposable protected files report complete/partial results correctly.
- Switch NixOS generations while a dialog is open: the old helper confirmation refuses execution; a fresh plan shows and uses the new helper/action. Disabling the module or breaking helper/policy/manifest trust keeps helper actions unavailable without fallback.
- AppImage and Flatpak with the companion installed still hide all root actions and root-mode indicators, including
--root; ordinary browsing and existing portals continue working with unchanged sandbox permissions.