fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(connect): refresh relay credentials before expiry - #9178

Merged
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery
Sep 2, 2026
Merged

fix(connect): refresh relay credentials before expiry#9178
maria-rcks merged 5 commits into
pingdotgg:mainfrom
maria-rcks:t3code/clarify-token-refresh-recovery

Conversation

@maria-rcks

@maria-rcksmaria-rcks commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Relay WebSockets could outlive their one-hour DPoP access tokens, leaving HTTP-backed views such as pull request diffs stuck with stale credentials. Prepared connections now retain token expiry and renew the relay connection one minute early; rejected pull request diff sessions also tell users to refresh the page or reopen T3 Code.

Before

pull request code view showing expired relay credentials

After

pull request code view loading the diff after proactive credential refresh

Verified with 54 focused tests, contracts and client-runtime typechecks, targeted lint and formatting, and diff checks.

This fixes#8326; implemented by gpt-5.6-sol via the Codex harness.


Note

Medium Risk
Changes connection lifecycle and a breaking shape on DPoP PreparedHttpAuthorization; incorrect refresh timing could cause extra reconnects or brief auth gaps on relay environments.

Overview
Relay connections now renew before DPoP access tokens expire, so long-lived WebSockets do not keep using credentials that HTTP calls (e.g. pull request diffs) can no longer use.

Prepared DPoP authorization carries expiresAtEpochMs, with a shared DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS (60s) used for cache hits in authorization and for timing in EnvironmentSupervisor. While connected, the supervisor races session teardown with waitForAuthorizationRefresh, which sleeps until expiry minus the skew and triggers a reconnect with a fresh token.

When a pull request diff request gets invalid_credential, it is mapped to PullRequestDiffCredentialRejectedError with guidance to refresh the page or reopen T3 Code.

Reviewed by Cursor Bugbot for commit 62480b0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh DPoP relay credentials before expiry in EnvironmentSupervisor

  • DPoP tokens now carry expiresAtEpochMs in PreparedHttpAuthorization.Dpop; a shared 60-second refresh skew constant in model.ts replaces the previous service-local constant in service.ts.
  • waitForAuthorizationRefresh in supervisor.ts sleeps until expiresAtEpochMs minus the skew for DPoP connections, then emits a refresh signal; the runAttempt connected lifecycle now races this waiter alongside session closure and lease monitoring, leaving the connected state to trigger reconnect when the token nears expiry.
  • Adds PullRequestDiffCredentialRejectedError in pullRequestDiffHttp.ts and maps invalid_credentialEnvironmentAuthInvalidError from fetchEnvironmentPullRequestDiff to it with a fixed recovery message; PullRequestDiffLoader.load now declares the widened error union.
  • Behavioral Change: PreparedHttpAuthorization.Dpop now requires expiresAtEpochMs, so all fixtures and consumers must supply it. DPoP-connected supervisors now leave the connected state when the token enters the 60-second refresh window, in addition to existing session/lease triggers.

Macroscope summarized 62480b0.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 2, 2026
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
Comment threadpackages/contracts/src/environmentHttp.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes the production lifecycle of relay DPoP connections and adds authentication-specific error handling. The affected authorization and credential paths are sensitive and warrant human review.

You can add or adjust custom eligibility rules. Learn more.

Comment threadpackages/client-runtime/src/state/pullRequestDiffHttp.ts Outdated
@maria-rcks
maria-rcks merged commit 9e646ad into pingdotgg:mainSep 2, 2026
27 checks passed
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expired DPoP session returns HTTP 200 with authenticated:false, so clients report it as a permissions denial

1 participant

@maria-rcks