Skip to content

Data Directory Layout

All persistent application data lives next to the executable. This is a portable layout — moving the .app bundle moves all its data with it. The paths are centralized in src-tauri/src/app_paths.rs.

Source: src-tauri/src/app_paths.rs


Layout

{exe_dir}/
  rutracker/
    session.json        reqwest cookie jar (Base64 JSON)
    meta.json           username, avatar data URL, active mirror
    proxy.txt           HTTP proxy URL (one line, optional)
    covers/             torrent post cover art disk cache
    webview/            WebView2 data directory (login window)

  soulseek/
    creds.json          login credentials { username, password }
    covers/             SoulSeek peer cover art disk cache

  bt/                   (debug builds: dev/bt/)
    torrent/            librqbit session state (export downloads)
    covers/             librqbit cover art session
    vozduxan/           vozduxan C++ libtorrent session state

  cache_settings.json   cache size limits and TTL overrides
  app_debug.json        debug log ring buffer (persisted on exit)
  general-list.json     debug track registry (every seen track)
  likes.json            liked track IDs array

  dev/                  debug builds only
    bt/torrent|covers|vozduxan/   isolated dev streaming sessions
    dumps/              search dump snapshots

Debug builds use dev/bt/ instead of bt/ so development libtorrent sessions (with their accumulated piece cache and peer lists) do not contaminate the production data on the same machine.


Path helpers

Every path in the application is obtained through a helper in app_paths.rs. Adding a new file requires adding a helper there — never hardcode a path string in any other module.

fn base(_app: &AppHandle) -> Result<PathBuf, String> {
    let exe = std::env::current_exe()?;
    exe.parent()
        .ok_or_else(|| "executable has no parent directory".to_string())
        .map(|p| p.to_path_buf())
}

base() is the executable directory. All other helpers build on it:

// RuTracker
pub fn rt_session_path(app: &AppHandle) -> Result<PathBuf, String> {
    Ok(rt_dir(app)?.join("session.json"))
}

// BitTorrent (debug vs release)
pub fn bt_vozduxan_dir(app: &AppHandle) -> Result<PathBuf, String> {
    Ok(bt_base_dir(app)?.join("vozduxan"))
}

pub fn bt_base_dir(app: &AppHandle) -> Result<PathBuf, String> {
    let b = base(app)?;
    Ok(if cfg!(debug_assertions) {
        b.join("dev").join("bt")
    } else {
        b.join("bt")
    })
}

Notable files

session.json

reqwest cookie jar in JSON format. Used by the RuTracker HTTP client to persist the phpBB session cookie across app restarts. On startup, rutracker_restore_session validates the cookies are still valid by making a live request; if not, it clears the file and returns logged-out state.

meta.json

{
  "username": "user123",
  "avatar_data_url": "data:image/jpeg;base64,...",
  "mirror": "rutracker.org"
}

The avatar is stored as a data URL because the WebView does not share cookies with the Rust HTTP client — loading the avatar URL directly would return a 403. Rust fetches it on login and serializes to base64.

vozduxan/

libtorrent session state: resume data for all known torrents, the DHT routing table, and the piece cache. This directory can grow large for users who stream many different albums. The cache size and TTL are configurable via cache_settings.json and enforced by vozduxan_session_evict.

general-list.json

A debug registry that records every track entity the app has ever seen. Written by general_list_write (Rust) via the general_list_append Tauri command. Used to reconstruct session history for debugging — not part of the normal user-facing feature set.


macOS app bundle note

On macOS, std::env::current_exe() returns the path inside the .app bundle:

neegde.app/Contents/MacOS/neegde

So bt/vozduxan/ lives at:

neegde.app/Contents/MacOS/bt/vozduxan/

This is intentional — data travels with the bundle. The bundle's Contents/MacOS/ directory is writable by the owning user on macOS (unlike Contents/Resources/).