The catalogue moves to its own repository
Thirty modules the mesh builds, provisions and runs, as manifests — one per module, flat under modules/. They were in mesh-control/examples/, which framed the mesh's real modules as illustrations of a control-plane package; they are neither examples nor the control plane's. The engine that reads them stays in mesh-control; the data lives here, consumed as a build source. Answers the tier-4 question novox/hq ADR 0030 left open — where the catalogue lives — in favour of one flat repository, which the drop of domain grouping (seats, claims and tags instead) makes the right shape. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# mesh-catalog
|
||||
|
||||
The Novox Mesh catalogue. The modules the mesh builds, provisions and runs — as manifests, one
|
||||
per module under [`modules/`](modules/).
|
||||
|
||||
This is **data, not a control-plane concern**. The manifests describe *what a module is*: what it
|
||||
provides, what it requires, the seats it claims, the resources the host applies for it. The
|
||||
engine that reads them — parsing, eligibility resolution, sealing, declaration emission — lives
|
||||
in the control plane (`novox/mesh-control`, `internal/catalogue`), which consumes this repository
|
||||
as a build source. The host (`novox/mesh-host`) applies the declarations the control plane emits.
|
||||
Neither is here.
|
||||
|
||||
## What a module is, and is not
|
||||
|
||||
A module is one thing the mesh can run, named once, described completely by its manifest. A
|
||||
manifest names its image (pinned by digest), the resources the host owns for it (directories,
|
||||
files, the container, the private network it joins), what it `requires` from a provider and what
|
||||
it `provides` to consumers, and the sealed secrets it needs filled on the machine.
|
||||
|
||||
- **Core mesh components are not modules.** The node host, the substrate, the control-plane
|
||||
contexts and the surfaces are the mesh itself; they ship as their own repositories
|
||||
(`mesh-host`, `mesh-substrate`, `mesh-control`, `mesh-surfaces`, `mesh-sdk`), not from here.
|
||||
- **Standalone applications are not here either.** A larger application lives in its own
|
||||
repository with its manifest at the root, registered with the mesh as a build source
|
||||
(novox/hq [ADR 0010](https://git.novox.be/novox/hq)). This repository holds the modules the
|
||||
mesh maintains as its shared catalogue; an application the mesh merely hosts keeps its manifest
|
||||
beside its own code.
|
||||
|
||||
So there is one home for the catalogue the mesh owns, and every application that runs *on* the
|
||||
mesh rather than being *of* it carries its own — both reach the pipeline the same way, as a
|
||||
registered source.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
modules/<name>.json one manifest per module
|
||||
```
|
||||
|
||||
Flat, because the catalogue's shape carries no meaning: a module is found by its name and
|
||||
described by its manifest, and what relates two modules — a shared seat, a claim, a
|
||||
provider/consumer edge — is data inside the manifests, not a directory the tree encodes
|
||||
(novox/hq, the domain-grouping question closed in favour of seats, claims and tags).
|
||||
|
||||
## The manifest contract
|
||||
|
||||
The shape a manifest must satisfy is owned by the control plane's catalogue engine and is what
|
||||
validates a manifest before a machine ever sees it — a stray key, a consumer contributing the
|
||||
wrong provision field, an image that nothing builds. That validation belongs with this
|
||||
repository and is being re-homed here from `mesh-control`; until it is, the pipeline is the
|
||||
gate — it builds each module and refuses a manifest it cannot resolve.
|
||||
|
||||
## Where the reasoning lives
|
||||
|
||||
Design and decisions are in [`novox/hq`](https://git.novox.be/novox/hq):
|
||||
|
||||
- `02-DECISIONS/0002-everything-is-a-module.md` — one unit, no second mechanism
|
||||
- `02-DECISIONS/0010-applications-live-in-their-own-repository.md` — why applications are not here
|
||||
- `02-DECISIONS/0030-the-repository-structure.md` — the repositories, and the open tier-4 question this repository answers
|
||||
- `03-DESIGN/00-as-is/10-module-catalogue.md` — the catalogue's shape, and what it records
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"module": "bazarr",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 6767,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "managing subtitles"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/bazarr/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-movies",
|
||||
"type": "directory",
|
||||
"path": "/services/media/movies",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-series",
|
||||
"type": "directory",
|
||||
"path": "/services/media/series",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-anime",
|
||||
"type": "directory",
|
||||
"path": "/services/media/anime",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-downloads",
|
||||
"type": "directory",
|
||||
"path": "/services/media/downloads",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "bazarr",
|
||||
"image": "lscr.io/linuxserver/bazarr@sha256:3a820372f19fcb2981ea19fe4b5382934d67414afaba974bce831ddda0a64a02",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"6767"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/bazarr/config:/config",
|
||||
"/services/media/movies:/movies",
|
||||
"/services/media/series:/series",
|
||||
"/services/media/anime:/anime",
|
||||
"/services/media/downloads:/downloads"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"module": "dnsmasq",
|
||||
"version": "1",
|
||||
"requires": [
|
||||
"resolver-data"
|
||||
],
|
||||
"provides": [
|
||||
"wildcard-resolution"
|
||||
],
|
||||
"claims": [
|
||||
{
|
||||
"name": "the-dns-port",
|
||||
"scope": "node"
|
||||
}
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 53,
|
||||
"protocol": "udp",
|
||||
"from": "mesh",
|
||||
"why": "names under every machine in this mesh, for this machine and what it runs",
|
||||
"fixed": true
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "package",
|
||||
"type": "package",
|
||||
"package": "dnsmasq"
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"type": "file",
|
||||
"path": "/etc/dnsmasq.conf",
|
||||
"mode": "0644",
|
||||
"content": "# Managed by the mesh. dnsmasq's own defaults are replaced whole rather than\n# patched, because this module owns the file and a patch would leave whatever\n# was there before to be discovered later.\n\n# What the mesh computed: one wildcard per machine, its name and everything\n# under it. Rewritten whenever a machine joins or leaves, which is why the\n# service below reflects it.\nconf-file=/etc/mesh-resolver/nodes.conf\n\n# Where it answers. Both are names the mesh chose, so this file needs to know\n# nothing about this particular machine:\n#\n# mesh0 the private network, so anything on it \u2014 including a container\n# on this machine \u2014 can ask.\n# 127.0.0.55 this machine's own use, for whatever points resolution at the\n# mesh. Not .53 or .54: systemd-resolved holds BOTH \u2014 .53 is its\n# stub and .54 its proxy stub \u2014 which this module asserted was\n# free until a machine said otherwise.\n#\n# Listening on a loopback address makes dnsmasq take the rest of\n# loopback with it, 127.0.0.1 included. That is why this module\n# claims `the-dns-port`: it takes the machine's DNS port, and\n# saying it takes only one address would be the same kind of\n# comfortable claim that .54 was free.\n#\n# .55 is a convention and not a reservation. If a future systemd\n# takes it, this line changes and nothing else does, which is the\n# reason it is written once here rather than in each module that\n# points at it.\n#\n# bind-dynamic rather than bind-interfaces: mesh0 does not exist until the\n# machine is on the private network, and binding an interface that is not there\n# yet fails to start rather than waiting for it.\nbind-dynamic\ninterface=mesh0\nlisten-address=127.0.0.55\n\n# **It forwards nothing, and must not read resolv.conf to find out where to.**\n# Whatever points this machine at the mesh writes its own address into\n# resolv.conf \u2014 so a resolver that read it for upstreams would find itself,\n# and every query it could not answer locally would loop until its receive\n# queue filled. That is not theoretical: it filled with 15KB of queries and\n# every lookup on the machine hung.\n#\n# It needs no upstream because it is never asked for anything else: the\n# asking module routes only the mesh's suffix here and leaves the rest\n# wherever the machine already sent it.\nno-resolv\ndomain-needed\nbogus-priv\n"
|
||||
},
|
||||
{
|
||||
"id": "service",
|
||||
"type": "service",
|
||||
"unit": "dnsmasq.service",
|
||||
"state": "running",
|
||||
"boot": "enabled",
|
||||
"restart-on": [
|
||||
"config",
|
||||
"mesh-resolver.nodes"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"module": "gitea",
|
||||
"version": "1",
|
||||
"requires": [
|
||||
"postgres-database"
|
||||
],
|
||||
"contributes": {
|
||||
"postgres-database": {
|
||||
"name": "gitea"
|
||||
}
|
||||
},
|
||||
"binds": {
|
||||
"postgres-database": "/var/lib/gitea/database.json"
|
||||
},
|
||||
"secrets": {
|
||||
"postgres-database": "/var/lib/gitea/database.secret"
|
||||
},
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 3000,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the forge, over http"
|
||||
},
|
||||
{
|
||||
"port": 2222,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "git over ssh. Not 22: the machine's own daemon holds that, and a module does not take it"
|
||||
}
|
||||
],
|
||||
"own-secrets": {
|
||||
"internal-token": "/var/lib/gitea/internal-token.secret"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/gitea",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/gitea/server.env",
|
||||
"mode": "0600",
|
||||
"content": "GITEA__security__INTERNAL_TOKEN=${secret:internal-token}\nGITEA__database__DB_TYPE=postgres\nGITEA__database__HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nGITEA__database__NAME=gitea\nGITEA__database__USER=${bound:postgres-database:as}\nGITEA__database__PASSWD=${secret:postgres-database}\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/gitea/gitea",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "gitea",
|
||||
"image": "gitea/gitea@sha256:dfc61e347c8b582df918f4556401bf2cecdfbdb56c5282ae9488dd76fca3e41c",
|
||||
"env": {
|
||||
"DB_TYPE": "postgres",
|
||||
"USER_UID": "1000",
|
||||
"USER_GID": "1000"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/gitea/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"3000",
|
||||
"2222:22"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/gitea/gitea:/data"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"module": "grafana",
|
||||
"version": "1",
|
||||
"own-secrets": {
|
||||
"admin": "/var/lib/grafana-module/admin.secret"
|
||||
},
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 3000,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the dashboards. Also 3000 inside, like the forge - which is the mesh's port assignment earning its keep"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/grafana-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/grafana-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "GF_SECURITY_ADMIN_PASSWORD=${secret:admin}\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/grafana/data",
|
||||
"mode": "0700",
|
||||
"owner": "472:472"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "grafana",
|
||||
"image": "grafana/grafana@sha256:f772d434e8fab0049deb2b1b30abd43342bcfca1537614aa8d36080232cf4283",
|
||||
"env-file": [
|
||||
"/var/lib/grafana-module/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"3000"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/grafana/data:/var/lib/grafana"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"module": "home-assistant",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 8123,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the dashboard and the API"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/home-assistant/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "home-assistant",
|
||||
"image": "ghcr.io/home-assistant/home-assistant@sha256:14931c6b13756317849f46da1d01b45937a1150db66c081cfe529d48215943fe",
|
||||
"network": "host",
|
||||
"env": {
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"volumes": [
|
||||
"/services/home-assistant/config:/config"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"module": "icecast",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"own-secrets": {
|
||||
"source": "/var/lib/icecast-module/source.secret",
|
||||
"admin": "/var/lib/icecast-module/admin.secret",
|
||||
"relay": "/var/lib/icecast-module/relay.secret"
|
||||
},
|
||||
"listens": [
|
||||
{
|
||||
"port": 8000,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "streams in from sources and out to listeners"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/icecast-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/icecast-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "ICECAST_SOURCE_PASSWORD=${secret:source}\nICECAST_ADMIN_PASSWORD=${secret:admin}\nICECAST_RELAY_PASSWORD=${secret:relay}\nICECAST_ADMIN_USERNAME=admin\n"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "icecast",
|
||||
"image": "infiniteproject/icecast@sha256:cd506cf3dfe31ce05fd37d7e672dbd1213e7255cc93d28ecf5a3b547af4e162c",
|
||||
"env-file": [
|
||||
"/var/lib/icecast-module/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"8000"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"module": "influxdb",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"own-secrets": {
|
||||
"admin": "/var/lib/influxdb-module/admin.secret",
|
||||
"admin-token": "/var/lib/influxdb-module/admin-token.secret"
|
||||
},
|
||||
"listens": [
|
||||
{
|
||||
"port": 8086,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "queries and writes, over http"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/influxdb-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/influxdb-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "DOCKER_INFLUXDB_INIT_MODE=setup\nDOCKER_INFLUXDB_INIT_USERNAME=admin\nDOCKER_INFLUXDB_INIT_PASSWORD=${secret:admin}\nDOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${secret:admin-token}\nDOCKER_INFLUXDB_INIT_ORG=mesh\nDOCKER_INFLUXDB_INIT_BUCKET=default\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/influxdb/data",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/influxdb/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "influxdb",
|
||||
"image": "influxdb@sha256:f75e48af0598e8aec7986e991a848d19a119101a7d563a2e5db1dfaac9c45daa",
|
||||
"env-file": [
|
||||
"/var/lib/influxdb-module/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"8086"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/influxdb/data:/var/lib/influxdb2",
|
||||
"/services/influxdb/config:/etc/influxdb2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"module": "jackett",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 9117,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the indexer proxy"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/jackett/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "jackett",
|
||||
"image": "lscr.io/linuxserver/jackett@sha256:fd72d42b731ebf750b5de9711127251cf3b3f609419c32083ea8b3b3ee840b77",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"9117"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/jackett/config:/config"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"module": "keycloak",
|
||||
"version": "1",
|
||||
"requires": [
|
||||
"postgres-database"
|
||||
],
|
||||
"contributes": {
|
||||
"postgres-database": {
|
||||
"name": "keycloak"
|
||||
}
|
||||
},
|
||||
"binds": {
|
||||
"postgres-database": "/var/lib/keycloak/database.json"
|
||||
},
|
||||
"secrets": {
|
||||
"postgres-database": "/var/lib/keycloak/database.secret"
|
||||
},
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 8080,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "anything the mesh runs that authenticates a person"
|
||||
}
|
||||
],
|
||||
"own-secrets": {
|
||||
"admin": "/var/lib/keycloak/admin.secret"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/keycloak",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "admin-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/keycloak/admin.env",
|
||||
"mode": "0600",
|
||||
"content": "KEYCLOAK_ADMIN=admin\nKEYCLOAK_ADMIN_PASSWORD=${secret:admin}\n"
|
||||
},
|
||||
{
|
||||
"id": "database-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/keycloak/database.env",
|
||||
"mode": "0600",
|
||||
"content": "KC_DB_URL=jdbc:postgresql://${bound:postgres-database:at}:${bound:postgres-database:port}/keycloak\nKC_DB_USERNAME=${bound:postgres-database:as}\nKC_DB_PASSWORD=${secret:postgres-database}\n"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
"type": "network",
|
||||
"name": "keycloak"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "keycloak",
|
||||
"image": "quay.io/keycloak/keycloak@sha256:ecd43971114b0c764f8a3288dddab73f98cb473daccc4feaffe4dc14adeaf866",
|
||||
"network": "keycloak",
|
||||
"args": [
|
||||
"start-dev"
|
||||
],
|
||||
"env": {
|
||||
"KC_DB": "postgres",
|
||||
"KC_HTTP_ENABLED": "true",
|
||||
"KC_HEALTH_ENABLED": "true"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/keycloak/admin.env",
|
||||
"/var/lib/keycloak/database.env"
|
||||
],
|
||||
"ports": [
|
||||
"8080"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
{
|
||||
"module": "mailu",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 25,
|
||||
"protocol": "tcp",
|
||||
"from": "anywhere",
|
||||
"why": "mail from other mail servers",
|
||||
"fixed": true
|
||||
},
|
||||
{
|
||||
"port": 465,
|
||||
"protocol": "tcp",
|
||||
"from": "anywhere",
|
||||
"why": "submission over TLS",
|
||||
"fixed": true
|
||||
},
|
||||
{
|
||||
"port": 587,
|
||||
"protocol": "tcp",
|
||||
"from": "anywhere",
|
||||
"why": "submission",
|
||||
"fixed": true
|
||||
},
|
||||
{
|
||||
"port": 993,
|
||||
"protocol": "tcp",
|
||||
"from": "anywhere",
|
||||
"why": "IMAP over TLS",
|
||||
"fixed": true
|
||||
},
|
||||
{
|
||||
"port": 7080,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the web interface, behind a proxy"
|
||||
}
|
||||
],
|
||||
"own-secrets": {
|
||||
"secret-key": "/var/lib/mailu/secret-key.secret",
|
||||
"database": "/var/lib/mailu/database.secret",
|
||||
"admin": "/var/lib/mailu/admin.secret"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/mailu",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "secret-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/mailu/secret.env",
|
||||
"mode": "0600",
|
||||
"content": "SECRET_KEY=${secret:secret-key}\n"
|
||||
},
|
||||
{
|
||||
"id": "database-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/mailu/database.env",
|
||||
"mode": "0600",
|
||||
"content": "POSTGRES_USER=mailu\nPOSTGRES_DB=mailu\nPOSTGRES_PASSWORD=${secret:database}\nDB_USER=mailu\nDB_NAME=mailu\nDB_PW=${secret:database}\n"
|
||||
},
|
||||
{
|
||||
"id": "admin-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/mailu/admin.env",
|
||||
"mode": "0600",
|
||||
"content": "INITIAL_ADMIN_PW=${secret:admin}\n"
|
||||
},
|
||||
{
|
||||
"id": "data-certs",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/certs",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-data",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/data",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-dkim",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/dkim",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-filter",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/filter",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-mail",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/mail",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-mailqueue",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/mailqueue",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-redis",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/redis",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-webmail",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/webmail",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-dovecot",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/overrides/dovecot",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-nginx",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/overrides/nginx",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data-pgdata",
|
||||
"type": "directory",
|
||||
"path": "/services/mailu/data/data/psql_admindb/pgdata",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
"type": "network",
|
||||
"name": "mailu"
|
||||
},
|
||||
{
|
||||
"id": "resolver",
|
||||
"type": "container",
|
||||
"name": "mailu-resolver",
|
||||
"image": "ghcr.io/mailu/unbound@sha256:142aaad82ad1b0d5b59a5f1303778dba61a3e0a540f5d969c48862bcc99f6f5d",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "redis",
|
||||
"type": "container",
|
||||
"name": "mailu-redis",
|
||||
"image": "redis@sha256:1db42ccef14898aa29bae778452d567534b59c107129cbc1163fb552de184d3c",
|
||||
"network": "mailu",
|
||||
"volumes": [
|
||||
"/services/mailu/data/redis:/data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "admindb",
|
||||
"type": "container",
|
||||
"name": "mailu-admindb",
|
||||
"image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee",
|
||||
"network": "mailu",
|
||||
"env": {
|
||||
"PGDATA": "/var/lib/postgresql/data/pgdata"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/mailu/database.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/data/psql_admindb/pgdata:/var/lib/postgresql/data/pgdata"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "admin",
|
||||
"type": "container",
|
||||
"name": "mailu-admin",
|
||||
"image": "ghcr.io/mailu/admin@sha256:dcac20e9cbdad560faef9653b1b5ac0d9266f4098dc00f0e7f0d35f4e70ed8f1",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env",
|
||||
"/var/lib/mailu/database.env",
|
||||
"/var/lib/mailu/admin.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/data:/data",
|
||||
"/services/mailu/data/dkim:/dkim"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "imap",
|
||||
"type": "container",
|
||||
"name": "mailu-imap",
|
||||
"image": "ghcr.io/mailu/dovecot@sha256:46d18ba51032be8ebd6841aa49c1ef8762c729038c5fd86a081b5b884d478af9",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/mail:/mail",
|
||||
"/services/mailu/data/overrides/dovecot:/overrides:ro"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "smtp",
|
||||
"type": "container",
|
||||
"name": "mailu-smtp",
|
||||
"image": "ghcr.io/mailu/postfix@sha256:bbf882880f68849511710b35237a933f3fe80c4b28bf48ff20205dbd1f1433d7",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/mailqueue:/queue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "antispam",
|
||||
"type": "container",
|
||||
"name": "mailu-antispam",
|
||||
"image": "ghcr.io/mailu/rspamd@sha256:e87ab93dd252cc69499caa5317dd10d445fd4291a7ecf6bca09793c7d475a0c8",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/filter:/var/lib/rspamd"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "webmail",
|
||||
"type": "container",
|
||||
"name": "mailu-webmail",
|
||||
"image": "ghcr.io/mailu/webmail@sha256:076b720fc766e58a97321cdb700e887c2008d6d323685fe59f323088333059dc",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/webmail:/data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "front",
|
||||
"type": "container",
|
||||
"name": "mailu-front",
|
||||
"image": "ghcr.io/mailu/nginx@sha256:09f28ab6d36367fcacc7994f7021f132ac845bdc05f04bf80906102d11aaa057",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/secret.env"
|
||||
],
|
||||
"ports": [
|
||||
"25",
|
||||
"465",
|
||||
"587",
|
||||
"993",
|
||||
"7080:80"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/certs:/certs",
|
||||
"/services/mailu/data/overrides/nginx:/overrides:ro"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"module": "minio",
|
||||
"version": "1",
|
||||
"provides": [
|
||||
{
|
||||
"name": "s3-bucket",
|
||||
"scope": "mesh"
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 9000,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the S3 endpoint"
|
||||
}
|
||||
],
|
||||
"serves": {
|
||||
"s3-bucket": {
|
||||
"scheme": "http",
|
||||
"region": "us-east-1"
|
||||
}
|
||||
},
|
||||
"receives": {
|
||||
"s3-bucket": "/var/lib/minio/grants/mesh.json"
|
||||
},
|
||||
"grants": {
|
||||
"s3-bucket": "/var/lib/minio/grants"
|
||||
},
|
||||
"own-secrets": {
|
||||
"root": "/var/lib/minio/root.secret"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/minio",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "grants",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/minio/grants",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "root-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/minio/root.env",
|
||||
"mode": "0600",
|
||||
"content": "MINIO_ROOT_USER=meshroot\nMINIO_ROOT_PASSWORD=${secret:root}\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/minio/data/data1-1",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
"type": "network",
|
||||
"name": "minio"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "minio",
|
||||
"image": "minio/minio@sha256:aefec8a86702aff0b0dcfdd9284bd7ab7c5631cbf9be63275799e6edcb30dfa2",
|
||||
"network": "minio",
|
||||
"args": [
|
||||
"server",
|
||||
"/data",
|
||||
"--console-address",
|
||||
":9001"
|
||||
],
|
||||
"env-file": [
|
||||
"/var/lib/minio/root.env"
|
||||
],
|
||||
"ports": [
|
||||
"9000"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/minio/data/data1-1:/data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "provisioner",
|
||||
"type": "container",
|
||||
"name": "mesh-provision-objectstore",
|
||||
"image": "mesh-provision-objectstore@sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"network": "minio",
|
||||
"env": {
|
||||
"GRANTS": "/var/lib/minio/grants",
|
||||
"MESH_OBJECTSTORE_URL": "http://minio:9000",
|
||||
"MESH_OBJECTSTORE_ROOT_USER": "meshroot",
|
||||
"MESH_OBJECTSTORE_ROOT_PASSWORD_FILE": "/run/secrets/root"
|
||||
},
|
||||
"volumes": [
|
||||
"/var/lib/minio/grants:/var/lib/minio/grants:ro",
|
||||
"/var/lib/minio/root.secret:/run/secrets/root:ro"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"module": "nextcloud",
|
||||
"version": "1",
|
||||
"requires": [
|
||||
"postgres-database",
|
||||
"s3-bucket"
|
||||
],
|
||||
"contributes": {
|
||||
"postgres-database": {
|
||||
"name": "nextcloud"
|
||||
},
|
||||
"s3-bucket": {
|
||||
"bucket": "nextcloud"
|
||||
}
|
||||
},
|
||||
"binds": {
|
||||
"postgres-database": "/var/lib/nextcloud-module/database.json",
|
||||
"s3-bucket": "/var/lib/nextcloud-module/store.json"
|
||||
},
|
||||
"secrets": {
|
||||
"postgres-database": "/var/lib/nextcloud-module/database.secret",
|
||||
"s3-bucket": "/var/lib/nextcloud-module/store.secret"
|
||||
},
|
||||
"own-secrets": {
|
||||
"admin": "/var/lib/nextcloud-module/admin.secret"
|
||||
},
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 80,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "files and sync, over http; a public name is a route grant later"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/nextcloud-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/nextcloud-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "POSTGRES_HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nPOSTGRES_DB=nextcloud\nPOSTGRES_USER=${bound:postgres-database:as}\nPOSTGRES_PASSWORD=${secret:postgres-database}\nNEXTCLOUD_ADMIN_USER=admin\nNEXTCLOUD_ADMIN_PASSWORD=${secret:admin}\nOBJECTSTORE_S3_HOST=${bound:s3-bucket:at}\nOBJECTSTORE_S3_PORT=${bound:s3-bucket:port}\nOBJECTSTORE_S3_BUCKET=nextcloud\nOBJECTSTORE_S3_KEY=${bound:s3-bucket:as}\nOBJECTSTORE_S3_SECRET=${secret:s3-bucket}\nOBJECTSTORE_S3_SSL=false\nOBJECTSTORE_S3_USEPATH_STYLE=true\n"
|
||||
},
|
||||
{
|
||||
"id": "html",
|
||||
"type": "directory",
|
||||
"path": "/services/nextcloud/html",
|
||||
"mode": "0750",
|
||||
"owner": "33:33"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "nextcloud",
|
||||
"image": "nextcloud@sha256:0b8261f6335af6b95264ce893b4d645857638e0fa151b5ba620f25f377318ae1",
|
||||
"env-file": [
|
||||
"/var/lib/nextcloud-module/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"80"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/nextcloud/html:/var/www/html"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"module": "nodered",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 1880,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the flow editor and the endpoints flows expose"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/nodered/data",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "nodered",
|
||||
"image": "nodered/node-red@sha256:02a2b92a41b73d2bc388238b86e4fcaab7fb5466373adb24e1df6aa5845265ff",
|
||||
"env": {
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"1880"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/nodered/data:/data"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"module": "nzbget",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 6789,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the download client's pages"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/nzbget/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-downloads",
|
||||
"type": "directory",
|
||||
"path": "/services/media/downloads",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "nzbget",
|
||||
"image": "lscr.io/linuxserver/nzbget@sha256:5f3d3fa71029004156eff2cbf4ef4455ce4ce59517cf13fa7d1d7c8a4cd2c8a4",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"6789"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/nzbget/config:/config",
|
||||
"/services/media/downloads:/downloads"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"module": "ombi",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 3579,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "requests from viewers"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/ombi/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "ombi",
|
||||
"image": "lscr.io/linuxserver/ombi@sha256:a6f76ac521ba01eee2e9f0c23a3fed22e56630d97a04d5eeaeaa36c1e681640d",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"3579"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/ombi/config:/config"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"module": "photos",
|
||||
"version": "1",
|
||||
"requires": [
|
||||
"s3-bucket"
|
||||
],
|
||||
"contributes": {
|
||||
"s3-bucket": {
|
||||
"bucket": "photos"
|
||||
}
|
||||
},
|
||||
"binds": {
|
||||
"s3-bucket": "/etc/photos/store.json"
|
||||
},
|
||||
"secrets": {
|
||||
"s3-bucket": "/etc/photos/store.secret"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/etc/photos",
|
||||
"mode": "0750"
|
||||
},
|
||||
{
|
||||
"id": "app",
|
||||
"type": "container",
|
||||
"name": "photos",
|
||||
"image": "alpine@sha256:c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e",
|
||||
"env": {
|
||||
"PHOTOS_STORE": "/etc/photos/store.json",
|
||||
"PHOTOS_STORE_SECRET_FILE": "/etc/photos/store.secret"
|
||||
},
|
||||
"volumes": [
|
||||
"/etc/photos/store.json:/etc/photos/store.json:ro",
|
||||
"/etc/photos/store.secret:/etc/photos/store.secret:ro"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"module": "plex",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 32400,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "streaming and the app; reaching it from outside is a route grant later"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/plex/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "transcode",
|
||||
"type": "directory",
|
||||
"path": "/services/plex/transcode",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-movies",
|
||||
"type": "directory",
|
||||
"path": "/services/media/movies",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-series",
|
||||
"type": "directory",
|
||||
"path": "/services/media/series",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-anime",
|
||||
"type": "directory",
|
||||
"path": "/services/media/anime",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-music",
|
||||
"type": "directory",
|
||||
"path": "/services/media/music",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-audiobooks",
|
||||
"type": "directory",
|
||||
"path": "/services/media/audiobooks",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "plex",
|
||||
"image": "plexinc/pms-docker@sha256:83a425ae9e133b1cb2cc3b809556e01c61cd8ff65c582e41b4374bc2210bac9e",
|
||||
"network": "host",
|
||||
"env": {
|
||||
"PLEX_UID": "1000",
|
||||
"PLEX_GID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"volumes": [
|
||||
"/services/plex/config:/config",
|
||||
"/services/plex/transcode:/transcode",
|
||||
"/services/media/movies:/movies",
|
||||
"/services/media/series:/series",
|
||||
"/services/media/anime:/anime",
|
||||
"/services/media/music:/music",
|
||||
"/services/media/audiobooks:/audiobooks"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"module": "portainer",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 9443,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the container dashboard, over its own tls"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/portainer/data",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "portainer",
|
||||
"image": "portainer/portainer-ce@sha256:511f3f06c96fe3b993ebeaafde311c1959cae73a7ef825dba6397d51b450dffa",
|
||||
"ports": [
|
||||
"9443"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/portainer/data:/data",
|
||||
"/var/run/docker.sock:/var/run/docker.sock"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"module": "postgres",
|
||||
"version": "1",
|
||||
"provides": [
|
||||
{
|
||||
"name": "postgres-database",
|
||||
"scope": "mesh"
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 5432,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "modules on any machine that were granted a database"
|
||||
}
|
||||
],
|
||||
"serves": {
|
||||
"postgres-database": {}
|
||||
},
|
||||
"receives": {
|
||||
"postgres-database": "/var/lib/postgres/grants/mesh.json"
|
||||
},
|
||||
"grants": {
|
||||
"postgres-database": "/var/lib/postgres/grants"
|
||||
},
|
||||
"own-secrets": {
|
||||
"superuser": "/var/lib/postgres/superuser.secret"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/postgres",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "grants",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/postgres/grants",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "superuser-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/postgres/superuser.env",
|
||||
"mode": "0600",
|
||||
"content": "POSTGRES_PASSWORD=${secret:superuser}\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/postgres/db-data",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
"type": "network",
|
||||
"name": "postgres"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "postgres",
|
||||
"image": "postgres@sha256:7456ef82e5f5bc43d997f4781bbd7c0d6389bff397564649a356e206ba473aee",
|
||||
"network": "postgres",
|
||||
"env": {
|
||||
"POSTGRES_USER": "postgres",
|
||||
"POSTGRES_DB": "postgres"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/postgres/superuser.env"
|
||||
],
|
||||
"ports": [
|
||||
"5432"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/postgres/db-data:/var/lib/postgresql/data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "provisioner",
|
||||
"type": "container",
|
||||
"name": "mesh-provision-postgres",
|
||||
"image": "mesh-provision-postgres@sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"network": "postgres",
|
||||
"env": {
|
||||
"GRANTS": "/var/lib/postgres/grants",
|
||||
"MESH_PROVISION_POSTGRES": "postgres://postgres@postgres:5432/postgres?sslmode=disable",
|
||||
"MESH_PROVISION_PASSWORD_FILE": "/run/secrets/superuser"
|
||||
},
|
||||
"volumes": [
|
||||
"/var/lib/postgres/grants:/var/lib/postgres/grants:ro",
|
||||
"/var/lib/postgres/superuser.secret:/run/secrets/superuser:ro"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"module": "qbittorrent",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 8080,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the download client's pages"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/qbittorrent/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-downloads",
|
||||
"type": "directory",
|
||||
"path": "/services/media/downloads",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "qbittorrent",
|
||||
"image": "lscr.io/linuxserver/qbittorrent@sha256:a00b6a597a3832a1814cde0ef60abc55c94644f3f80902c3432f6af6de8d4a96",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"8080"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/qbittorrent/config:/config",
|
||||
"/services/media/downloads:/downloads"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"module": "radarr",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 7878,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "managing films"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/radarr/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-movies",
|
||||
"type": "directory",
|
||||
"path": "/services/media/movies",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-downloads",
|
||||
"type": "directory",
|
||||
"path": "/services/media/downloads",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "radarr",
|
||||
"image": "lscr.io/linuxserver/radarr@sha256:119aaa4a4f7349bcd2a136c5373a0d7925b5479915c7dfe0c0ad352db2a6d438",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"7878"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/radarr/config:/config",
|
||||
"/services/media/movies:/movies",
|
||||
"/services/media/downloads:/downloads"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"module": "redis",
|
||||
"version": "1",
|
||||
"provides": [
|
||||
{
|
||||
"name": "redis-cache",
|
||||
"scope": "mesh"
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"serves": {
|
||||
"redis-cache": {}
|
||||
},
|
||||
"receives": {
|
||||
"redis-cache": "/var/lib/redis-module/grants/mesh.json"
|
||||
},
|
||||
"grants": {
|
||||
"redis-cache": "/var/lib/redis-module/grants"
|
||||
},
|
||||
"own-secrets": {
|
||||
"default": "/var/lib/redis-module/default.secret"
|
||||
},
|
||||
"listens": [
|
||||
{
|
||||
"port": 6379,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "modules on any machine that were granted a cache"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/redis-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "grants-dir",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/redis-module/grants",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
"path": "/services/redis/data",
|
||||
"mode": "0700",
|
||||
"owner": "999:999"
|
||||
},
|
||||
{
|
||||
"id": "server-conf",
|
||||
"type": "file",
|
||||
"path": "/var/lib/redis-module/redis.conf",
|
||||
"mode": "0600",
|
||||
"content": "requirepass ${secret:default}\nappendonly yes\ndir /data\n",
|
||||
"owner": "999:999"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
"type": "network",
|
||||
"name": "redis"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "redis",
|
||||
"image": "redis@sha256:ff02b58f971e7d7d156a1267e283fcbbeee91773b6aa36c49dac28ecfe28eadf",
|
||||
"network": "redis",
|
||||
"ports": [
|
||||
"6379"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/redis/data:/data",
|
||||
"/var/lib/redis-module/redis.conf:/etc/redis/redis.conf:ro"
|
||||
],
|
||||
"args": [
|
||||
"/etc/redis/redis.conf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "provisioner",
|
||||
"type": "container",
|
||||
"name": "mesh-provision-redis",
|
||||
"image": "mesh-provision-redis@sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"network": "redis",
|
||||
"env": {
|
||||
"GRANTS": "/var/lib/redis-module/grants",
|
||||
"MESH_PROVISION_REDIS": "redis:6379",
|
||||
"MESH_PROVISION_PASSWORD_FILE": "/run/secrets/default"
|
||||
},
|
||||
"volumes": [
|
||||
"/var/lib/redis-module/grants:/var/lib/redis-module/grants:ro",
|
||||
"/var/lib/redis-module/default.secret:/run/secrets/default:ro"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"module": "registry",
|
||||
"version": "1",
|
||||
"provides": [
|
||||
{
|
||||
"name": "artifact-store",
|
||||
"scope": "mesh"
|
||||
}
|
||||
],
|
||||
"claims": [
|
||||
{
|
||||
"name": "the-artifact-store",
|
||||
"scope": "node"
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"serves": {
|
||||
"artifact-store": {
|
||||
"port": 5000
|
||||
}
|
||||
},
|
||||
"listens": [
|
||||
{
|
||||
"port": 5000,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "every machine pulls images and artifacts from here"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/mesh/registry",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "store",
|
||||
"type": "container",
|
||||
"name": "mesh-registry",
|
||||
"image": "registry@sha256:a3d8aaa63ed8681a604f1dea0aa03f100d5895b6a58ace528858a7b332415373",
|
||||
"ports": [
|
||||
"5000:5000"
|
||||
],
|
||||
"volumes": [
|
||||
"mesh-registry-data:/var/lib/registry"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"module": "resolv-conf",
|
||||
"version": "1",
|
||||
|
||||
"requires": ["wildcard-resolution"],
|
||||
"claims": [{"name": "the-resolver-configuration", "scope": "node"}],
|
||||
|
||||
"resources": [
|
||||
{"id": "resolv", "type": "file", "path": "/etc/resolv.conf", "mode": "0644",
|
||||
"content": "# Managed by the mesh.\n#\n# For a machine where nothing else owns this file. On one where systemd-resolved\n# or NetworkManager does, assign that module instead — this one and those claim\n# the same thing, so the mesh refuses the pair rather than letting them take\n# turns overwriting each other, which is the failure this claim exists to stop.\n#\n# The mesh's resolver first, because it answers only the mesh's names and\n# forwards nothing: a query it does not recognise falls through to the next\n# line rather than being answered wrongly.\nnameserver 127.0.0.55\n\n# And what this machine used before. Replace this line with the resolver this\n# machine should use for everything that is not the mesh — it is not the mesh's\n# to choose, and a public one written here by default would send every query\n# this machine makes somewhere nobody agreed to.\nnameserver 127.0.0.53\n"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"module": "resolved-split-dns",
|
||||
"version": "1",
|
||||
|
||||
"requires": ["wildcard-resolution"],
|
||||
"claims": [{"name": "the-resolver-configuration", "scope": "node"}],
|
||||
|
||||
"resources": [
|
||||
{"id": "drop-in", "type": "directory", "path": "/etc/systemd/resolved.conf.d", "mode": "0755"},
|
||||
|
||||
{"id": "route", "type": "file",
|
||||
"path": "/etc/systemd/resolved.conf.d/mesh.conf", "mode": "0644",
|
||||
"content": "# Managed by the mesh.\n#\n# **Only the mesh's names.** The tilde makes this a routing domain rather than a\n# search domain: queries under it go to the resolver below, and everything else\n# keeps going wherever this machine already sent it. A resolver that took over\n# all of DNS would be this module claiming the machine's whole network, which\n# is not what it says it claims.\n#\n# 127.0.0.55 is where the mesh's resolver answers on every machine — a fixed\n# address, so this file needs to know nothing about this particular machine.\n# systemd-resolved holds .53 and .54, which is why it is neither.\n[Resolve]\nDNS=127.0.0.55\nDomains=~internal\n"},
|
||||
|
||||
{"id": "resolved", "type": "service", "unit": "systemd-resolved.service",
|
||||
"state": "running", "boot": "enabled", "restart-on": ["route"]}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"module": "searxng",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"own-secrets": {
|
||||
"secret": "/var/lib/searxng-module/secret.secret"
|
||||
},
|
||||
"listens": [
|
||||
{
|
||||
"port": 8080,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the search pages"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/searxng-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/searxng-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "SEARXNG_SECRET=${secret:secret}\nSEARXNG_VALKEY_URL=valkey://valkey:6379/0\n"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
"type": "network",
|
||||
"name": "searxng"
|
||||
},
|
||||
{
|
||||
"id": "cache",
|
||||
"type": "container",
|
||||
"name": "valkey",
|
||||
"image": "valkey/valkey@sha256:b21fd94099dcd4bc6b2b9230daef69b6558b887ad4a2a1afe56ff6e745a88cdb",
|
||||
"network": "searxng",
|
||||
"args": [
|
||||
"valkey-server",
|
||||
"--save",
|
||||
"30",
|
||||
"1",
|
||||
"--loglevel",
|
||||
"warning"
|
||||
],
|
||||
"volumes": [
|
||||
"searxng-valkey-data:/data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "searxng",
|
||||
"image": "searxng/searxng@sha256:c7cc75852051bf6254afda6ed1b920dd1677d8efe4ab141bf558f02e582f4371",
|
||||
"network": "searxng",
|
||||
"env-file": [
|
||||
"/var/lib/searxng-module/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"8080"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"module": "sonarr",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 8989,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "managing series"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/sonarr/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-series",
|
||||
"type": "directory",
|
||||
"path": "/services/media/series",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-anime",
|
||||
"type": "directory",
|
||||
"path": "/services/media/anime",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "media-downloads",
|
||||
"type": "directory",
|
||||
"path": "/services/media/downloads",
|
||||
"mode": "0755",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "sonarr",
|
||||
"image": "lscr.io/linuxserver/sonarr@sha256:c19aa4ecdf03d73e1d5c901da33744cb7eb4d921f89bafed1ca264601d7fa224",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"8989"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/sonarr/config:/config",
|
||||
"/services/media/series:/series",
|
||||
"/services/media/anime:/anime",
|
||||
"/services/media/downloads:/downloads"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"module": "tautulli",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 8181,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "watch statistics"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "directory",
|
||||
"path": "/services/tautulli/config",
|
||||
"mode": "0700",
|
||||
"owner": "1000:1000"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "tautulli",
|
||||
"image": "lscr.io/linuxserver/tautulli@sha256:13f03ecfc61a7af89d492677389771ac29682a72153ce08a5cd4faffbc0197e8",
|
||||
"env": {
|
||||
"PUID": "1000",
|
||||
"PGID": "1000",
|
||||
"TZ": "Etc/UTC"
|
||||
},
|
||||
"ports": [
|
||||
"8181"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/tautulli/config:/config"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"module": "umami",
|
||||
"version": "1",
|
||||
"requires": [
|
||||
"postgres-database"
|
||||
],
|
||||
"contributes": {
|
||||
"postgres-database": {
|
||||
"name": "umami"
|
||||
}
|
||||
},
|
||||
"binds": {
|
||||
"postgres-database": "/var/lib/umami/database.json"
|
||||
},
|
||||
"secrets": {
|
||||
"postgres-database": "/var/lib/umami/database.secret"
|
||||
},
|
||||
"own-secrets": {
|
||||
"app-secret": "/var/lib/umami/app.secret"
|
||||
},
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 3000,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the analytics pages and the collection endpoint"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "state",
|
||||
"type": "directory",
|
||||
"path": "/var/lib/umami",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/umami/server.env",
|
||||
"mode": "0600",
|
||||
"content": "DATABASE_URL=postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/umami\nAPP_SECRET=${secret:app-secret}\n"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "umami",
|
||||
"image": "ghcr.io/umami-software/umami@sha256:fa32d116cf20cad52cbc3fad9a63b46e7fa02299d8f967168eb453d49c476b4a",
|
||||
"env-file": [
|
||||
"/var/lib/umami/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"3000"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"module": "verdaccio",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"container-runtime"
|
||||
],
|
||||
"listens": [
|
||||
{
|
||||
"port": 4873,
|
||||
"protocol": "tcp",
|
||||
"from": "mesh",
|
||||
"why": "the package registry, for installs and publishes"
|
||||
}
|
||||
],
|
||||
"resources": [
|
||||
{
|
||||
"id": "conf",
|
||||
"type": "directory",
|
||||
"path": "/services/verdaccio/conf",
|
||||
"mode": "0755",
|
||||
"owner": "10001:10001"
|
||||
},
|
||||
{
|
||||
"id": "storage",
|
||||
"type": "directory",
|
||||
"path": "/services/verdaccio/storage",
|
||||
"mode": "0700",
|
||||
"owner": "10001:10001"
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"type": "file",
|
||||
"path": "/services/verdaccio/conf/config.yaml",
|
||||
"mode": "0644",
|
||||
"content": "storage: /verdaccio/storage\nauth:\n htpasswd:\n file: /verdaccio/conf/htpasswd\n max_users: 10\nuplinks:\n npmjs:\n url: https://registry.npmjs.org/\npackages:\n \"**\":\n access: $all\n publish: $authenticated\n proxy: npmjs\nserver:\n keepAliveTimeout: 60\n maxBodySize: 10mb\nmiddlewares:\n audit:\n enabled: true\nlog:\n type: stdout\n format: pretty\n level: http\n"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "verdaccio",
|
||||
"image": "verdaccio/verdaccio@sha256:fcb86134563534e2f634752e6c6c3edcdb78242ec16578c73ce39d1dadbaa801",
|
||||
"ports": [
|
||||
"4873"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/verdaccio/storage:/verdaccio/storage",
|
||||
"/services/verdaccio/conf:/verdaccio/conf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user