feat(infra): add Render cloud environment - #5378
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
d2edb64 to
35ebbc4
Compare
| if (!directoryName) { | ||
| return null; | ||
| } | ||
| const workspaceRoot = input.workspaceRoot.trim().replace(/[\\/]+$/, ""); |
There was a problem hiding this comment.
🟠 High cloud/cloudEnvironment.ts:63
cloudCloneDestination strips all trailing slashes from workspaceRoot, so a valid POSIX root of / becomes "" and the function returns null instead of /<directoryName>. This prevents cloning when the configured cloud workspace root is the filesystem root. Consider preserving a root path of / before trimming trailing separators.
| const workspaceRoot = input.workspaceRoot.trim().replace(/[\\/]+$/, ""); | |
| const workspaceRoot = input.workspaceRoot.trim().replace(/(?<=^\/)\/+$/, "").replace(/(?<!^)[\/]+$/, ""); |
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/cloud/cloudEnvironment.ts around line 63:
`cloudCloneDestination` strips all trailing slashes from `workspaceRoot`, so a valid POSIX root of `/` becomes `""` and the function returns `null` instead of `/<directoryName>`. This prevents cloning when the configured cloud workspace root is the filesystem root. Consider preserving a root path of `/` before trimming trailing separators.
| const RENDER_DEPLOY_URL = | ||
| "https://dashboard.render.com/blueprint/new?repo=https%3A%2F%2Fgithub.com%2Fpingdotgg%2Ft3code&branch=feat%2Frender-cloud-environment"; |
There was a problem hiding this comment.
🟠 High settings/CloudEnvironmentsSettings.tsx:25
RENDER_DEPLOY_URL hard-codes branch=feat%2Frender-cloud-environment, so once that feature branch is deleted the shipped "Deploy on Render" button will point Render at a nonexistent branch and new users cannot deploy the Blueprint. The URL should target the durable default branch (e.g. main) instead.
| const RENDER_DEPLOY_URL = | |
| "https://dashboard.render.com/blueprint/new?repo=https%3A%2F%2Fgithub.com%2Fpingdotgg%2Ft3code&branch=feat%2Frender-cloud-environment"; | |
| +const RENDER_DEPLOY_URL = | |
| + "https://dashboard.render.com/blueprint/new?repo=https%3A%2F%2Fgithub.com%2Fpingdotgg%2Ft3code&branch=main"; |
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/settings/CloudEnvironmentsSettings.tsx around lines 25-26:
`RENDER_DEPLOY_URL` hard-codes `branch=feat%2Frender-cloud-environment`, so once that feature branch is deleted the shipped "Deploy on Render" button will point Render at a nonexistent branch and new users cannot deploy the Blueprint. The URL should target the durable default branch (e.g. `main`) instead.
| readonly onDisconnect: (environmentId: EnvironmentId) => void; | ||
| }) { | ||
| const isConnected = environment.connection.phase === "connected"; | ||
| const isBusy = busyEnvironmentIds.has(environment.environmentId); |
There was a problem hiding this comment.
🟡 Medium settings/CloudEnvironmentsSettings.tsx:76
When an environment's connection phase is connecting or reconnecting (e.g. from an automatic startup/retry), RenderEnvironmentRow still enables the connect/disconnect button because isBusy only tracks locally initiated commands and ignores environment.connection.phase. Clicking the button during those phases invokes retryNow again, issuing duplicate connection attempts instead of showing the in-progress state. Consider deriving isBusy (or an equivalent disabled flag) from environment.connection.phase as well so the action is disabled while a connection is already in progress.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/settings/CloudEnvironmentsSettings.tsx around line 76:
When an environment's connection phase is `connecting` or `reconnecting` (e.g. from an automatic startup/retry), `RenderEnvironmentRow` still enables the connect/disconnect button because `isBusy` only tracks locally initiated commands and ignores `environment.connection.phase`. Clicking the button during those phases invokes `retryNow` again, issuing duplicate connection attempts instead of showing the in-progress state. Consider deriving `isBusy` (or an equivalent disabled flag) from `environment.connection.phase` as well so the action is disabled while a connection is already in progress.
| const endpoint = new URL("/.well-known/t3/render/pair", host); | ||
| let response: Response; | ||
| try { | ||
| response = await (input.fetcher ?? globalThis.fetch)(endpoint, { |
There was a problem hiding this comment.
🟡 Medium cloud/renderBootstrap.ts:68
The fetch call in requestRenderPairing has no timeout, so if the Render service accepts the connection but never completes the response (common with a free instance waking up), the returned promise stays pending indefinitely. The caller never gets a RenderBootstrapRequestError, leaving the UI stuck and unable to retry without reloading the page. Consider passing an AbortSignal with a bounded timeout to fetch and mapping the abort to the reachability error.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/cloud/renderBootstrap.ts around line 68:
The `fetch` call in `requestRenderPairing` has no timeout, so if the Render service accepts the connection but never completes the response (common with a free instance waking up), the returned promise stays pending indefinitely. The caller never gets a `RenderBootstrapRequestError`, leaving the UI stuck and unable to retry without reloading the page. Consider passing an `AbortSignal` with a bounded timeout to `fetch` and mapping the abort to the reachability error.
There was a problem hiding this comment.
🟠 High
The normal nav renders each icon as <Icon /> with no props, but RenderLogo is a raw SVG without default dimensions, so the Cloud environments row renders at the browser's default replaced-element size (300×150) instead of icon size. This breaks the sidebar row layout. Pass the sizing class used by the search-result path (or give RenderLogo default dimensions) so it matches the other icons.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/settings/SettingsSidebarNav.tsx around line 293:
The normal nav renders each icon as `<Icon />` with no props, but `RenderLogo` is a raw SVG without default dimensions, so the Cloud environments row renders at the browser's default replaced-element size (300×150) instead of icon size. This breaks the sidebar row layout. Pass the sizing class used by the search-result path (or give `RenderLogo` default dimensions) so it matches the other icons.
There was a problem hiding this comment.
Reviewed the Effect-service changes in this PR (apps/server/src/render/http.ts, apps/server/src/environment/ServerEnvironment.ts, packages/contracts/src/*, and the new web modules). One finding on error translation; everything else follows the service/layer, dependency-acquisition, and import conventions.
Posted via Macroscope — Effect Service Conventions
| const issued = yield* serverAuth.issuePairingCredential({ label: "Render setup" }).pipe( | ||
| Effect.mapError( | ||
| () => | ||
| new EnvironmentHttpInternalServerError({ | ||
| message: "Could not create a Render pairing credential.", | ||
| }), | ||
| ), | ||
| ); |
There was a problem hiding this comment.
The mapError callback ignores its argument, so the underlying ServerAuthInternalError (and its stack) is dropped with no diagnostics. EnvironmentHttpInternalServerError is a wire schema with only message, so the underlying failure should at least be preserved via a log, as failEnvironmentInternal does for the same auth failures elsewhere.
| const issued = yield* serverAuth.issuePairingCredential({ label: "Render setup" }).pipe( | |
| Effect.mapError( | |
| () => | |
| new EnvironmentHttpInternalServerError({ | |
| message: "Could not create a Render pairing credential.", | |
| }), | |
| ), | |
| ); | |
| const issued = yield* serverAuth.issuePairingCredential({ label: "Render setup" }).pipe( | |
| Effect.tapError((cause) => | |
| Effect.logError("render pairing credential issuance failed", { cause }), | |
| ), | |
| Effect.mapError( | |
| () => | |
| new EnvironmentHttpInternalServerError({ | |
| message: "Could not create a Render pairing credential.", | |
| }), | |
| ), | |
| ); |
Posted via Macroscope — Effect Service Conventions
| const environmentLabel = | ||
| hostEnvironment.T3CODE_ENVIRONMENT_LABEL?.trim() || "Render cloud environment"; | ||
|
|
||
| return handlers.handle( |
There was a problem hiding this comment.
🔴 Critical render/http.ts:36
The pair handler calls serverAuth.issuePairingCredential with no authentication or secret verification, so any caller who can reach the Render service URL can POST to this endpoint and receive a one-time credential that grants authorized client access. The configuredProvider !== "render" check only validates server-side configuration and does not authenticate the caller. Consider requiring a bootstrap secret or shared token before issuing the pairing credential, or document the rationale if the endpoint is intentionally open on a trusted network.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/render/http.ts around line 36:
The `pair` handler calls `serverAuth.issuePairingCredential` with no authentication or secret verification, so any caller who can reach the Render service URL can POST to this endpoint and receive a one-time credential that grants authorized client access. The `configuredProvider !== "render"` check only validates server-side configuration and does not authenticate the caller. Consider requiring a bootstrap secret or shared token before issuing the pairing credential, or document the rationale if the endpoint is intentionally open on a trusted network.
What changed
Adds a one-session T3 Code cloud environment powered by Render:
T3 Code requests a standard one-time pairing credential directly from the service. Users do not need to inspect Render logs, copy a Pair URL, or manage a setup code. This URL-only pairing is intended for the private one-off demo described here, not a public multi-tenant deployment.
Verification
Model: GPT-5.6; harness: Codex in T3 Code.