feat: let users rename environments#7267

closed6
main
GitHub fork: feat/rename-environments
openedAug 16, 2026byIinayayousfi
View on GitHubTwo-way sync enabledPR last imported 14d ago

Written by inayayousfi, typed by gpt-5.6-sol running in OpenCode. Every call here is inayayousfi's, and no agent acted on its own.

Problem

Environment names come from the host machine, so multiple servers on one machine can appear under the same name. Discussion #7017 describes this with several T3 Connect environments that all inherit one hostname.

Users also had no shared rename path. A client-local name could fix one device, but other web, desktop, mobile, and T3 Connect clients would keep showing something else.

Solution

Store the custom name in server settings and use it in the environment descriptor. Clearing the value restores the detected machine name.

Web and desktop get an inline editor in Settings → Connections. Mobile gets a native rename sheet in Settings → Environments. Any connected client with the orchestration operate scope can rename an environment.

Name changes propagate through the server config stream. T3 Connect receives the same effective name through an environment-authenticated relay endpoint with bounded retries.

Duplicate names remain allowed after confirmation. Existing mobile-only names are ignored so the server stays the source of truth.

Related Work

PR #4538 stores names on one web client, so other clients do not share the override.

PR #4850 also introduces a server-owned name. This pull request extends that model to remote environment editing, native mobile controls, duplicate confirmation, an explicit live label event, and direct relay synchronization.

PR #6053 addresses the narrower WSL hostname collision by adding an automatic distro suffix.

Web Evidence

Paired environment

A dark Connections page showing a paired remote environment with the same machine name

Editing the remote name

A dark Connections page showing the inline remote environment name editor

Renamed environment

A dark Connections page showing the paired remote environment renamed to T3 Server

The Playwright pass loaded built web assets, paired a separate t3 serve environment, and renamed it to T3 Server. The browser reported no console errors.

Verification

  • packages/contracts: 36 focused tests passed; type check passed.
  • packages/client-runtime: 14 focused tests passed; type check passed.
  • apps/server: 4 focused tests passed; type check passed.
  • infra/relay: 16 focused tests passed; type check passed.
  • apps/web: type check passed.
  • apps/mobile: type check passed.
  • Targeted formatting and lint passed.
  • git diff --check passed.

Addresses discussion #7017 and the converted issue #5623.


Note

Medium Risk Touches settings persistence, WebSocket live updates, and relay synchronization; unsupported servers surface rename errors, while relay sync failures are only logged.

Overview Introduces server-owned environment names via a new environmentLabel setting (up to 40 characters; empty restores the machine name). The execution descriptor and connected clients pick up changes through a dedicated environmentLabelUpdated config stream event and updateEnvironmentLabel RPC.

Web adds inline rename controls in Connections (primary environment + saved backends) gated on AuthOrchestrationOperateScope, with duplicate-name confirmation. Mobile moves rename out of the expanded connection row into a dedicated EnvironmentRename sheet and only shows rename when connected with the same scope; URL-only edits no longer send a label.

Server applies settings to an in-memory label override, starts background jobs to mirror settings into the descriptor and to push labels to the relay (PUT /v1/environments/:environmentId/label) with retries and cancellation of stale syncs. Relay link rows get updateLabel for the authenticated environment credential.

Client presentation prefers serverConfig.environment.label over connection-catalog labels; bearer URL updates can omit label so stored names are preserved.

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

Note

Add environment renaming across web, mobile, server, and relay

  • Adds an environmentLabel field to ServerSettings (max 40 chars, defaults to empty) and a new setEnvironmentLabel method on ServerEnvironment; getDescriptor returns the custom label when set.
  • Adds a relay API endpoint PUT /v1/environments/:environmentId/label to persist label changes to active environment links in the database.
  • Adds updateEnvironmentLabel to client-side server environment atoms, which patches settings via WebSocket and verifies the server acknowledges the rename; emits environmentLabelUpdated stream events to connected clients.
  • Adds rename UI to the web connections settings page (EnvironmentLabelControl) and a dedicated mobile screen (SettingsEnvironmentRenameRouteScreen), both with duplicate-name confirmation and error handling.
  • On startup, the server subscribes to settings changes to keep the in-memory label in sync and runs a background relay sync task (runEnvironmentLabelRelaySync) that pushes label updates to the relay with exponential retry.

