kometa: a nightly scheduled run against plex, its credentials as files #159

Closed
mesh-admin wants to merge 2 commits from feat/kometa-for-ace into main
Contributor

New module: kometa (ace runs it under HAL today).

  • One container, "schedule": "0 3 * * *", kometa --run (ADR 0053) — replaces HAL's always-on container with Kometa's own 03:00 scheduler.
  • Secrets as files (ADR 0086): mesh-rendered config.yml (0600, root, read-only, KOMETA_READ_ONLY_CONFIG=true) carries ${secret:plex-api} and ${secret:tmdb}; HAL passed both through the environment.
  • Plex via provisioning: requires: ["plex-api", "secret"], ${bound:plex-api:at|port}; the Plex token is the accepted pair credential (ADR 0092), the TMDb key an accepted vault secret.
  • Depends on a provider half not in this PR: plex must provide/serve plex-api ({"scheme":"http","port":32400}, scope mesh). Plex was outside this change's scope; kometa is unassignable until that lands.
  • Libraries are per machine: "libraries": {} by default; the assignment merges them in (ace: Formula 1, metadata from f1-circuits' public name).
  • Image: the digest ace runs (2.4.8).

Verified: catalogue tests (MESH_CATALOGUE set) on mesh-controller main and #149; a stub-provider resolution renders binding, secrets and the merged config; the pinned image with the rendered file root-owned 0600 :ro parses it, tolerates endpoints, never writes back, and stops only at TMDb refusing the dummy key.

New module: **kometa** (ace runs it under HAL today). - One container, `"schedule": "0 3 * * *"`, `kometa --run` (ADR 0053) — replaces HAL's always-on container with Kometa's own 03:00 scheduler. - Secrets as files (ADR 0086): mesh-rendered `config.yml` (0600, root, read-only, `KOMETA_READ_ONLY_CONFIG=true`) carries `${secret:plex-api}` and `${secret:tmdb}`; HAL passed both through the environment. - Plex via provisioning: `requires: ["plex-api", "secret"]`, `${bound:plex-api:at|port}`; the Plex token is the accepted pair credential (ADR 0092), the TMDb key an accepted vault secret. - **Depends on a provider half not in this PR**: plex must `provide`/`serve` `plex-api` (`{"scheme":"http","port":32400}`, scope mesh). Plex was outside this change's scope; kometa is unassignable until that lands. - Libraries are per machine: `"libraries": {}` by default; the assignment merges them in (ace: Formula 1, metadata from f1-circuits' public name). - Image: the digest ace runs (2.4.8). Verified: catalogue tests (MESH_CATALOGUE set) on mesh-controller main and #149; a stub-provider resolution renders binding, secrets and the merged config; the pinned image with the rendered file root-owned 0600 :ro parses it, tolerates `endpoints`, never writes back, and stops only at TMDb refusing the dummy key.
mesh-admin added 1 commit 2026-09-30 09:57:53 +00:00
ace runs Kometa under HAL as a long-lived container whose own scheduler wakes
at 03:00, with the Plex token and the TMDb key in its environment
(KOMETA_PLEX_TOKEN, KOMETA_TMDB_APIKEY). This is the case ADR 0053 was written
for: the module is one container marked "schedule": "0 3 * * *" that runs
kometa --run to completion, so nothing stays up between runs.

Secrets are files (ADR 0086): config.yml is rendered by the mesh, 0600 and
root-owned (Kometa runs as root), with ${secret:plex-api} and ${secret:tmdb}
in it, and mounted read-only. KOMETA_READ_ONLY_CONFIG stops Kometa writing its
defaults back into it (without it the run dies on EROFS); the file already
carries every attribute Kometa filled in on ace, so behaviour is unchanged.

Plex is reached through provisioning, not a URL: kometa requires plex-api
and reads ${bound:plex-api:at|port}; the token is that pair's credential,
which the operator accepts (ADR 0092). The provider half (plex provides and
serves plex-api, 32400) is NOT in this change: plex is outside this work, so
until plex declares it kometa cannot be assigned. The TMDb key is an
operator-accepted vault secret.

Which libraries Kometa manages is per machine: the manifest ships
"libraries": {}, and config.yml is the module's one merge:json file, so the
assignment supplies them (ace: the Formula 1 library, whose metadata file
comes from f1-circuits' public name).

Verified: catalogue tests with MESH_CATALOGUE pointed here, on mesh-controller
main and on #149; a resolution with stub providers renders the binding, the
two secret files and config.yml with the assignment's library merged in; the
pinned digest (the one ace runs, 2.4.8) in a throwaway container with the
rendered file root-owned 0600 read-only: config parses, the "endpoints" key is
tolerated, no write-back with the read-only flag, and the run stops only at
TMDb refusing the dummy key.
jschoubben added 1 commit 2026-09-30 11:14:27 +00:00
The url's scheme was typed as http while its host and port came from the
binding. plex now serves plex-api with its scheme (#162), so the whole
address is the mesh's: a plex that one day serves https is followed rather
than dialled on the wrong protocol.
Author
Contributor

kometa: the whole Plex URL now comes from the binding (commit 2ee77de)

  • The scheme was typed as http while the host and port came from plex-api. plex now serves its scheme (#162), so the URL is ${bound:plex-api:scheme}://${bound:plex-api:at}:${bound:plex-api:port}. On ace it renders http://ace.internal:32400 (checked with a scratch Resolve/Declaration). ${secret:plex-api} fills from kometa's own pair credential.
  • Credential: kometa needs no write-in step, because config.yml is the mesh's own file. A minted value makes kometa's 03:00 run fail with plex's 401, and nothing of kometa's is overwritten. Accept the server's token for this pair: secret accept ace kometa plex-api --provider ace --from <file>.
  • End to end: I ran the pinned kometa image against a throwaway Plex (1.43.4) with the config.yml the mesh renders. Kometa's own Plex client (python-plexapi) connected and listed the library. With a minted value in place of the token it was refused with 401.
  • Not tested: a full --run. Kometa checks the TMDb key first, and that key is an operator secret.
**kometa: the whole Plex URL now comes from the binding** (commit 2ee77de) - The scheme was typed as `http` while the host and port came from `plex-api`. plex now serves its scheme (#162), so the URL is `${bound:plex-api:scheme}://${bound:plex-api:at}:${bound:plex-api:port}`. On ace it renders `http://ace.internal:32400` (checked with a scratch Resolve/Declaration). `${secret:plex-api}` fills from kometa's own pair credential. - **Credential:** kometa needs no write-in step, because config.yml is the mesh's own file. A minted value makes kometa's 03:00 run fail with plex's 401, and nothing of kometa's is overwritten. Accept the server's token for this pair: `secret accept ace kometa plex-api --provider ace --from <file>`. - **End to end:** I ran the pinned kometa image against a throwaway Plex (1.43.4) with the config.yml the mesh renders. Kometa's own Plex client (python-plexapi) connected and listed the library. With a minted value in place of the token it was refused with 401. - **Not tested:** a full `--run`. Kometa checks the TMDb key first, and that key is an operator secret.
Author
Contributor

Superseded: this module now lives in novox/mesh-media-catalog (the media chain, consolidated from #145–#168 in stack order; its non-media parts merged via #195). Closing.

Superseded: this module now lives in novox/mesh-media-catalog (the media chain, consolidated from #145–#168 in stack order; its non-media parts merged via #195). Closing.
mesh-admin closed this pull request 2026-09-30 19:45:22 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#159