Mori

A private, offline media and file browser.

by Ernest Magriñá Privacy & SecurityPhoto & MediaUtilities

Type
Desktop app
Runs on
macOS · Windows · Linux
Get it
Download
Price
€0+ Pay what you want

How it’s built

Source available

The source is public to read, but not under an open-source license.

No license has been chosen yet.

Repository
App
  • Tauri 2
  • Rust
  • React
  • TypeScript
  • Vite
Isolation
  • Sandboxed decoder workers
  • macOS system sandbox
  • Content-Security-Policy

Architecture

A Tauri 2 desktop app: a React and TypeScript interface running in the system webview, and a Rust backend that owns everything touching the disk. The split is deliberate — the interface is where untrusted names and previews get displayed, so it's also the part given the least power. File bytes only reach it through a custom mori:// protocol.

Threat model

Mori assumes media from an unfamiliar drive may be malformed or deliberately crafted: image and container bugs, decompression bombs, disguised extensions, symlink tricks, filenames that spoof their own extension. It treats decoding itself as the risky step and keeps that work away from the rest of the app.

Isolated decoding

Images are decoded in short-lived worker processes by memory-safe decoders. A worker receives bytes over a pipe, never a path, and returns a freshly encoded image the host validates. On macOS it enters the system sandbox before reading any input; pixel, memory, input-size and time caps apply everywhere, and a crash or hang fails only that one preview.

Security model

Least privilege first. The interface has no filesystem, shell or HTTP access; its Tauri capability allow-lists only Mori's own commands, and it works with opaque file ids rather than paths. File types come from magic bytes, filenames have control and bidi-override characters replaced before display, and every change to your files passes a backend mutation policy.

Filesystem boundaries

Every file access goes through Rust: the id is looked up in the index, .. and absolute components are rejected, the path is canonicalised and checked against the canonical root (which defeats symlink escapes), and the file is opened without following links.

Indexing & browsing

Opening a drive loads its last index snapshot immediately while a background thread rescans it. Each entry carries a pre-folded search key — lower-cased, accents stripped, Unicode normalised — so "cumpleanos" finds "cumpleaños". Gallery, grid and list are one virtualised view, so folders with tens of thousands of files stay smooth.

Builds & verification

macOS v1.0.1

Not signed with an Apple Developer ID or notarized yet, so macOS warns once. On macOS 15 and later, open it from System Settings → Privacy & Security → Open Anyway; on earlier versions, Control-click Mori and choose Open.

Apple Silicon · Apple Silicon· 6.6 MB sha256:28bd2e3e44335285e4f4569e02de2dedfe988fbed0f7bb8ade6b62bfd207bb4c
Intel · Intel· 7.0 MB sha256:8ba7c138955e63f4fc55977045406e1abaf008676489c1761168cc3a9c376aff

Windows v1.0.1

Not signed with a publisher certificate yet. SmartScreen says "Windows protected your PC" the first time — choose More info → Run anyway.

Installer · x64· 3.5 MB sha256:010e2a6079cb874bebd795adc061bf6870818f90af4fb2b24036575ceab85b30
MSI package · x64· 5.0 MB sha256:f757c9e61c72a85f52115994a5569aad4f0d2b69fc6f1521d04cb09f83ea1a97

Linux v1.0.1

AppImage · x64· 83.0 MB sha256:5310b961ea2c2e463a9cbe14d5cca5855311bcdf707183fd529c2ba8c6c86b8d
.deb package · x64· 4.7 MB sha256:afb78a2b07ca06eaeac74725079405dc3cc7a3ad528a7edcdbf3fd8299d3759b