Macroscope summarized e7eb70e.

Checks

1 failing · 15 successful · 7 skipped · 1 neutral
Some checks failed

Failing1

e7eb70e

Added 1 commit

  • feat: let users rename environmentsc3f5a86
Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c7aa9e06-7a90-4f3f-9c55-3347dc8d19a2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

apps/server/src/cloud/EnvironmentLabelRelaySync.tsline 49Outdated · c3f5a86Open thread
……@@ -0,0 +1,81 @@
46+ readSecretString(RELAY_URL_SECRET),
47+ readSecretString(RELAY_ENVIRONMENT_CREDENTIAL_SECRET),
48+ ]);
49+ if (!relayUrl || !environmentCredential) return;

🟡 Medium cloud/EnvironmentLabelRelaySync.ts:49

When RELAY_URL_SECRET or RELAY_ENVIRONMENT_CREDENTIAL_SECRET is absent, synchronize returns successfully and the current environment label is never sent after applyCloudRelayConfig installs the credentials. Secret changes do not emit a settings event, so a server linked at runtime remains unsynchronized until the label changes or the server restarts. Trigger synchronization when the relay configuration is applied, or otherwise subscribe to secret changes.

🤖 Copy this AI Prompt to have your agent fix this:
text
1In file @apps/server/src/cloud/EnvironmentLabelRelaySync.ts around line 49:
2
3When `RELAY_URL_SECRET` or `RELAY_ENVIRONMENT_CREDENTIAL_SECRET` is absent, `synchronize` returns successfully and the current environment label is never sent after `applyCloudRelayConfig` installs the credentials. Secret changes do not emit a settings event, so a server linked at runtime remains unsynchronized until the label changes or the server restarts. Trigger synchronization when the relay configuration is applied, or otherwise subscribe to secret changes.

Written by inayayousfi, typed by gpt-5.6-sol running in OpenCode. Every call here is inayayousfi's, and no agent acted on its own.

Fixed in f1b37a687. applyCloudRelayConfig now calls synchronizeCurrentEnvironmentLabelWithRelay immediately after persisting the relay credentials. The focused helper test verifies that the current descriptor label reaches the relay. The relay-configuration integration test verifies that synchronization failure stays visible without invalidating the persisted configuration.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

