Import the MetaMesh app family from Worph/AppStore
23 apps (MetaCore, the library/network/client tiers and 13 MetaFeeder*) move out of the general-purpose Worph/AppStore into their own store so the MetaMesh family releases on its own cadence. - asset URLs repointed to cdn.jsdelivr.net/gh/Worph/MetaAppStore@main - store scaffolding added (category/recommend/featured lists, licence, contributing, gitattributes) - psd-source/meta-icons-v2 icon generator follows the apps MetaMCP stays in Worph/AppStore: it is third-party metatool-ai software, not part of MetaMesh.
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
name: metafuse
|
||||
|
||||
services:
|
||||
# Backend service. Reachable on the pcs network as `metafuse-app`.
|
||||
# Public ingress goes through the `metafuse` hash-lock proxy below.
|
||||
metafuse-app:
|
||||
image: ghcr.io/worph/meta-fuse:1.1.0
|
||||
cpu_shares: 50
|
||||
container_name: metafuse-app
|
||||
hostname: metafuse-app
|
||||
restart: unless-stopped
|
||||
privileged: true
|
||||
|
||||
expose:
|
||||
- 80
|
||||
|
||||
# FUSE capabilities required for virtual filesystem
|
||||
devices:
|
||||
- /dev/fuse:/dev/fuse
|
||||
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
- DAC_READ_SEARCH
|
||||
- DAC_OVERRIDE
|
||||
|
||||
security_opt:
|
||||
- apparmor:unconfined
|
||||
|
||||
volumes:
|
||||
# Shared infrastructure state (leader election, Redis dump, service discovery)
|
||||
# Owned by MetaCore; mounted rw here for service registration + leader reads.
|
||||
- ${DATA_ROOT:-/DATA}/AppData/metacore/meta-core:/meta-core:rw
|
||||
|
||||
# FUSE virtual filesystem mount point
|
||||
- ${DATA_ROOT:-/DATA}/MetaFuse/Library:/mnt/virtual:shared
|
||||
|
||||
# Configuration storage
|
||||
- ${DATA_ROOT:-/DATA}/AppData/metafuse/config:/meta-fuse/config:rw
|
||||
|
||||
# Note: no /files mounts — files are accessed via WebDAV from MetaCore
|
||||
# (Architecture V3). MetaCore is the single source of truth for media files.
|
||||
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- META_CORE_PATH=/meta-core
|
||||
- CONFIG_DIR=/meta-fuse/config
|
||||
- SERVICE_NAME=meta-fuse
|
||||
- SERVICE_VERSION=1.0.0
|
||||
- BASE_URL=https://metafuse-$APP_DOMAIN
|
||||
- TZ=$TZ
|
||||
|
||||
# Note: Runs as root for FUSE mount operations
|
||||
user: 0:0
|
||||
|
||||
networks:
|
||||
- pcs
|
||||
|
||||
# OIDC perimeter for the MetaFuse dashboard. Self-registers as an OIDC client
|
||||
# with the platform's auth-registrar on first login (no secrets to manage).
|
||||
#
|
||||
# Naming: container_name MUST equal the URL-prefix of the public hostname so
|
||||
# the registrar's redirect-URI allowlist accepts `metafuse-<user>.<domain>`.
|
||||
metafuse:
|
||||
image: ghcr.io/yundera/appshield:2.0.9
|
||||
cpu_shares: 80
|
||||
container_name: metafuse
|
||||
hostname: metafuse
|
||||
restart: unless-stopped
|
||||
user: "0:0"
|
||||
|
||||
expose:
|
||||
- 80
|
||||
|
||||
environment:
|
||||
OIDC_REGISTRAR_URL: "http://auth-registrar:9092"
|
||||
BACKEND_HOST: "metafuse-app"
|
||||
BACKEND_PORT: "80"
|
||||
LISTEN_PORT: "80"
|
||||
REDIRECT_HOST_SUFFIXES: "${APP_DOMAIN},${APP_PUBLIC_IP_DASH}.nip.io,${APP_PUBLIC_IP_DASH}.sslip.io"
|
||||
|
||||
depends_on:
|
||||
- metafuse-app
|
||||
|
||||
labels:
|
||||
# ---- metafuse-${APP_DOMAIN} (gateway TLS) ----
|
||||
caddy_0: metafuse-${APP_DOMAIN}
|
||||
caddy_0.import: gateway_tls
|
||||
# Unauthenticated paths bypass the hash-lock proxy and hit the backend directly.
|
||||
caddy_0.0_handle: "/health"
|
||||
caddy_0.0_handle.reverse_proxy: "metafuse-app:80"
|
||||
caddy_0.1_handle: "/webdav*"
|
||||
caddy_0.1_handle.reverse_proxy: "metafuse-app:80"
|
||||
caddy_0.2_handle: ""
|
||||
caddy_0.2_handle.reverse_proxy: "{{upstreams 80}}"
|
||||
|
||||
# ---- metafuse-IP.nip.io (gateway TLS) ----
|
||||
caddy_1: metafuse-${APP_PUBLIC_IP_DASH}.nip.io
|
||||
caddy_1.import: gateway_tls
|
||||
caddy_1.0_handle: "/health"
|
||||
caddy_1.0_handle.reverse_proxy: "metafuse-app:80"
|
||||
caddy_1.1_handle: "/webdav*"
|
||||
caddy_1.1_handle.reverse_proxy: "metafuse-app:80"
|
||||
caddy_1.2_handle: ""
|
||||
caddy_1.2_handle.reverse_proxy: "{{upstreams 80}}"
|
||||
|
||||
# ---- metafuse-IP.sslip.io (Let's Encrypt) ----
|
||||
caddy_2: metafuse-${APP_PUBLIC_IP_DASH}.sslip.io
|
||||
caddy_2.0_handle: "/health"
|
||||
caddy_2.0_handle.reverse_proxy: "metafuse-app:80"
|
||||
caddy_2.1_handle: "/webdav*"
|
||||
caddy_2.1_handle.reverse_proxy: "metafuse-app:80"
|
||||
caddy_2.2_handle: ""
|
||||
caddy_2.2_handle.reverse_proxy: "{{upstreams 80}}"
|
||||
|
||||
networks:
|
||||
- pcs
|
||||
|
||||
networks:
|
||||
pcs:
|
||||
name: ${APP_NET:-pcs}
|
||||
external: true
|
||||
|
||||
x-casaos:
|
||||
architectures:
|
||||
- amd64
|
||||
- arm64
|
||||
author: Worph Team
|
||||
category: Media
|
||||
developer: Aptero Team
|
||||
|
||||
description:
|
||||
en_us: |
|
||||
**Access your media library through a beautifully organized virtual filesystem.**
|
||||
|
||||
MetaFuse creates a virtual view of your media (Movies, TV Shows, Anime) organized by metadata - while your actual files stay exactly where they are. Access via FUSE mount or WebDAV network share.
|
||||
|
||||
**Key Features:**
|
||||
- **Zero File Movement** - Your files stay in place, MetaFuse creates an organized virtual view
|
||||
- **FUSE Virtual Filesystem** - Mount as a local directory on your system
|
||||
- **WebDAV Network Access** - Access your organized library as a network drive from any device
|
||||
- **Smart Organization** - Files organized by title, year, season, episode automatically
|
||||
- **Real-time Updates** - Virtual filesystem updates instantly when MetaSort processes new files
|
||||
- **Plex/Jellyfin Ready** - Point your media server to the organized virtual library
|
||||
|
||||
**Requires:** MetaCore (install first)
|
||||
|
||||
**Perfect for:** Plex/Jellyfin users, media archivists, or anyone wanting an organized view without reorganizing files
|
||||
|
||||
fr_fr: |
|
||||
**Accedez a votre bibliotheque multimedia via un systeme de fichiers virtuel magnifiquement organise.**
|
||||
|
||||
MetaFuse cree une vue virtuelle de vos medias (Films, Series TV, Anime) organisee par metadonnees - pendant que vos fichiers restent exactement ou ils sont. Acces via montage FUSE ou partage reseau WebDAV.
|
||||
|
||||
**Fonctionnalites Cles:**
|
||||
- **Zero Deplacement de Fichiers** - Vos fichiers restent en place, MetaFuse cree une vue virtuelle organisee
|
||||
- **Systeme de Fichiers Virtuel FUSE** - Montez comme un repertoire local sur votre systeme
|
||||
- **Acces Reseau WebDAV** - Accedez a votre bibliotheque organisee comme un lecteur reseau
|
||||
- **Organisation Intelligente** - Fichiers organises par titre, annee, saison, episode automatiquement
|
||||
- **Mises a Jour Temps Reel** - Le systeme virtuel se met a jour instantanement
|
||||
|
||||
**Necessite:** MetaCore (installer en premier)
|
||||
|
||||
tagline:
|
||||
en_us: Virtual filesystem for organized media access - FUSE mount and WebDAV without moving files
|
||||
fr_fr: Systeme de fichiers virtuel pour acces media organise - Montage FUSE et WebDAV sans deplacer de fichiers
|
||||
|
||||
title:
|
||||
en_us: MetaFuse
|
||||
|
||||
icon: https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaFuse/icon.png
|
||||
screenshot_link:
|
||||
- https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaFuse/screenshot-1.png
|
||||
- https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaFuse/screenshot-2.png
|
||||
- https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaFuse/screenshot-3.png
|
||||
thumbnail: https://cdn.jsdelivr.net/gh/Worph/MetaAppStore@main/Apps/MetaFuse/thumbnail.png
|
||||
|
||||
index: /
|
||||
store_app_id: metafuse
|
||||
main: metafuse
|
||||
is_uncontrolled: false
|
||||
|
||||
tips:
|
||||
before_install:
|
||||
en_us: |
|
||||
**Requires MetaCore** - Install MetaCore first.
|
||||
|
||||
MetaFuse connects to MetaCore for metadata. The dashboard is protected by
|
||||
Yundera's built-in Authelia via OIDC — sign in with your existing Yundera account.
|
||||
|
||||
**FUSE Mount:** Your organized virtual library is exposed on the host at
|
||||
`/DATA/MetaFuse/Library/` — deliberately outside `/DATA/AppData/` so you and other
|
||||
apps (Jellyfin, Plex, Samba, …) can browse it. It is a *virtual* view of records
|
||||
MetaCore already holds, not a second copy of your files. See `rationale.md`.
|
||||
|
||||
# Maison (Yundera dashboard) configuration. Read in preference to x-casaos.
|
||||
# schema_version 2 is required for `folders`: an older Maison refuses the app
|
||||
# outright rather than starting it without its directories.
|
||||
x-compose-app:
|
||||
schema_version: 2
|
||||
# Created and chowned by Maison before any image is pulled, before any hook
|
||||
# runs and before the containers start — on the first boot and every one after.
|
||||
# Compose would otherwise create a missing bind source as a root-owned dir.
|
||||
folders:
|
||||
- path: /DATA/AppData/metacore/meta-core
|
||||
user: $PUID
|
||||
group: $PGID
|
||||
- path: /DATA/AppData/metafuse/config
|
||||
user: $PUID
|
||||
group: $PGID
|
||||
- path: /DATA/MetaFuse/Library
|
||||
user: $PUID
|
||||
group: $PGID
|
||||
# Same string as the caddy_0 label, so the tile link and the route agree.
|
||||
webui-host: metafuse-${domain}
|
||||
webui-path: "/"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
@@ -0,0 +1,59 @@
|
||||
# MetaFuse — Rationale
|
||||
|
||||
## What deviation / exception is being requested
|
||||
|
||||
1. `metafuse-app` runs `privileged: true`, `user: 0:0`, with `/dev/fuse`,
|
||||
`cap_add: [SYS_ADMIN, DAC_READ_SEARCH, DAC_OVERRIDE]` and `apparmor:unconfined`.
|
||||
2. It mounts `/DATA/MetaFuse/Library`, which is outside both
|
||||
`/DATA/AppData/metafuse/` and the standard user directories, with `shared` mount
|
||||
propagation.
|
||||
3. It mounts `/DATA/AppData/metacore/meta-core` — another app's AppData directory.
|
||||
4. `/webdav*` bypasses the SSO gate.
|
||||
|
||||
## Why it is necessary
|
||||
|
||||
1. MetaFuse *is* a FUSE filesystem. Mounting one from inside a container requires
|
||||
`/dev/fuse`, `SYS_ADMIN`, and an unconfined AppArmor profile; the `DAC_*` caps let the
|
||||
filesystem answer `getattr`/`open` for files owned by other uids. There is no
|
||||
unprivileged path to this feature.
|
||||
2. `shared` propagation is what makes the mount visible on the **host** at
|
||||
`/DATA/MetaFuse/Library` instead of dying inside the container's namespace. That host
|
||||
path is the entire point of the app: it is the directory the user (and Jellyfin, Plex,
|
||||
Samba, …) browse. It is deliberately *not* under `/DATA/AppData/` because it is
|
||||
user-facing content, not application state.
|
||||
3. `/meta-core` is MetaCore's service-discovery and leader-election directory. MetaMesh
|
||||
apps coordinate through it; MetaFuse writes its own service registration there and
|
||||
reads the current leader. MetaCore is a hard prerequisite of this app.
|
||||
4. WebDAV clients (media servers, file managers) authenticate at the WebDAV layer and
|
||||
cannot complete an interactive OIDC redirect.
|
||||
|
||||
## Security mitigations in place
|
||||
|
||||
- The interactive dashboard is behind AppShield + Authelia SSO
|
||||
(`ghcr.io/yundera/appshield:2.0.9`). Only `/health` and `/webdav*` are exempt.
|
||||
- `/DATA/MetaFuse/Library` is a **virtual** view: it projects records already held by
|
||||
MetaCore. It is not a second copy of the user's library and holds no independent data.
|
||||
- Everything MetaFuse persists is under `/DATA/AppData/metafuse/config/`, declared in
|
||||
`x-compose-app.folders`.
|
||||
- Image tags pinned; `cpu_shares` set on both services.
|
||||
|
||||
## Alternatives considered and rejected
|
||||
|
||||
- **Unprivileged FUSE (`fusermount` with `user_allow_other`).** The container still needs
|
||||
`/dev/fuse` and `SYS_ADMIN` to create the mount namespace; dropping `privileged` left
|
||||
the mount failing at startup.
|
||||
- **Mount under `/DATA/AppData/metafuse/Library`.** It would work, but the resulting path
|
||||
is not somewhere a user is expected to browse, and other apps' file pickers hide
|
||||
`AppData`. The whole feature is "a library folder you can open".
|
||||
- **Put `/webdav*` behind the SSO gate.** Every non-browser WebDAV client then fails to
|
||||
authenticate, which removes the reason the endpoint exists.
|
||||
- **Bundle a private MetaCore.** Duplicates the Redis/leader tier per app and breaks the
|
||||
single-source-of-truth model the ecosystem is built on.
|
||||
|
||||
## Data protection
|
||||
|
||||
MetaFuse stores no user media of its own — the library it exposes is a projection of
|
||||
MetaCore's records. Its own configuration lives in `/DATA/AppData/metafuse/config/`, inside
|
||||
the folder Maison archives and restores, so uninstall/reinstall preserves it. The shared
|
||||
`/meta-core` mount is read-mostly: MetaFuse writes only its own service-registration file
|
||||
under `/meta-core/services/`.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
Reference in New Issue
Block a user