# Plex plex.tv server-claim token (WS-APPS). SOPS-encrypted (age). # Consumed by catalog/plex.yaml as env PLEX_CLAIM via valueFrom.secretKeyRef. # # NOTE ON THESE COMMENTS: they carry NO dollar-brace expressions, deliberately. This file is applied # by the apps-wave-0 Flux Kustomization, which runs envsubst over the built output, and kustomize # preserves body comments into that stream (see conformance/tests/l0-static/flux-postbuild- # substitution). A braced token quoted from the upstream shell script would be a live hazard here, # not prose, so the upstream conditions below are described in words. # # THIS FILE NO LONGER HOLDS THE PLACEHOLDER. Corrected 2026-09-29: the server was CLAIMED on that # date, and the value here is the SPENT token from that claim. It is inert and safe to leave -- # upstream gates the exchange on PlexOnlineToken being ABSENT from /config, and it is now present on # the plex-config PVC, so this value is a NO-OP on every start whatever it holds. Deleting the Secret # would NOT be safe (see below), which is why a spent token stays here rather than being blanked. # # The claim token is SHORT-LIVED -- minutes. Read the countdown that plex.tv/claim renders when it # issues one; do NOT budget against a number written in this tree. Earlier revisions of this comment # asserted a "~4-minute TTL" as established fact with no citation, and upstream plexinc/pms-docker # documents no expiry at all. That uncited number was load-bearing for the whole procedure design. # # This file exists to make the Secret — and therefore the env reference — ALWAYS RESOLVABLE IN THE # FLUX GRAPH, not to carry a working token. Re-claiming (only needed after a /config wipe) is # scripted: infra/plex/claim-ship.sh, measured at ~2m45s end to end. # # WHY SHIPPING A PLACEHOLDER IS SAFE (verified against plexinc/pms-docker branch `master`, the # s6-overlay v2 script root/etc/cont-init.d/40-plex-first-run, cross-checked byte-identical against # the same file extracted from the running `plexinc/pms-docker:latest` image): # - line 106 guards the claim exchange on TWO conditions: PLEX_CLAIM non-empty AND the # PlexOnlineToken pref read out of /config empty. Once the server is claimed that block is # skipped entirely, so whatever this Secret holds is a NO-OP on every later start. Upstream # README: "If server is already logged in, this parameter is ignored." # - on a still-UNCLAIMED /config, an invalid or expired token makes plex.tv answer 403 with an # empty body. curl's exit status is never checked, the sed for the authentication-token element # yields the empty string, and the success branch at line 120 simply does not fire. There is no # else branch and no non-zero exit; the script has no `set -e` and ends by touching # /.firstRunComplete and echoing, so it exits 0. The server comes up UNCLAIMED and serving. # It does NOT crashloop. # - the image never sets S6_BEHAVIOUR_IF_STAGE2_FAILS, whose default is 0 ("continue silently even # if a cont-init script failed"), so even a hypothetical non-zero exit would not halt startup. # A MISSING Secret would NOT be safe: the ServiceDefinition schema's valueFrom.secretKeyRef shape # has no `optional` field, so an absent Secret is a hard CreateContainerConfigError. That is exactly # why this file is committed and listed in apps/wave-0/kustomization.yaml instead of being created # by hand at claim time. # # INJECTION / ROTATION (operator, not agents) — runbooks/plex-claim.md has the timed procedure: # infra/devcontainer/dev.sh run sops apps/wave-0/plex-claim-secret.sops.yaml # Never decrypt this file into a shell, a log, or a shared terminal. apiVersion: v1 kind: Secret metadata: name: plex-claim namespace: plex labels: app.kubernetes.io/name: plex type: Opaque stringData: PLEX_CLAIM: ENC[AES256_GCM,data:nLmzeUBWRhRjUakrGiGzQpQuqZNcIYvRH6M=,iv:z0on/dArsNejNLrp3V1nSiNLFzKcREhPuMO1634CwQw=,tag:Ih23CT1JYna/5lfYQUb1Dw==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2dWtqOVNQalZ2QlRxOXJa cDc1MTdtWENkdWIxS1JId3dIU1JDSmk0ODNrCk1YUzhFWXR3bUxyV2NNcmFOakdz THl1Q284dFJxTy9Rd1h0eXJJaVZtdmcKLS0tIERuN0ZFeW9zdi9aNFRYNWxmUS9s QUtYdHEvN042ajNoNktOLzlYQmQrc1kK4NHozMbXVzNGYbUBRbK6a9Uw/sVPAfe6 RW5KWzZ4I/hloqjN3zzVISoSMieMkBGDnF5qZS5N6FqIc6/HrsDD8A== -----END AGE ENCRYPTED FILE----- lastmodified: "2026-09-29T06:31:16Z" mac: ENC[AES256_GCM,data:62BmBAq45CD2g6LDyx+Ho4bTfrhJcaro/RB/iM+OaRc4a/D83MhjbcBL11Uz/O726inqhkvCKDJ/fFemoc50JDbtC79cYMaxrGJNjFM0sdckWrowPvh6ie7HHHUBRBaefl+gNfnc+pbvcncvRzwA4RyzcZse1wbxKfHN8FjEopE=,iv:h0cMShs3tIgnY6eruUYymYrcLFzQZLk/uXQJ/Jsd5ek=,tag:nZ9Pau7p4xWT1QCN1vv2vA==,type:str] pgp: [] encrypted_regex: ^(data|stringData)$ version: 3.9.4