Skip to content

About

Self-hosted media platform — neutral media server + clients (chino video; musig/tv planned).

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

224 Commits

Folders and files

Repository files navigation

Zaentrum

A neutral media client + server for a library you own and are entitled to stream. Bring your own files; Zaentrum catalogs, processes, and streams them to clean clients on the web, your phone/tablet, and your TV.


Try it in one command

docker run -d --privileged --restart unless-stopped -p 80:80 --name zaentrum \
  ghcr.io/zaentrum/appliance:latest
open http://zaentrum.localhost

That single container runs the whole platform: a full Kubernetes (k3s) in-process, the operator, and everything the operator brings up — the portal, the web app, the catalog and its consoles, streaming, and bundled Keycloak, Postgres, Valkey and Kafka. One image, one port, nothing else to install; the first boot pulls the platform's images from ghcr.io and takes a few minutes.

  • Port 80, and the name zaentrum.localhost. The platform's Ingress answers that host only, and its sign-in is bound to http://zaentrum.localhost — no other name, no other port. Modern browsers resolve *.localhost to 127.0.0.1, so this needs no /etc/hosts edit.
  • linux/amd64 only. No arm64 image is published yet.
  • This machine only. Phones, TVs and other computers need https — see phones and TVs.
  • Keep the container. The library and the database live in its volume, and replacing the container starts an empty platform — see persistence.

The appliance in detail: deploy/allinone/README.md.


First run

There is no setup wizard: a fresh install comes up configured, with the bundled Keycloak (realm zaentrum) and an empty library. Three steps make it yours (self-hosting → first run):

  1. Sign in as admin with the first admin password. The operator generates it once per install and keeps it in the Secret zaentrum-keycloak-admin; Keycloak then has you choose a password of your own:

    docker exec zaentrum kubectl -n zaentrum get secret zaentrum-keycloak-admin \
      -o jsonpath='{.data.realm-admin-password}' | base64 -d; echo

    On a cluster, run the same kubectl without docker exec zaentrum.

  2. Add a TMDB key under Catalog Management → settings (/katalog-manage/) before the first scan: titles, posters and plots come from TMDB, and the images carry no key.

  3. Copy your files into the library, then trigger a scan in Catalog Management. The library is the media/ folder of the platform's media volume — on the appliance, a directory inside the container (how to fill it).

Phones and TVs

Sign-in needs https everywhere but on the machine itself. Keycloak marks its login cookies Secure, which a browser keeps over plain http for localhost names only — over http://<lan-ip> Keycloak answers the login form with "Cookie not found" — and the Android phone and TV apps refuse plain http altogether and trust public certificate authorities only. The appliance serves plain http at zaentrum.localhost, so it is for the machine it runs on. For other devices, give the appliance a name and a certificate (how), or run the operator on a cluster under a real hostname with TLS (self-hosting, troubleshooting).

Identity

Zaentrum runs its own bundled Keycloak (realm zaentrum), or — with identity.mode: external on a cluster — validates the tokens of your own OIDC provider and renders no Keycloak. Those are the two modes the CRD accepts; federating your provider through the bundled Keycloak (broker, ADR-0007) is designed, not built.

Users are managed in Keycloak, whose admin console is not on the public host: the Ingress sends only /auth/realms and /auth/resources to Keycloak. Reach it through a port-forward on local port 8080 — the console's links point there — signed in as the bootstrap admin (keys username and password of the Secret zaentrum-keycloak-admin), then switch to the realm zaentrum:

kubectl -n zaentrum port-forward svc/keycloak 8080:80
open http://localhost:8080/auth/admin/

Or publish it on the public host with spec.identity.exposeAdminConsole: true. The details: operator/README.md; the appliance's variant: deploy/allinone/README.md.

Run it on a cluster

The same platform runs on any Kubernetes cluster through the operator, which reconciles the whole stack from a single Zaentrum resource. Install it once, as cluster-admin, from an install manifest — the CRDs, the cluster RBAC and the controller — then apply a Zaentrum:

# the newest release, its controller on operator:vX.Y.Z — for the stable channel (the default)
kubectl apply -f https://github.com/zaentrum/zaentrum-operator/releases/latest/download/operator-install.yaml
# …or the newest build of main, pinned to one immutable operator:sha-<commit> — for edge:
#   kubectl apply -f https://raw.githubusercontent.com/zaentrum/zaentrum-operator/main/deploy/operator-install.yaml
kubectl create namespace zaentrum
kubectl apply -f zaentrum.yaml     # your Zaentrum resource
kubectl -n zaentrum get zaentrum   # PHASE Ready once it is up

The operator renders the platform from the chart it embeds, so pair a release's operator with the release's images (Releases).

On OpenShift or any OLM cluster, the OLM bundle is the alternative; without the operator, helm install the chart it renders, operator/platform/chart.

spec.hostname is the name the platform answers at: the operator derives the OIDC issuer, Keycloak's KC_HOSTNAME and the Ingress host from it. Serve that name over https — with spec.tls, a certificate Secret or a cert-manager issuer (the platform's certificate), or TLS terminated in front of the Ingress and identity.issuerScheme: https — and network.issuerHostAliasIP so in-cluster token validation reaches the https issuer. A minimal resource and every field: self-hosting, operator & CR reference.

After every update the platform checks itself — outside-in through its public URL, with a real sign-in — and the operator reports the verdict in status.verification (kubectl get zaentrum, zae platform status). What runs, when, the test account and how to ask for a run: operator/README.md.

