Files
MetaAppStore/Apps/MetaSort
Manielle 8c98785e38 serve assets and the store archive from gitea-watch (the GitHub repo is gone)
github.com/Worph/MetaAppStore answers 404, and with it every jsDelivr
cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/ URL: the icons, thumbnails and
screenshots of all 44 apps were missing from the store listing, and the store
source in the README no longer downloaded.

- Asset URLs in every Apps/*/docker-compose.yml now point at
  https://gitea-watch.nsl.sh/metastar/MetaAppStore/raw/branch/main/Apps/{AppName}/
  (103 of the 109 distinct URLs checked: 200 image/png). The other 6, MetaGateway
  and MetaWatch screenshot-1..3, name files that have never been in this repo.
- README: the store source is https://gitea-watch.nsl.sh/metastar/MetaAppStore/archive/main.zip
  (public, checked to download anonymously).
- CLAUDE.md and CONTRIBUTING.md: the asset rule and its grep guard follow, so an
  import cannot bring back a jsDelivr URL unnoticed.
2026-10-19 16:06:27 +00:00
..

MetaSort - MetaMesh Ecosystem

MetaSort is part of the MetaMesh ecosystem, a suite of 4 interconnected apps that work together to organize and serve your media library.

The MetaMesh Ecosystem

App Role Port Dependency
MetaCore Leader election, Redis, WebDAV, Authelia SSO 80, 9091 None (install first)
MetaSort Metadata extraction, file indexing, plugin system 80 Requires MetaCore
MetaFuse Virtual filesystem (FUSE + WebDAV) 80 Requires MetaCore
MetaStremio Stremio addon with HLS transcoding 7000 Requires MetaCore
┌─────────────────────────────────────────────────────────────┐
│                     User's Media Files                       │
│              /DATA/Downloads, /DATA/Media                    │
└──────────────────────────┬──────────────────────────────────┘
                           │ (read-only bind mounts at /files/watch/*)
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                       MetaCore                               │
│  • Leader election (flock)                                   │
│  • Redis (metadata storage)                                  │
│  • Authelia OIDC (single sign-on for all dashboards)         │
│  • WebDAV file serving + dynamic mount management            │
└──────────────────────────┬──────────────────────────────────┘
                           │
           ┌───────────────┼───────────────┐
           ▼               ▼               ▼
      /DATA/AppData/metacore/meta-core    pcs (Yundera platform network)
      (bind mount)              (inter-container comms)
           │                           │
     ┌─────┴─────┐  ┌─────────────┐  ┌─────────────┐
     ▼           ▼  ▼             ▼  ▼             ▼
┌─────────┐ ┌─────────┐  ┌─────────────┐
│MetaSort │ │MetaFuse │  │ MetaStremio │
└─────────┘ └─────────┘  └─────────────┘

Shared Infrastructure

MetaCore Directory (/DATA/AppData/metacore/meta-core)

A bind mount shared by all MetaMesh apps. Contains:

/DATA/AppData/metacore/meta-core/
├── db/redis/          # Redis database (metadata storage)
├── locks/             # Leader election lock files (kv-leader.info)
└── services/          # Service discovery JSON files

Why a bind mount?

  • Persists independently of Docker volume lifecycle
  • Predictable location for debugging/backup
  • Survives app reinstalls

User media directories — only mounted into MetaCore

User media is bind-mounted only into the MetaCore container:

/files/                       (inside MetaCore container)
└── watch/
    ├── downloads/            # ← /DATA/Downloads (host bind, read-only)
    └── media/                # ← /DATA/Media     (host bind, read-only)

MetaSort, MetaFuse, and MetaStremio do not mount /files at all. They reach files through MetaCore's WebDAV endpoint (http://metacore/webdav/*), discovering the URL at runtime from the leader-info file in /meta-core/locks/kv-leader.info.

This matches the dev stack (Architecture V3) — MetaCore is the single source of truth for media files; siblings are stateless w.r.t. file storage.

Earlier versions had two problems here:

  1. A metamesh-files named volume mounted at /files with the user's media bind-mounted as subpaths. docker volume rm metamesh-files then recursively traversed the bind-mount targets (which shared inodes with the host paths) and deleted everything in /DATA/Downloads and /DATA/Media.
  2. Even after dropping the named volume, every sibling app duplicated the bind mounts of /DATA/Downloads and /DATA/Media — multiplying the blast radius of any future bind-mount footgun and breaking the "MetaCore owns files" architectural promise.

Mounting media only into MetaCore eliminates both issues.

Network (pcs)

All MetaMesh containers attach to the Yundera platform network pcs (declared external: true everywhere). No app-private docker network — services discover each other by container hostname over pcs.

Installation Order

MetaCore must be installed first — it provisions the Authelia secrets and OIDC clients that the other apps depend on.

  1. Install MetaCore → Creates Authelia secrets and OIDC clients (metasort, metafuse, metacore)
  2. Install MetaSort, MetaFuse, MetaStremio in any order

Each app's pre-install-cmd checks for /DATA/AppData/metacore/authelia/secrets/client-<app>.plain and aborts with an error if MetaCore hasn't been installed.

Volume Permissions

Resource MetaCore MetaSort MetaFuse MetaStremio
/DATA/AppData/metacore/meta-core rw rw rw rw
/DATA/Downloads, /DATA/Media ro (mounts at /files/watch/*) — (WebDAV) — (WebDAV) — (WebDAV)
/DATA/AppData/<app>/cache rw (own) rw (own) — rw (own)
/DATA/AppData/metafuse/config — — rw —
/DATA/MetaFuse/Library — — rw (FUSE mount) —
/DATA/AppData/metacore/authelia/secrets rw (creates) ro (oauth sidecar) ro (oauth sidecar) —
pcs network (Yundera platform) external external external external

Developer Notes

Adding a New MetaMesh App

  1. Add pre-install-cmd that checks for /DATA/AppData/metacore/authelia/secrets/client-<app>.plain
  2. Bind-mount /DATA/AppData/metacore/meta-core:/meta-core:rw for leader info / service registration
  3. Do NOT bind-mount user media — read files via MetaCore's WebDAV (http://metacore/webdav/*), URL discovered from /meta-core/locks/kv-leader.info
  4. Join the pcs network as external: true (Yundera platform-managed; do not declare an app-private network)
  5. Add an oauth2-proxy sidecar that reads its client secret from /DATA/AppData/metacore/authelia/secrets/client-<app>.plain
  6. Discover Redis at runtime by reading /meta-core/locks/kv-leader.info

Debugging

# Check Redis data (Redis runs in MetaCore)
docker exec metacore redis-cli keys '*'

# Check leader election
cat /DATA/AppData/metacore/meta-core/locks/kv-leader.info

# Check service discovery
ls /DATA/AppData/metacore/meta-core/services/

# Check what's mounted into a service
docker exec metasort ls -la /files/watch/

Testing Locally

The MetaMesh dev environment (meta-root-v2/dev/) mirrors this architecture: direct bind mounts of ${DATA_WATCH_PATH}, ./cache/test/files, and ./cache/meta-sort/plugin-output at /files/watch, /files/test, /files/plugin — no parent named volume.