Repository navigation
Replies: 7 comments
TriageConfirmed as a real product gap, not a misread of the current design. Pairing-issued sessions have a fixed absolute 30-day TTL with no operator override and no activity-based extension. What the code does today
This matches the reporter’s Related
Suggested directionProduct call first (absolute longer TTL vs sliding vs durable device auth + rotated short tokens). Smallest useful change after that: a server setting/env for default pairing-session TTL, applied explicitly at both pairing issue call sites, plus user/ops docs. Sliding expiry or auto-refresh is a larger follow-up because of the signed Labels: |
Previously paired desktop environments reject credentials while all apps remain runningWhat happenedTwo remote environments that previously worked now show this error in the macOS T3 Code desktop app:
The user reports that the local app and the app instances on both remote PCs remain running. They expected those connections to remain usable without manually pairing again. DiagnosisConfirmed: both remote servers answer discovery requests successfully, then reject the desktop client's bearer-authenticated WebSocket-ticket request with HTTP 401 and The retained traces already show failures while the desktop was on Possible explanation, not confirmed for these sessions: the fixed lifetime of directly paired bearer credentials described in #13055. Source at release commit
The affected sessions' issue/expiry/revocation timestamps and remote verification errors were not available. Expiry cannot yet be distinguished from revocation, missing session records, or signature failure. The event that triggered reconnection is also unknown. No credential was decrypted, and the secrets directory was not read. Documentation gapThe user was unaware that a paired device could require pairing again after a fixed lifetime. The installed-release documentation and the current main README and remote-access guide were checked on September 22, 2026:
Regardless of whether expiry caused these particular failures, the direct-pairing guide should disclose the lifetime and recovery steps. An expiry-specific client message would also make recovery easier than the current generic invalid-credential message. Steps to reproduceReported sequence, not a deterministic reproduction from fresh state:
Original pairing dates and the precise transition from working to failing have not been established. No fresh authenticated reproduction was run during triage. Version
EnvironmentmacOS client, Darwin 25.5.0, arm64. Triage CLI used Node v26.8.2 and was launched with EvidenceSanitized summary of retained client trace spans; hostnames, IP addresses, identifiers, credentials, and home paths omitted: Related issues
This is intended as additional evidence and user impact on an existing discussion, not a claim of a new confirmed root cause. Fix applied or workaroundNone applied. Fresh pairing links from each host were proposed, but have not been generated or redeemed. No authentication state, database records, or application source were changed. To resolve the remaining uncertainty, inspect only the affected remote sessions' Filed byPrepared by Codex (GPT-6) via |
|
+1 — same kind of deployment here: On day 30 our desktop clients lost access without warning, and from the client side it looked like a server outage rather than an expired pairing. We worked around it on desktop by provisioning a bearer session issued with Related: after re-pairing, our desktop app still did not reconnect because the environment had been switched off in the meantime — reported separately. |
|
We hit this too: a single-operator I'd like to propose option 2 (sliding expiry) and offer a PR if it's the direction you'd accept. We have a working prototype (+544/−5, about half of it tests) that we haven't submitted. Here's the design so you can say yes or no before anyone spends review time on it. Server
Client (
Tradeoffs we know about
Relationship to #13092: they're compatible. A configurable If you'd rather go with option 3 (TTL carried by the pairing grant) or a different renewal shape, we'll follow that and drop this. The prototype was written with Codex. We'd rebase it onto current |
|
+1 on this issue. I'm not sure what the best solution is, but having to have codex ssh around and copy in some new pairing urls ain't it. |
|
Another data point for the product decision. Setup: T3 Code 0.0.45 as the background service (systemd user unit) on a home server. It listens on 127.0.0.1 only and is published to my tailnet with Tailscale Serve. One operator, and every client is my own device: the Windows desktop app and browsers so far, Android phones next. Every device paired on the same day expires on the same day. For a setup like this, an operator-set lifetime (#13092's |
|
Another data point: a single-operator setup with the T3 Code Nightly desktop app on macOS, the iOS app over LAN and Tailscale, all my own devices. The phone's session expired on day 30 with no warning, and nothing in the app said so. It showed a connection timeout, even after reordering routes and relaunching the app several times, because an unreachable LAN route masked the 401. Only after I removed that route did it say "The environment credential is invalid." The server logged the reason as Re-pairing didn't cleanly recover it either. Since environments got multiple routes (#15467), re-pairing replaces the login only on the route it pairs through, so the expired login stayed on the Tailscale route and became the only one left once I removed the LAN route. I've filed that separately as #17014. +1 for sliding expiry (option 2). Even without it, the client could show "login expires on " and a one-tap re-pair when the server reports an expired session, which would turn day 30 into a 10-second fix instead of a debugging session. |
Uh oh!
There was an error while loading. Please reload this page.
What happened
Feature request, not a bug. I run T3 Code as a background service (systemd user unit) on a home server, reachable only over my Tailnet / reverse proxy. Every client is my own hardware: the desktop Electron app, Chrome, Firefox, and the mobile app.
Every session created through the normal pairing flow (QR code or link) expires exactly 30 days after it was issued, even on devices I use every day. Activity never extends a session, so each device has to be re-paired by hand every month. On a private, single-operator deployment that is unnecessary friction, and I can't configure it.
The request is to make session lifetime for pairing-issued sessions configurable, in any of these ways:
expiresAtwhen an active session connects or is used.pairing create --ttlonly sets how long the pairing code can be redeemed).Diagnosis
Checked against the source at tag
v0.0.43-nightly.20260922.2096.SessionStore.tsandEnvironmentAuth.tsonmainhave had no changes since then.apps/server/src/auth/SessionStore.ts:423:const DEFAULT_SESSION_TTL = Duration.days(30);, used at:658asinput?.ttl ?? DEFAULT_SESSION_TTL. No config or env override exists.ttl:EnvironmentAuth.createBrowserSession(EnvironmentAuth.ts:712-770) callssessions.issue({ method: "browser-session-cookie", subject, scopes, client }). It has nottl, and the grant carries none.exchangeBootstrapCredentialForAccessToken(EnvironmentAuth.ts:803-835) only setsttl(1h) in the DPoP branch. The plainbearer-access-tokenbranch falls back to the 30-day default.pairing create --ttl→createPairingLink(EnvironmentAuth.ts:884-895) →PairingGrantStore.issueOneTimeToken(PairingGrantStore.ts:385-389). That TTL only sets when the pairing credential expires. It is not stored on the grant for use when the session is issued.SessionStore.markConnected(SessionStore.ts:566-600) only writeslast_connected_at. No code path updatesexpires_atfor a live session (persistence/AuthSessions.tshas no expiry-updating statement).t3 auth session issue --ttl(EnvironmentAuth.issueSession,:932-942). It mints a raw admin bearer token outside the pairing flow, so the browser and mobile pairing UIs can't use it.Steps to reproduce
t3 serveor the background service) and pair a browser, the desktop app, and the mobile app using the normal QR code or link.last_connected_atupdates each day, butexpires_atstays atissued_at + 30d.Version
0.0.43-nightly.20260922.2096
Environment
Linux x64 (Debian, kernel 6.1), Node v26.8.2, server run as a systemd user service behind Tailnet/reverse proxy; clients: desktop (Electron), Chrome, Firefox, mobile app
Evidence
Sessions last used 1–4 days before expiry still expired at exactly
issued_at + 30d.Related issues
#9884 (Session TTL is inverted vs credential strength: DPoP 1h, bearer 30d). Related, but it pushes the other way and asks for an explicit, shorter bearer TTL. Not a duplicate: this issue asks for the pairing-issued session lifetime to be configurable by the operator and/or to slide with activity. A configurable TTL, set explicitly at the pairing call sites, would address both issues: a short default for exposed deployments, and a longer or sliding lifetime for private single-operator ones.
Fix applied or workaround
None applied. The only workaround is re-pairing every 30 days.
t3 auth session issue --ttldoesn't help, because the browser and mobile pairing flows can't redeem the token it mints.Filed by
Claude Code (claude-opus-5) via t3 triage
All reactions