- multistreaming (new): RTMP ingest + multi-platform fan-out with pluggable providers (Twitch/YouTube/Kick/custom), zero-knowledge key vaults, Authelia OIDC auth, shared rooms with editor/streamer roles, single-use invites, per-account streaming grants, and scenes & composition (grid/PiP layouts, text/image overlays, per-output audio routing). - installer: support Dockerfile build in metadata (not just image) and RSA key generation for the Authelia OIDC JWKS. - authelia: add OIDC provider with portainer + multistreaming clients (public + PKCE). - services: remove allprox; add nginx-proxy-manager and portainer; update lldap; regenerate catalog.
203 lines
8.4 KiB
Markdown
203 lines
8.4 KiB
Markdown
# homelab
|
|
|
|
A common, reusable repository for running services on a personal homelab.
|
|
|
|
Every service lives in its own folder under [`services/`](services/) and is described by a
|
|
`metadata.json` file (plus any extra files the service needs, such as a config file). The
|
|
[`installer/`](installer/) directory contains a Bun + TypeScript
|
|
terminal UI (TUI) that reads the catalog straight from GitHub (used as the CDN) and installs or
|
|
updates services with Docker.
|
|
|
|
```
|
|
homelab/
|
|
├── services/ # one folder per service
|
|
│ ├── catalog.json # generated index used by the installer (CDN listing)
|
|
│ ├── nginx-proxy-manager/ # reverse proxy dashboard (NPM)
|
|
│ │ └── metadata.json
|
|
│ ├── authelia/ # SSO / IdP (login portal, OIDC, forward-auth)
|
|
│ │ ├── metadata.json
|
|
│ │ ├── configuration.yml
|
|
│ │ └── users_database.yml
|
|
│ ├── lldap/ # lightweight LDAP user store
|
|
│ │ └── metadata.json
|
|
│ ├── multistreaming/ # self-built: OBS ingest -> multi-platform re-streaming
|
|
│ │ ├── metadata.json
|
|
│ │ ├── Dockerfile
|
|
│ │ ├── src/
|
|
│ │ └── web/
|
|
│ ├── portainer/ # Docker + network manager (VLANs, static IPs)
|
|
│ │ └── metadata.json
|
|
│ └── ...
|
|
├── installer/ # Bun + TypeScript TUI
|
|
│ ├── src/
|
|
│ └── scripts/
|
|
└── docs/
|
|
└── metadata-schema.json
|
|
```
|
|
|
|
## Identity stack (SSO)
|
|
|
|
Every web service logs in through the same IdP ([`authelia`](services/authelia)), which authenticates
|
|
users against [`lldap`](services/lldap) and sits behind [`nginx-proxy-manager`](services/nginx-proxy-manager):
|
|
|
|
```
|
|
browser ──▶ nginx-proxy-manager ──▶ authelia ──LDAP──▶ lldap
|
|
```
|
|
|
|
Two integration modes, depending on what each app supports:
|
|
|
|
1. **OIDC (true SSO)** — the app delegates login to Authelia. [`portainer`](services/portainer) uses
|
|
this (client `portainer` is pre-registered in Authelia).
|
|
2. **Forward-auth (proxy gate)** — the app has no OIDC, so NPM asks Authelia to authorize each
|
|
request via an `auth_request` block before forwarding. Used by `multistreaming` and lldap's web UI.
|
|
|
|
| Service | SSO mode |
|
|
| --- | --- |
|
|
| Portainer | OIDC (native) |
|
|
| multistreaming panel | forward-auth (no OIDC) |
|
|
| lldap web UI | forward-auth (no OIDC) |
|
|
| Nginx Proxy Manager | its own login (no OIDC; keep port 81 restricted) |
|
|
|
|
Authelia also exposes OIDC discovery at `https://auth.example.com/.well-known/openid-configuration`
|
|
for any future OIDC-capable app. Install order: `lldap`, `authelia`, `nginx-proxy-manager`:
|
|
|
|
```bash
|
|
bun run src/index.ts install lldap authelia nginx-proxy-manager
|
|
```
|
|
|
|
## Network & DMZ topology
|
|
|
|
[`portainer`](services/portainer) is the Docker/network manager: from its dashboard you can create
|
|
**macvlan/ipvlan networks** (each mapped to a host VLAN via its parent interface) and assign each
|
|
container a **static IP**. A typical setup keeps only the reverse proxy in the DMZ and everything
|
|
else on separate VLANs:
|
|
|
|
```
|
|
internet ──▶ DMZ VLAN (e.g. 10.0.10.0/24)
|
|
└── nginx-proxy-manager (the only public entrypoint)
|
|
│
|
|
├─▶ services VLAN (10.0.20.0/24) ── multistreaming, …
|
|
└─▶ identity VLAN (10.0.30.0/24) ── authelia, lldap
|
|
```
|
|
|
|
To make a VLAN usable as a macvlan parent, the tagged sub-interface must first exist on the host
|
|
(e.g. `eth0.10`, configured in `/etc/network/interfaces` or netplan — outside Docker's scope).
|
|
Then create the network in Portainer and attach services to it with the IPs you want.
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
# 1. Install Bun: https://bun.sh
|
|
|
|
# 2. Install the installer's dependencies
|
|
cd installer
|
|
bun install
|
|
|
|
# 3. Launch the TUI
|
|
bun run src/index.ts
|
|
```
|
|
|
|
The TUI lets you browse the catalog, install services, update them, uninstall them, and inspect
|
|
what is currently running. Services are deployed with `docker compose` into `~/.homelab/services/<id>/`
|
|
(a `docker-compose.yml` and a `.env` are generated from each `metadata.json`).
|
|
|
|
> **Requirement:** Docker must be installed and the `docker` command available on your `PATH`.
|
|
|
|
## Non-interactive CLI
|
|
|
|
The same app works as a plain CLI:
|
|
|
|
```bash
|
|
bun run src/index.ts list
|
|
bun run src/index.ts info nginx-proxy-manager
|
|
bun run src/index.ts install nginx-proxy-manager
|
|
bun run src/index.ts update # update everything installed
|
|
bun run src/index.ts update nginx-proxy-manager # update one service
|
|
bun run src/index.ts uninstall nginx-proxy-manager
|
|
bun run src/index.ts status
|
|
```
|
|
|
|
Useful flags:
|
|
|
|
| Flag | Meaning |
|
|
| --- | --- |
|
|
| `--local <path>` | Read services from a local folder instead of GitHub (handy while developing a new service). |
|
|
| `--owner/--repo/--branch` | Override the GitHub source (also available as `HOMELAB_OWNER`, `HOMELAB_REPO`, `HOMELAB_BRANCH`). |
|
|
| `--env KEY=VALUE` | Provide an environment value non-interactively (repeatable). |
|
|
| `--yes` | Never prompt; use defaults or provided `--env` values. |
|
|
| `--dry-run` | Print what would happen (including the generated compose file) without touching Docker. |
|
|
| `--force` | Re-run an update even if versions already match. |
|
|
| `--verbose` | Stream `docker compose` output instead of only showing failures. |
|
|
|
|
## How the CDN works
|
|
|
|
The installer treats this GitHub repository as its content delivery network:
|
|
|
|
1. It lists services from [`services/catalog.json`](services/catalog.json) (one request). If that
|
|
file is missing it falls back to the GitHub `git/trees` API.
|
|
2. Each service's `metadata.json` is fetched from
|
|
`https://raw.githubusercontent.com/<owner>/<repo>/<branch>/services/<id>/metadata.json`.
|
|
|
|
Pointing the installer at your own fork is just a matter of setting `--owner`/`--repo` (or the
|
|
`HOMELAB_*` environment variables). For a **private** repository, export a GitHub token and the
|
|
installer authenticates its API and raw requests automatically: `HOMELAB_GITHUB_TOKEN` (or
|
|
`GITHUB_TOKEN` / `HOMELAB_TOKEN`). The repository must be pushed — the installer reads from GitHub,
|
|
not from your local checkout.
|
|
|
|
Adding a new service means adding a folder plus its `metadata.json` and regenerating the catalog:
|
|
|
|
```bash
|
|
cd installer
|
|
bun run catalog
|
|
```
|
|
|
|
## Adding a service
|
|
|
|
1. Create `services/<id>/metadata.json` (the folder name **must** equal the `id`).
|
|
2. Fill in the [metadata schema](docs/metadata-schema.json) — see [`services/README.md`](services/README.md)
|
|
for a walkthrough of every field. Add any extra files the service needs (a config file, a
|
|
`Dockerfile`, …) alongside `metadata.json`; the installer copies them into
|
|
the deploy directory, so reference them with relative bind mounts in `compose.volumes`.
|
|
3. Regenerate the catalog: `cd installer && bun run catalog`.
|
|
4. Commit and push. The installer will now offer the new service.
|
|
|
|
## Service metadata at a glance
|
|
|
|
`metadata.json` is the single source of truth for a service. The essential shape:
|
|
|
|
```json
|
|
{
|
|
"id": "nginx-proxy-manager",
|
|
"name": "Nginx Proxy Manager",
|
|
"description": "Reverse proxy with a web dashboard",
|
|
"version": "1.0.0",
|
|
"category": "network",
|
|
"compose": {
|
|
"image": "jc21/nginx-proxy-manager:2.14.0",
|
|
"container_name": "nginx-proxy-manager",
|
|
"restart": "unless-stopped",
|
|
"ports": ["80:80", "443:443", "81:81"],
|
|
"volumes": ["npm_data:/data", "npm_letsencrypt:/etc/letsencrypt"]
|
|
},
|
|
"volumes": { "npm_data": {}, "npm_letsencrypt": {} },
|
|
"env": [
|
|
{ "name": "TZ", "label": "Timezone", "default": "UTC" }
|
|
]
|
|
}
|
|
```
|
|
|
|
- `compose` is a standard Docker Compose *service* definition (image, ports, volumes, environment, …).
|
|
The installer wraps it in a generated `docker-compose.yml`.
|
|
- `volumes` / `networks` are optional top-level named volumes/networks to declare.
|
|
- `env` declares variables the installer should resolve for you. Use `${NAME}` in `compose` to
|
|
reference them — the installer writes the resolved values to a `.env` file next to the compose file.
|
|
- Any other files in the service folder (e.g. `configuration.yml`, `users_database.yml`) are copied
|
|
into the deploy directory, so you can mount them with relative paths (`./configuration.yml:...`) in
|
|
`compose.volumes`.
|
|
|
|
See [`docs/metadata-schema.json`](docs/metadata-schema.json) for the complete, machine-readable schema.
|
|
|
|
## License
|
|
|
|
This repository is a personal template. Service entries reference their upstream projects; each
|
|
service's own license applies to the software it deploys.
|