apps/server/src/serverRuntimeStartup.tsline 363Outdated · c3f5a86Open thread
……@@ -355,6 +357,18 @@ export const make = (options?: StartupOptions) =>
360+ yield* serverEnvironment.setEnvironmentLabel(
361+ (yield* serverSettings.getSettings).environmentLabel,
362+ );
363+ yield* serverSettings.streamChanges.pipe(
MmacroscopeappBoton line 363Aug 16, 2026

🟡 Medium src/serverRuntimeStartup.ts:363

A settings rename published between getSettings and the forked streamChanges subscriber is dropped, leaving serverEnvironment and clients on the old label until another change occurs. Subscribe with serverSettings.subscribeChanges before reading the snapshot to close this race.

No longer relevant as of f1b37a687e934e552ec915e185f3e597aa42513b

apps/server/src/cloud/EnvironmentLabelRelaySync.tsline 79Outdated · c3f5a86Open thread
……@@ -0,0 +1,81 @@
76+ yield* changes.pipe(
77+ Stream.map((next) => next.environmentLabel),
78+ Stream.changes,
79+ Stream.runForEach(synchronizeWithRetry),

🟡 Medium cloud/EnvironmentLabelRelaySync.ts:79

When a relay request is failing, a newer environment label remains queued behind the obsolete label's retries, so T3 Connect can display the old name for up to 10 minutes (and longer across multiple edits). Stream.runForEach(synchronizeWithRetry) processes events serially; switch or cancel the retrying synchronization when a newer label arrives, or retry only the latest label.

No longer relevant as of f1b37a687e934e552ec915e185f3e597aa42513b

MmacroscopeappBotreviewed · 3 inline threadsAug 16, 2026
Synced from GitHub

UI consistency review found 3 issues in apps/web/src/components/settings/ConnectionsSettings.tsx (new EnvironmentLabelControl): a primitive height override that breaks the Input geometry, an Escape handler that also closes the settings page, and a native window.confirm where the file already uses AlertDialog.

Posted via Macroscope — UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1445–1447Outdated · c3f5a86Open thread
……@@ -1333,20 +1337,149 @@ function NetworkAccessDescription({
1444+ onChange={(event) => setValue(event.target.value)}
1445+ onKeyDown={(event) => {
1446+ if (event.key === "Escape") cancel();
1447+ }}
MmacroscopeappBoton lines 1445–1447Aug 16, 2026

settings/ConnectionsSettings.tsx:1447

Escape cancels the rename but still bubbles, so it also closes the settings page/dialog — the same trap called out in SettingsPanels.tsx ("Discard uncommitted typing without closing the settings page, which is what an unhandled Escape does"). Suggest stopping the event once it is handled.

suggestion
1 onKeyDown={(event) => {
2 if (event.key === "Escape") {
3 event.preventDefault();
4 event.stopPropagation();
5 cancel();
6 }
7 }}

No longer relevant as of f560e7e1231c55d312a6093f49fe6738f02073a6

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1439–1439Outdated · c3f5a86Open thread
……@@ -1333,20 +1337,149 @@ function NetworkAccessDescription({
1436+ >
1437+ <Input
1438+ autoFocus
1439+ className="h-7 w-48"
MmacroscopeappBoton lines 1439–1439Aug 16, 2026

settings/ConnectionsSettings.tsx:1439

Input forwards className to the wrapper <span> only; the inner <input> keeps h-8.5 sm:h-7.5 leading-8.5 from size="default". h-7 therefore shrinks the bordered wrapper to 28px while the field inside stays 34px/30px, so the text sits off-centre and overflows the border/radius. The primitive already has a named compact size (h-7, rounded-md, matching before: radius) — use it and keep only width at the call site.

suggestion
1 size="compact"
2 className="w-48"

No longer relevant as of f560e7e1231c55d312a6093f49fe6738f02073a6

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1384–1388Outdated · c3f5a86Open thread
……@@ -1333,20 +1337,149 @@ function NetworkAccessDescription({
1385+ duplicate &&
1386+ !window.confirm(
1387+ `Another environment is already named “${nextLabel}”. Use this name for both environments?`,
1388+ )
MmacroscopeappBoton lines 1384–1388Aug 16, 2026

settings/ConnectionsSettings.tsx:1388

This is the only window.confirm in apps/web/src; every other confirmation in this file (WSL toggle, network exposure, Tailscale disable) uses the shared AlertDialog primitives already imported here. The native dialog is unthemed, ignores the app's focus/keyboard handling, and blocks the renderer in the Electron shell. Suggest driving it from local state (e.g. pendingDuplicateLabel) rendered through AlertDialog/AlertDialogPopup with AlertDialogClose for Cancel and a Button for "Use name", matching the existing rows. Fix spans state plus JSX, so no inline suggestion.

No longer relevant as of f560e7e1231c55d312a6093f49fe6738f02073a6

Ccursor[bot]reviewed · 2 inline threadsAug 16, 2026
Synced from GitHub

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c3f5a86b4daa7e4420f09f5ad18a1c71ac9b8ff5. Configure here.

apps/server/src/cloud/EnvironmentLabelRelaySync.tsline 80Outdated · c3f5a86Open thread
……@@ -0,0 +1,81 @@
77+ Stream.map((next) => next.environmentLabel),
78+ Stream.changes,
79+ Stream.runForEach(synchronizeWithRetry),
80+ );

Stale relay sync overwrites label

Medium Severity

synchronize calls setEnvironmentLabel inside an Effect.retry loop, and Stream.runForEach waits for each attempt (up to 10 minutes). A failing sync can rewrite the in-memory label back to an older value and delay later renames from reaching the relay.

Reviewed by Cursor Bugbot for commit c3f5a86b4daa7e4420f09f5ad18a1c71ac9b8ff5. Configure here.

……@@ -46,9 +47,16 @@ export function ConnectionEnvironmentRow(props: {
4957 const handleSave = useCallback(async () => {
5058 const result = await props.onUpdate(props.environment.environmentId, {
51- label: label.trim(),
59+ label: "",

URL save always rejects empty label

High Severity

After removing the local label field, URL save still calls onUpdate with label: "". prepareBearerConnectionUpdate rejects empty labels, so saving a URL-only change always fails with “Environment label cannot be empty.”

Reviewed by Cursor Bugbot for commit c3f5a86b4daa7e4420f09f5ad18a1c71ac9b8ff5. Configure here.

Synced from GitHub

Approvability

Verdict: Needs human review

1 blocking correctness issue found. This PR introduces a new user-facing feature for environment renaming spanning mobile, web, server, and relay infrastructure with new API endpoints and cross-cutting changes. New features of this scope warrant human review to validate the design and implementation.

You can customize Macroscope's approvability policy. Learn more.

Added 2 commits

  • fix(web): align environment name settingd73d6f9
  • fix(web): align environment rename interactionsf560e7e
MmacroscopeappBotreviewed · 1 inline threadAug 17, 2026
Synced from GitHub

One finding on the new environment rename control in apps/web/src/components/settings/ConnectionsSettings.tsx. The dialog/escape/compact-input issues from the previous run are resolved.

Posted via Macroscope — UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1409–1411Outdated · f560e7eOpen thread
……@@ -1333,20 +1337,193 @@ function NetworkAccessDescription({
1408+ {canRename ? (
1409+ <Button
1410+ size="icon-xs"
1411+ variant="ghost"
MmacroscopeappBoton lines 1409–1411Aug 17, 2026

settings/ConnectionsSettings.tsx:1411

The pencil trigger renders inline in the row title line (<div className="flex min-h-5 items-center gap-1.5">, line 1588), which is sized for 20px content — the sibling saved-environment rows and the pairing/client rows in this file put nothing taller than the h3 there. icon-xs is size-7 (sm:size-6), so every connected environment with operate scope gets a 24–28px title line while the rows without the pencil stay at 20px, and the same happens in the SettingsRow title convention (SettingResetButton reserves h-5 w-5 and uses icon-micro). Settings-level inline micro actions elsewhere (settingsLayout.tsx, KeybindingsSettings, DiagnosticsSettings, ProviderInstanceCard) all use size="icon-micro" + variant="ghost-muted" with a size-3 icon, which is exactly this geometry. Suggest using the micro size for the trigger (the save/cancel buttons inside the edit form should stay icon-xs so they match the h-7 compact input).

suggestion
1 <Button
2 size="icon-micro"
3 variant="ghost-muted"

No longer relevant as of f1b37a687e934e552ec915e185f3e597aa42513b

Added 1 commit

  • fix: harden environment rename flowsf1b37a6
MmacroscopeappBotreviewed · 1 inline threadAug 17, 2026
Synced from GitHub

One inline-rename consistency issue in apps/web/src/components/settings/ConnectionsSettings.tsx. The primitive usage itself (compact Input, icon-micro/ghost-muted pencil matching SettingResetButton, AlertDialog for the duplicate-name confirmation, Escape handled locally) now lines up with the shared component system.

Posted via Macroscope — UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1599–1604Outdated · f1b37a6Open thread
……@@ -1419,6 +1596,12 @@ function SavedBackendListRow({
1601+ label={environment.label}
1602+ environmentLabels={environmentLabels}
1603+ canRename={canRename}
1604+ />
MmacroscopeappBoton lines 1599–1604Aug 17, 2026

settings/ConnectionsSettings.tsx:1604

While the rename form is open this row still renders <h3>{environment.label}</h3> right above it (line 1598), so the environment name is shown twice side by side and the min-h-5 title line jumps to the input's height. The existing inline-rename pattern in this app swaps the title for the field instead of rendering both (Sidebar.tsx: const title = isRenaming ? <input …/> : <span …/>; ChatHeader.tsx line 265).

Suggest letting EnvironmentLabelControl own the displayed name in this row as well — e.g. reuse the existing showValue branch by passing the heading node in (a renderLabel/valueNode prop rendered in the non-editing return), so the static <h3> disappears while the form is visible.

No longer relevant as of 3b5eea815e7ac1f29a7ffe028f83ec7c9099d1ac

Added 1 commit

  • fix(web): replace environment title while renaming3b5eea8
MmacroscopeappBotreviewed · 1 inline threadAug 17, 2026
Synced from GitHub

One finding on the web rename control: the saved-environment row lost its heading element when the label moved into EnvironmentLabelControl.

Posted via Macroscope — UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1409–1409Outdated · 3b5eea8Open thread
……@@ -1333,20 +1337,195 @@ function NetworkAccessDescription({
1406+ if (!editing || !canRename) {
1407+ return (
1408+ <>
1409+ {showValue ? <span className={valueClassName}>{label}</span> : null}
MmacroscopeappBoton lines 1409–1409Aug 17, 2026

settings/ConnectionsSettings.tsx:1409

showValue always renders the name in a <span>, so SavedBackendListRow (line 1600) now emits a plain span where it previously emitted <h3 className="text-sm font-medium text-foreground">{environment.label}</h3>. Every peer row on this page still names itself with an h3 (pairing link line 697, client session line 936, SSH target line 1728, and SettingsRow itself), so the remote-environments list is the only one that drops out of the heading outline for screen-reader/heading navigation — the styling is unchanged, only the semantics regressed.

Suggest letting the caller choose the value element (e.g. an optional valueAs?: "span" | "h3" defaulting to "span", rendered here instead of the hard-coded span) and passing valueAs="h3" from SavedBackendListRow, so the two SettingsRow usages keep a span (their h3 is the row title) while the list row keeps its heading.

No longer relevant as of e7eb70ec31619cce0585e9e58f111cf05fb40b39

Added 1 commit

  • fix(web): preserve environment heading while renaminge7eb70e
Synced from GitHub

I was about to open the same PR. Hope this gets merged soon. Let me know if I can help in any way

Synced from GitHub

I hit the stale-label case this PR appears to address. After renaming a Linux host, the environment's local and public descriptors both returned the new hostname. I then disabled and re-enabled T3 Connect and refreshed the mobile app, but Settings → Environments continued to show the old hostname.

This was on T3 server/desktop v0.0.40. The environment ID remained unchanged, so preserving identity while synchronizing a server-owned label through the relay—as implemented here—looks like the right model.

Happy to test this flow again once the change reaches a release.

Synced from GitHub

I was about to open the same PR. Hope this gets merged soon. Let me know if I can help in any way

Same - this is an important addition. Thanks for opening it!

Synced from GitHub

Thank you guys, it has been opened for a while, but I need to see how to resolve the conflict. I will do it when I have the time to.

Note

This comment is posted by Julius' dot

Closing for missing UI verification. This PR replaces the mobile label field with a native rename sheet, but the three supplied images show only web Connections and the mobile verification reports only a type check. Add before/after screenshots of the mobile change and a short recording showing rename, duplicate-name confirmation, and clearing a name, with the device or simulator and observed results, then request reconsideration.