So a valid, decade-long token stopped being accepted after ~30 minutes of session inactivity.
The two failures look alike and are not:
#485:400 {"error":"expired JWT access token"} — the token
now:401 {"error":"This session has expired"} — the session
Different mechanism, different error, identical symptom: a panel that silently stops updating while the renderer keeps rendering happily.
I noticed both when diagnosing the first one — the PR text says Terminus "has two independent expiries" — then fixed one and moved on. That's the actual mistake, not the missing variable.
Why disabling rather than extending
It's the state the renderer's own code already assumes ("stock Terminus disables session expiration") — false by default, has to be made true here, same shape as the token period.
And it's the deciding constraint for an unattended appliance: a session that expires needs a human with a password to restore it. A hallway panel has neither.
Post-merge
Reloader will roll the pod (the annotation from #485 covers this). The existing session is already dead, so tokens need re-minting and re-seeding afterwards, then a renderer restart — I'll do that and confirm pushes resume.
Unrelated observation
GET /api/devices returns {"data":[]} — the three junk test devices are gone, so nothing currently consumes the pushed screen. That's fine until the real panel is provisioned in Phase 5, but worth knowing: Terminus is storing an up-to-date screen that no device is bound to yet.
The renderer stopped pushing and the panel preview froze. Every webhook since ~19:37Z:
```
HTTP 401: Unauthorized - This session has expired, please login again
```
## Two expiries, and I fixed one
Terminus has **two independent expiries**:
| | Governs | Status |
|---|---|---|
| `API_ACCESS_TOKEN_PERIOD` | the JWT's own lifetime | fixed in #485 — 10 years |
| `SESSION_EXPIRATION_ENABLED` / `SESSION_INACTIVITY_LIMIT` | the **session behind** that token | left at defaults ← |
So a valid, decade-long token stopped being accepted after ~30 minutes of session inactivity.
The two failures look alike and are not:
- **#485:** `400 {"error":"expired JWT access token"}` — the token
- **now:** `401 {"error":"This session has expired"}` — the session
Different mechanism, different error, identical symptom: a panel that silently stops updating while the renderer keeps rendering happily.
**I noticed both when diagnosing the first one** — the PR text says Terminus "has two independent expiries" — then fixed one and moved on. That's the actual mistake, not the missing variable.
## Why disabling rather than extending
It's the state the renderer's own code already assumes (*"stock Terminus disables session expiration"*) — false by default, has to be made true here, same shape as the token period.
And it's the deciding constraint for an unattended appliance: a session that expires needs a human with a password to restore it. A hallway panel has neither.
## Post-merge
Reloader will roll the pod (the annotation from #485 covers this). The existing session is already dead, so tokens need re-minting and re-seeding afterwards, then a renderer restart — I'll do that and confirm pushes resume.
## Unrelated observation
`GET /api/devices` returns `{"data":[]}` — the three junk test devices are gone, so nothing currently consumes the pushed screen. That's fine until the real panel is provisioned in Phase 5, but worth knowing: Terminus is storing an up-to-date screen that no device is bound to yet.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The renderer stopped pushing and the panel preview froze. Every webhook:
HTTP 401: Unauthorized - This session has expired, please login again
Terminus has TWO independent expiries and the earlier fix only addressed
one. API_ACCESS_TOKEN_PERIOD governs the JWT's own lifetime and is now
10 years. SESSION_EXPIRATION_ENABLED / SESSION_INACTIVITY_LIMIT govern
the SESSION BEHIND that token and were left at their defaults, so a
valid, decade-long token stopped being accepted after about half an
hour of session inactivity.
Worth being precise, because the two look alike and are not: the first
failure was 400 "expired JWT access token", the token itself. This is
401 "This session has expired", the session. Different mechanism,
different error, same symptom — a panel that silently stops updating.
I noticed both expiries when diagnosing the first one, wrote that
Terminus "has two independent expiries", and then fixed one and moved
on. That is the actual mistake here, not the missing variable.
Disabling session expiry is the state the renderer's own code already
assumes ("stock Terminus disables session expiration"), which is false
by default and has to be made true here — the same shape as the token
period. And it is the deciding constraint for an unattended appliance:
a session that expires needs a human with a password to restore it, and
a hallway panel has neither.
Reloader will roll the pod; the ConfigMap annotation added earlier
covers this. Existing tokens must be re-minted afterwards — the current
session is already dead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🤖Automated review — model CLI default (complexity: complex), commit 7e293d3de74a48c93638b23a7c8fbe4f9b2a5d4a.
Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.
Summary
Adds SESSION_EXPIRATION_ENABLED: "false" to the terminus-config ConfigMap (kubernetes/apps/trmnl/terminus.yaml:78), disabling Terminus's session-inactivity/lifetime expiry as a follow-up to the token-lifetime fix in #485. The change itself is a single, well-scoped ConfigMap key addition — no deployment/env wiring changes needed since the container already consumes this ConfigMap via envFrom, and the existing configmap.reloader.stakater.com/reload: terminus-config annotation (added in #485) already covers rolling the pod on this edit. No blocking issues found.
🟡 Suggestions
Config scope is app-wide, not renderer-only.terminus.yaml:78 disables session expiration for the entire Terminus instance, not just the long-lived JWT used by the renderer. Per the PR's own quote of the upstream doc, SESSION_EXPIRATION_ENABLED "determines if your browser session or JWT API token is expired or not" — and kubernetes/apps/trmnl/ingress.yaml exposes a browser-facing admin UI (login, device registration, playlists) at trmnl.renner.uno with no additional auth gate in front of it (unlike some other apps in this repo, e.g. the *-gate-egress.yaml Authentik forward-auth policies for other services — trmnl has none). With this change, an authenticated admin browser session to that UI will also never expire due to inactivity or age, which is a real reduction in defense-in-depth for the one credential currently protecting that UI (password login only). The PR's rationale ("a session that expires needs a human with a password to restore it, and this panel has neither") is true for the renderer's API session, but the same change also weakens session hygiene for human admin logins, which do have a human able to re-auth. Worth a line acknowledging this trade explicitly (the way the API_ACCESS_TOKEN_PERIOD comment above it lays out its trade-offs), even if the conclusion is unchanged — this is an unattended LAN-only appliance and the human UI is presumably rarely used.
Upstream doc citation is unverifiable from the checkout. The comment cites doc/configuration.adoc from the Terminus source (not vendored in this repo) for SESSION_EXPIRATION_ENABLED, SESSION_INACTIVITY_LIMIT (1800s), and SESSION_LIFETIME_LIMIT (86400s). I could not fetch the upstream repo to confirm the variable name/semantics directly, so this is taken on the PR's word — same as the pre-existing API_ACCESS_TOKEN_PERIOD comment above it, which is corroborated by an observed 30-minute expiry matching the documented default. There's no equivalent direct observation here confirming the nameSESSION_EXPIRATION_ENABLED is correct (as opposed to, say, a typo'd env var that Terminus silently ignores, leaving the session bug unfixed). Not blocking — the PR body says a post-merge step will re-mint tokens and confirm pushes resume, which is the actual verification — but flagging since a wrong variable name here would look identical to the current diff (i.e., fail silently and require another investigation cycle like #485→#487).
Tests
No automated tests exist or are expected for this kustomize/ConfigMap-only change; verification is operational (confirm pushes resume after the pod rolls and tokens are re-minted), which the PR body already commits to doing post-merge. Reasonable given the nature of the change.
Recommendation
Looks reasonable — no blocking issues; comments above are optional.
🤖 **Automated review** — model `CLI default` (complexity: complex), commit `7e293d3de74a48c93638b23a7c8fbe4f9b2a5d4a`.
> Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review.
---
## Summary
Adds `SESSION_EXPIRATION_ENABLED: "false"` to the `terminus-config` ConfigMap (`kubernetes/apps/trmnl/terminus.yaml:78`), disabling Terminus's session-inactivity/lifetime expiry as a follow-up to the token-lifetime fix in #485. The change itself is a single, well-scoped ConfigMap key addition — no deployment/env wiring changes needed since the container already consumes this ConfigMap via `envFrom`, and the existing `configmap.reloader.stakater.com/reload: terminus-config` annotation (added in #485) already covers rolling the pod on this edit. No blocking issues found.
## 🟡 Suggestions
- **Config scope is app-wide, not renderer-only.** `terminus.yaml:78` disables session expiration for the *entire* Terminus instance, not just the long-lived JWT used by the renderer. Per the PR's own quote of the upstream doc, `SESSION_EXPIRATION_ENABLED` "determines if your browser session **or** JWT API token is expired or not" — and `kubernetes/apps/trmnl/ingress.yaml` exposes a browser-facing admin UI (login, device registration, playlists) at `trmnl.renner.uno` with no additional auth gate in front of it (unlike some other apps in this repo, e.g. the `*-gate-egress.yaml` Authentik forward-auth policies for other services — trmnl has none). With this change, an authenticated admin browser session to that UI will also never expire due to inactivity or age, which is a real reduction in defense-in-depth for the one credential currently protecting that UI (password login only). The PR's rationale ("a session that expires needs a human with a password to restore it, and this panel has neither") is true for the renderer's API session, but the same change also weakens session hygiene for human admin logins, which do have a human able to re-auth. Worth a line acknowledging this trade explicitly (the way the `API_ACCESS_TOKEN_PERIOD` comment above it lays out its trade-offs), even if the conclusion is unchanged — this is an unattended LAN-only appliance and the human UI is presumably rarely used.
- **Upstream doc citation is unverifiable from the checkout.** The comment cites `doc/configuration.adoc` from the Terminus source (not vendored in this repo) for `SESSION_EXPIRATION_ENABLED`, `SESSION_INACTIVITY_LIMIT` (1800s), and `SESSION_LIFETIME_LIMIT` (86400s). I could not fetch the upstream repo to confirm the variable name/semantics directly, so this is taken on the PR's word — same as the pre-existing `API_ACCESS_TOKEN_PERIOD` comment above it, which is corroborated by an observed 30-minute expiry matching the documented default. There's no equivalent direct observation here confirming the *name* `SESSION_EXPIRATION_ENABLED` is correct (as opposed to, say, a typo'd env var that Terminus silently ignores, leaving the session bug unfixed). Not blocking — the PR body says a post-merge step will re-mint tokens and confirm pushes resume, which is the actual verification — but flagging since a wrong variable name here would look identical to the current diff (i.e., fail silently and require another investigation cycle like #485→#487).
## Tests
No automated tests exist or are expected for this kustomize/ConfigMap-only change; verification is operational (confirm pushes resume after the pod rolls and tokens are re-minted), which the PR body already commits to doing post-merge. Reasonable given the nature of the change.
## Recommendation
Looks reasonable — no blocking issues; comments above are optional.
<!-- pr-review:sha=7e293d3de74a48c93638b23a7c8fbe4f9b2a5d4a -->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The renderer stopped pushing and the panel preview froze. Every webhook since ~19:37Z:
Two expiries, and I fixed one
Terminus has two independent expiries:
API_ACCESS_TOKEN_PERIODSESSION_EXPIRATION_ENABLED/SESSION_INACTIVITY_LIMITSo a valid, decade-long token stopped being accepted after ~30 minutes of session inactivity.
The two failures look alike and are not:
400 {"error":"expired JWT access token"}— the token401 {"error":"This session has expired"}— the sessionDifferent mechanism, different error, identical symptom: a panel that silently stops updating while the renderer keeps rendering happily.
I noticed both when diagnosing the first one — the PR text says Terminus "has two independent expiries" — then fixed one and moved on. That's the actual mistake, not the missing variable.
Why disabling rather than extending
It's the state the renderer's own code already assumes ("stock Terminus disables session expiration") — false by default, has to be made true here, same shape as the token period.
And it's the deciding constraint for an unattended appliance: a session that expires needs a human with a password to restore it. A hallway panel has neither.
Post-merge
Reloader will roll the pod (the annotation from #485 covers this). The existing session is already dead, so tokens need re-minting and re-seeding afterwards, then a renderer restart — I'll do that and confirm pushes resume.
Unrelated observation
GET /api/devicesreturns{"data":[]}— the three junk test devices are gone, so nothing currently consumes the pushed screen. That's fine until the real panel is provisioned in Phase 5, but worth knowing: Terminus is storing an up-to-date screen that no device is bound to yet.🤖 Generated with Claude Code
The renderer stopped pushing and the panel preview froze. Every webhook: HTTP 401: Unauthorized - This session has expired, please login again Terminus has TWO independent expiries and the earlier fix only addressed one. API_ACCESS_TOKEN_PERIOD governs the JWT's own lifetime and is now 10 years. SESSION_EXPIRATION_ENABLED / SESSION_INACTIVITY_LIMIT govern the SESSION BEHIND that token and were left at their defaults, so a valid, decade-long token stopped being accepted after about half an hour of session inactivity. Worth being precise, because the two look alike and are not: the first failure was 400 "expired JWT access token", the token itself. This is 401 "This session has expired", the session. Different mechanism, different error, same symptom — a panel that silently stops updating. I noticed both expiries when diagnosing the first one, wrote that Terminus "has two independent expiries", and then fixed one and moved on. That is the actual mistake here, not the missing variable. Disabling session expiry is the state the renderer's own code already assumes ("stock Terminus disables session expiration"), which is false by default and has to be made true here — the same shape as the token period. And it is the deciding constraint for an unattended appliance: a session that expires needs a human with a password to restore it, and a hallway panel has neither. Reloader will roll the pod; the ConfigMap annotation added earlier covers this. Existing tokens must be re-minted afterwards — the current session is already dead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>🤖 Automated review — model
CLI default(complexity: complex), commit7e293d3de74a48c93638b23a7c8fbe4f9b2a5d4a.Summary
Adds
SESSION_EXPIRATION_ENABLED: "false"to theterminus-configConfigMap (kubernetes/apps/trmnl/terminus.yaml:78), disabling Terminus's session-inactivity/lifetime expiry as a follow-up to the token-lifetime fix in #485. The change itself is a single, well-scoped ConfigMap key addition — no deployment/env wiring changes needed since the container already consumes this ConfigMap viaenvFrom, and the existingconfigmap.reloader.stakater.com/reload: terminus-configannotation (added in #485) already covers rolling the pod on this edit. No blocking issues found.🟡 Suggestions
terminus.yaml:78disables session expiration for the entire Terminus instance, not just the long-lived JWT used by the renderer. Per the PR's own quote of the upstream doc,SESSION_EXPIRATION_ENABLED"determines if your browser session or JWT API token is expired or not" — andkubernetes/apps/trmnl/ingress.yamlexposes a browser-facing admin UI (login, device registration, playlists) attrmnl.renner.unowith no additional auth gate in front of it (unlike some other apps in this repo, e.g. the*-gate-egress.yamlAuthentik forward-auth policies for other services — trmnl has none). With this change, an authenticated admin browser session to that UI will also never expire due to inactivity or age, which is a real reduction in defense-in-depth for the one credential currently protecting that UI (password login only). The PR's rationale ("a session that expires needs a human with a password to restore it, and this panel has neither") is true for the renderer's API session, but the same change also weakens session hygiene for human admin logins, which do have a human able to re-auth. Worth a line acknowledging this trade explicitly (the way theAPI_ACCESS_TOKEN_PERIODcomment above it lays out its trade-offs), even if the conclusion is unchanged — this is an unattended LAN-only appliance and the human UI is presumably rarely used.doc/configuration.adocfrom the Terminus source (not vendored in this repo) forSESSION_EXPIRATION_ENABLED,SESSION_INACTIVITY_LIMIT(1800s), andSESSION_LIFETIME_LIMIT(86400s). I could not fetch the upstream repo to confirm the variable name/semantics directly, so this is taken on the PR's word — same as the pre-existingAPI_ACCESS_TOKEN_PERIODcomment above it, which is corroborated by an observed 30-minute expiry matching the documented default. There's no equivalent direct observation here confirming the nameSESSION_EXPIRATION_ENABLEDis correct (as opposed to, say, a typo'd env var that Terminus silently ignores, leaving the session bug unfixed). Not blocking — the PR body says a post-merge step will re-mint tokens and confirm pushes resume, which is the actual verification — but flagging since a wrong variable name here would look identical to the current diff (i.e., fail silently and require another investigation cycle like #485→#487).Tests
No automated tests exist or are expected for this kustomize/ConfigMap-only change; verification is operational (confirm pushes resume after the pod rolls and tokens are re-minted), which the PR body already commits to doing post-merge. Reasonable given the nature of the change.
Recommendation
Looks reasonable — no blocking issues; comments above are optional.