# 0056: A device credential lasts while the device is in use - Status: Accepted - Date: 2026-10-12 - Supersedes: the "Session lifetime" section of [0036](0036-managed-device-identity-and-pairing.md). Everything else in ADR 0036 — the device as its own principal, pairing, revocation on every request, re-pairing as a new identity — is unchanged. ## Context ADR 0036 gave a device credential a fixed 180 days from pairing. The intent was that a screen on a ceiling should not need re-pairing on a short cycle, and that an abandoned screen should eventually stop being an identity. A fixed horizon does the second and fails the first. Every screen in the building expires on the anniversary of its pairing whether or not it was in use the day before, and drops to a pairing code in front of the congregation until someone with Dashboard access walks to it. For screens paired together, that is every screen at once. Nothing about a working, unrevoked screen is less trustworthy on day 181 than on day 179; only the calendar changed. ## Decision **The 180 days are an idle window, not a lifetime.** A device credential is accepted while the device was last seen less than 180 days ago (or, if it has never been seen, was paired less than 180 days ago). `lastSeenAt` already moves forward on authenticated device requests (throttled to one write per 15 minutes), so the server-side window slides with use and needs no new column or migration. **The browser's copy is renewed by the host, not rotated.** The display descriptor — `GET /api/v1/module-displays/`, which every display requests when it starts, when it reconnects, and periodically while it runs — re-issues the device cookie that authenticated the request with a fresh 180-day `Max-Age` and the same attributes as at pairing (`HttpOnly`, `SameSite=Strict`, host-only, `Path=/`, `Secure` on https). The value is unchanged. Renewal happens only for a request that already resolved to a live, unrevoked device. A revoked, expired, or unknown credential is anonymous, as before, and is never renewed; a person viewing a display never receives a device cookie. **Revocation remains the boundary.** It is still checked on every request, and an administrator's revoke still takes effect immediately. ## Why not rotate Rotation (issue a new value, invalidate the old) would bound how long any one stolen value works and could reveal a cloned credential. For an unattended screen it trades that for a new way to strand the display: a renewal response lost to a network drop, a power cut, or a browser that never persisted the new cookie leaves the screen holding a dead credential and showing a pairing code, which is the failure this decision exists to remove. A refresh-token scheme with overlap windows could avoid that, at the cost of real complexity in the one place mistakes are invisible until a wall goes dark. That is not justified while revocation is immediate and a device principal can only read the displays it was paired for. ## Consequences - A screen that is used at least once every 180 days stays paired until an administrator revokes it. An abandoned screen still stops being an identity 180 days after it was last seen. - Existing credentials need no migration. A screen paired under the fixed rule is renewed the next time it requests its descriptor, provided its browser still holds the cookie. - A stolen device credential stays usable for as long as anyone keeps using it, until it is revoked. Under ADR 0036 it was usable for at most 180 days from pairing. The exposure is bounded by what a device may do — the display permissions of the module it was paired for, never a person's access — and by revocation. Operators can see each device's last-seen time and revoke anything unexpected. - A credential is not bound to a browser or network. Like a human session cookie, whoever presents it is the device. ## Alternatives - **Keep the fixed 180 days.** Rejected: every in-use screen fails on a schedule, and screens paired together fail together. - **Never expire.** Rejected: an abandoned or forgotten screen would remain an identity forever, and its credential would never stop working without someone remembering to revoke it. - **Rotate on renewal.** Rejected for now; see above. - **Renew on every authenticated response.** Rejected: principal resolution happens deep in shared middleware that never writes responses. One endpoint every display already requests periodically is enough, and keeps the behaviour in one visible place.