deploy/base, deploy/compose, deploy/k3s and deploy/overlays are older profiles kept in the tree; they do not bring up a working platform and are not supported (why).


Route map

One Ingress — or, with routing.provisionRoutes, OpenShift Routes with the same paths — fronts everything on the host spec.hostname names:

Path Backend What it is
/, /portal zaentrum-portal the portal — the launchpad that opens the apps and, for an admin, the consoles
/api/portal portal-api the portal's API
/chino, /auth/callback chino-web the video app — a static SPA
/api chino-api the product BFF
/katalog katalog-manager-ui the Catalog console (admin)
/katalog-manage katalog-manage-ui Catalog Management — scan and settings (admin)
/api/manage katalog-manager-api the neutral management / write API
/auth/realms, /auth/resources keycloak sign-in (bundled identity); all of /auth with exposeAdminConsole
flowchart LR
  user(["Browser"]) --> ing["Ingress (spec.hostname)"]
  ing -- "/ and /portal" --> portal["zaentrum-portal"]
  ing -- "/api/portal" --> papi["portal-api"]
  ing -- "/chino" --> web["chino-web (SPA)"]
  ing -- "/api" --> api["chino-api (BFF)"]
  ing -- "/katalog, /katalog-manage" --> consoles["catalog consoles"]
  ing -- "/api/manage" --> mgr["katalog-manager-api"]
  ing -- "/auth/realms, /auth/resources" --> kc["Keycloak"]
  api --> katalog["katalog-api (read)"]
  api --> stream["chino-stream (HLS)"]
  stream --> katalog
Loading

Products

Zaentrum is the platform; the clients are skins over one shared core.

Component What it is State
chino (web · mobile · androidtv) Video client — the reference product Real
portal (zaentrum-portal + portal-api) The launchpad: the apps, the catalog consoles, the operator console Real
katalog-manager-ui (/katalog, /katalog-manage) The Catalog and Catalog Management consoles Real
chino-api / chino-stream Product BFF + HLS/CMAF origin Real
katalog-manager-api Neutral management / write API Real
katalog-api Neutral catalog read API Real
processing (analyzer, packager, transcoder, katalog-ingest) The media pipeline — features.pipeline, on a GPU or the CPU (spec.pipeline); the appliance runs it on the CPU Real
musig / tv Music / live clients Planned

Releases

A release is one tag, vX.Y.Z, cut in every repository of the platform at once. Each publishes its images as :vX.Y.Z and :X.Y (the newest patch); this one also publishes the operator, the release's install manifest (operator-install.yaml on the GitHub release), the appliance ghcr.io/zaentrum/appliance:vX.Y.Z, and the OLM bundle and catalog at the same tag. latest stays the newest build of main; a release never moves it.

An install follows one of two channels, which the front door's releases.json points:

  • stable (the default) names the newest release. A new install starts on it. In manual mode (the default) an install then stays on the version it runs and reports the next release in status.availableUpdate; in auto mode it moves on its own. A push to main moves nothing: the operator pins each image to the digest of the tag the channel names.
  • edge is latest: every push to main, rolled component by component as the operator re-resolves each ghcr.io/zaentrum/* image to its current digest.

spec.version: vX.Y.Z pins an install to a release and opts it out of both. What a release publishes, and how one is cut: the front door's releases guide.

Documentation

Deployment & operations documentation lives in the front-door repo: github.com/zaentrum/zaentrum → docs/. It routes by audience and covers every path (prerequisites, self-hosting, the operator + Zaentrum CR reference, a worked GitOps deploy, day-2 updates, and troubleshooting). This repo holds the operator, the chart, and the install bundles the docs describe.

Repository layout

This is the platform meta-repo: the operator, the chart it renders, its install bundles and the appliance image. The application/service sources live in their own repos at github.com/zaentrum/<svc> and publish flat ghcr.io/zaentrum/<svc> images; the chart here just references those images.

operator/                     the controller-manager: reconciles a Zaentrum into the platform → ghcr.io/zaentrum/operator
operator/platform/chart/      the platform's Helm chart, embedded in the operator (or helm install it)
operator/bundle/              the OLM bundle (OperatorHub, OpenShift)
deploy/operator-install.yaml  the pinned cluster install: CRDs, RBAC, the controller
deploy/allinone/              the appliance: k3s + the operator install + a Zaentrum  → ghcr.io/zaentrum/appliance
deploy/{base,compose,k3s,overlays}/  older profiles, not supported
apps/admin/, platform/keycloak/      images CI still builds (admin, keycloak); only those profiles use them
                              (deployment docs live in the front-door repo: github.com/zaentrum/zaentrum/docs)

Service images the chart pulls, each built in a github.com/zaentrum service repo: zaentrum-portal · portal-api · chino-web · chino-api · chino-stream · katalog-api · katalog-manager · katalog-manager-ui · zae, and with the pipeline analyzer · packager · transcoder · katalog-ingest.

What is deliberately not here

Zaentrum is content-neutral. It catalogs and streams a library you already own; it never fetches content, and how files arrive on disk is out of scope. There are no built-in downloaders, no indexer integrations, and no automation that reaches out for media — by design and forever. See the scope section of the front door's architecture.

License

MPL-2.0 — file-level copyleft that protects the platform while staying distributable on mobile app stores (which matters for the iOS client). Rationale in the license section of the front door's architecture.

About

Self-hosted media platform — neutral media server + clients (chino video; musig/tv planned).

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages