Feature Specification: Component peertube
Created: 2026-10-07
Status: Implemented
Input: Reconstructed from the code at 093e68b6f (core/components/operator/peertube/default.nix, core/components/operator/peertube/oidc-plugin.nix, core/components/operator/peertube/oidc-plugin-install.nix, core/components/operator/peertube/test.nix, core/components/operator/peertube/test-script.py, core/components/operator/peertube/test-secrets.nix, core/components/operator/default.nix, core/components/operator/app-resources.nix, core/components/operator/operator-facing-options.csv, core/components/operator/testing/lib.nix, core/checks/nix-unit-tests.nix), pull requests #1384, #1597, #1651, #1694, #1724, #1753, #1835, #1853, #1911.
User Scenarios & Testing (mandatory)
User Story 1 - Run a PeerTube instance for a group (Priority: P1)
An operator enables peertube in the operator group. The component places
resource requests for single sign-on, mail, cache, database, object storage,
workload identity, TLS and public ports. The setups route each request to a
provider in the deployment. The node then serves PeerTube at
https://<subdomain>.<domain>, and accepts RTMP and RTMPS live streams. The
family spec ../spec.md gives what all operator applications
share.
Why this priority: the component exists to deploy this application.
Independent Test: test-peertube-service
(core/components/operator/peertube/test.nix). It posts a video in the
browser, finds the video in garage, and backs up the database.
Acceptance Scenarios:
- Given
peertube.enable = true, When the component evaluates, Then it requestspeertube-oidc,peertube-smtp,peertube-cache,peertube-db,spiffe,tls,ingest,peertube-bucketandcodez. - Given
peertube.enable = false, When the component evaluates, Then it requests no resources. - Given the
ingestrequest, When the firewall resolves, Then TCP ports80,443,1935and1936are public.
User Story 2 - Log in with the group's single sign-on (Priority: P1)
A user of the group logs in to PeerTube with the account from the group
directory. PeerTube has no declarative plugin option and its own installer
needs the network. So the component installs the auth-openid-connect
plugin offline and writes the plugin row and settings into the database.
Why this priority: SSO is the only path from the group directory to the application.
Independent Test: the subtests "Authelia registered the peertube OIDC
client with the extended request", "Authelia serves discovery" and "OIDC
issuer + secret" of test-peertube-service. No nix-unit check covers
the plugin install.
Acceptance Scenarios:
- Given an authelia provider, When the
peertube-oidcrequest resolves, Then the clientpeertubehas the redirect URIshttps://<domain>/plugins/auth-openid-connect/router/code-cbandhttps://<domain>/plugins/auth-openid-connect/0.x/router/code-cb. - Given PeerTube serves HTTP, When
peertube-oidc-plugin-installruns, Then it copies the plugin into the live plugins store and upserts thepluginrow with the issuer, client and scopes. - Given the client secret credential arrives empty, When the install unit fails, Then systemd retries it after 30 s.
User Story 3 - Configure the instance policy (Priority: P2)
An operator sets instance policy through peertube.settings. The option
type comes from PeerTube's own config/config-schema.json, narrowed to the
keys in operatorSettings. What the operator writes goes to
services.peertube.settings.
Why this priority: operators run their instance by their own policy, but the instance works with the defaults.
Independent Test: nix-unit-lib-option-selection,
nix-unit-lib-json-schema and nix-unit-lib-nixos-service-options cover
the helpers. No check evaluates the peertube selection itself.
Acceptance Scenarios:
- Given a selected key such as
signup.enabled, When the operator sets it, Then the value reachesproduction.yaml. - Given
federation.prevent_ssrforuser.disable_root_auth, When the operator sets it, Then the schema refuses the value. - Given an operator value for a
services.peertubeoption thatcodezalso sets, When the node evaluates, Then the evaluation fails and names the option.
User Story 4 - Store media in garage and back up the database (Priority: P3)
PeerTube writes videos and playlists to garage, and serves them from the
bucket web endpoint. The component backs up the database to the
peertube-backups bucket with restic.
Why this priority: media in object storage keeps the node small, and recovery needs the database.
Independent Test: the "Find video in garage" and "Backing up {db}"
subtests of test-peertube-service.
Acceptance Scenarios:
- Given the garage request, When PeerTube uploads a video, Then it uses the physical buckets of
peertube-videosandpeertube-playlists, andbase_urlis the bucket web URL. - Given a routed
postgresqlprovider, When the backup runs, Thenpg_dumpconnects to the remote host with the rendered password and the contractsslMode.
Edge Cases
- No
redisprovider resolves.services.peertube.redis.createLocallystays at itsmkDefault trueand a local redis runs. - No
postgresqlprovider resolves.services.peertube.database.createLocallystays at itsmkDefault trueand a local postgres runs. - The contract result has a
verify-*sslMode. An assertion fails the evaluation, because the result has no CA path for node'spgclient. - A password file is not rendered yet.
peertube.serviceis ordered afteropenbao-agent-default.service, and thepostgresqlrequest gates thepeertubeunit. - A rotated redis or database password reaches PeerTube only on its next start.
- The install unit runs before PeerTube creates its storage tree. The unit waits for the HTTP listener, because a root-made
storageancestor makes PeerTube fail withEACCES.
Requirements (mandatory)
Functional Requirements
- FR-001: The component MUST declare
name = "PeerTube",package = pkgs.peertube, the iconclient/src/assets/images/logo.svgfrompkgs.peertube.src, andoptional = true. - FR-002: The component MUST offer
enable,subdomain(defaultpeertube) and the hiddendomainfromsharedOptions.nix, and expose onlysettingsofservices.peertube. - FR-003: The component MUST type
settingswith the pinnedpeertube-config-schema(draft 2020-12), narrowed byoperatorSettings: an allow-list at the top layer and deny-lists infederation,tracker,userandclient. - FR-004: The component MUST request an
oidcclientpeertubewith both plugin callback URIs and the scopesopenid,profile,email, and setFEDIVERSITY_OIDC_ISSUERonpeertube.service. - FR-005:
peertube-oidc-plugin-installMUST read the client secret only as its own systemd credential, and MUST order afterpeertube.servicewithWants=, notRequires=. - FR-006: The component MUST request
smtpwith the sendernoreply@<domain>, use STARTTLS on the relay (tls = false,disable_starttls = false), and load the password as the credentialsmtp-password. - FR-007: The component MUST request
rediswithnamespace = "peertube", setenable_tlsfrom thetlsfield of the contract result, with the CA from itscaFilefield (/var/lib/openbao-init/pki-ca.pem), and load the password fromdatabase/static-creds/<namespace>(<namespace>-<deployment namespace>in a tenant deployment) as the credentialredis-password. - FR-008: The component MUST request
postgresqlwith database, consumer and userpeertube, password file ownerpeertubeand mode0400, and setsettings.database.sslwhensslModeis notdisable. - FR-009: The component MUST request
garagethroughmkGarageRequestwith the website bucketspeertube-videosandpeertube-playlists, and renderAWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYinto thepeertubeenv file throughenvSecrets. - FR-010: The component MUST request
spiffe,sslfordomain, andfirewallpublic TCP ports80,443,1935and1936. - FR-011: The
codeznixos-modulerequest MUST enableservices.peertubewithconfigureNginx,listenWeb = 443,enableWebHttps, andserviceEnvironmentFile = /run/openbao-secrets/peertube.env. - FR-012: The component MUST request a
streamingBackupof thepostgresdatabase to thepeertube-backupsbucket with the restic provider. - FR-013: The
redisandpostgresqlrequests MUST contain only data. Thecodezmodule MUST render their clients (cache.nix,database.nix) fromcontracts.redis.results.peertube.defaultandcontracts.postgresql.results.peertube.default, and MUST render nothing when the result is absent.
Key Entities
peertube-oidc-plugin-install.service: the root oneshot that installs the offline plugin and seeds itspluginrow.oidc-plugin.nix: the plugin directory, built without network from a vendoredpackage-lock.json./run/openbao-secrets/peertube.env: the env file with the S3 keys.peertube-secret: thevars-backendgenerateFilesinstance with the filesecret(openssl rand -hex 32, owned bypeertube), forservices.peertube.secrets.secretsFile. It is minted for each deployment and kept in OpenBao KV.peertubestarts aftergenerate-varsandopenbao-agent-default, and restarts when the agent renders a different value.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001:
test-peertube-servicepasses. A video posted in the browser is in garage, and the backup works. - SC-002:
nix-unit-setups-operator-config,nix-unit-setups-postgresql-render-gateandnix-unit-setups-valkey-gatepass. - SC-003: After one run of the install unit, the plugin settings carry the display name
Fediversity SSO.
Assumptions
- The
operatorgroup has the sharedvalkey,smtp,lldapandautheliacomponents. See../spec.md. - The schema comes from a branch pin of PeerTube
develop, because no release ships the file. A rename of a selected key fails the evaluation. - PeerTube's
PEERTUBE_SMTP_*env overrides are inert under NixOS, so SMTP goes throughsettings.smtp.
Known Deviations
mkGarageRequestgives each website bucket a CORS rule forGETfrom*. A TODO says this is too broad.