Files
MetaAppStore/Apps/MetaWatch/rationale.md
T
Worph 39f82e1f96 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.
2026-09-13 16:18:29 +00:00

3.3 KiB

MetaWatch — Rationale

What deviation / exception is being requested

  1. metawatch-core, metawatch-search and metawatch-share run user: 0:0, and metawatch-core runs privileged: true.
  2. The stack publishes two host ports: 4003/tcp and 4004/tcp (libp2p).
  3. The app exposes three public hostnames — metawatch-, metawatchshare- and metawatchsearch- — each with its own AppShield sidecar, so two sidecar container_names differ from the compose top-level name:.

Why it is necessary

  1. metawatch-core is a bundled MetaCore: rclone remote mounts need a mount namespace (SYS_ADMIN + /dev/fuse + an unconfined AppArmor profile), plus nginx and Redis. metawatch-search and metawatch-share write service registrations into the root-owned /meta-core/services/.
  2. libp2p is not HTTP — Caddy cannot proxy it, and remote peers resolve this node's public multiaddr from the kad-DHT and dial it directly. Without inbound reachability the node never serves bytes to the swarm. The app therefore carries the reserved needs-public-ip tag.
  3. Each hostname is a distinct UI, and auth-registrar derives an app's OIDC client_id from the sidecar's container name via PTR lookup, so three routes need three stable, distinct names. x-casaos.main points at metawatch, the sidecar carrying the primary route.

Security mitigations in place

  • Every publicly-routed hostname terminates at its own AppShield sidecar (ghcr.io/yundera/appshield:2.0.9) behind the PCS's Authelia SSO. Each sidecar sets hostname: equal to its container_name so auth-registrar can attest it via PTR and accept its redirect URIs; the names are app-prefixed, so they cannot collide with another app's services in the shared pcs DNS namespace.
  • The backends carry no Caddy labels and publish no HTTP port to the host. The only published ports are raw libp2p TCP.
  • libp2p transport is authenticated and encrypted (Noise) and peer identity is a public key, so an unauthenticated dial cannot read anything the node has not chosen to publish to the swarm.
  • No user directories are mounted — no /DATA/Documents, /DATA/Downloads, /DATA/Media or /DATA/Gallery.
  • cpu_shares on every service; memory-heavy services carry a hard mem_limit with memswap_limit equal to it. All image tags pinned.

Alternatives considered and rejected

  • Proxy libp2p through Caddy. Not possible; the DHT advertises a raw TCP multiaddr.
  • One sidecar with path-based routing. The three backends generate absolute URLs and set cookies on their own hostnames; path-mounting them breaks both.
  • Drop privileged from metawatch-core for a cap_add list. Same container as MetaCore; the reduced form was tried and still failed to bring up the supervised daemons.
  • Use the shared-backend variant instead. That is MetaWatchShared, for a box that already runs MetaCore and MetaShare. This app is the self-contained one.

Data protection

All state lives under /DATA/AppData/metawatch/, declared in x-compose-app.folders and archived by Maison on uninstall, so an uninstall/reinstall with "keep user data" returns My List and Continue Watching intact. No user media directory is mounted into any service; bytes are served through the app's own transport peer.