Skip to content

feat(infra): add Render cloud environment - #5378

Draft
shivamhwp wants to merge 10 commits into
mainfrom
feat/render-cloud-environment
Draft

feat(infra): add Render cloud environment#5378
shivamhwp wants to merge 10 commits into
mainfrom
feat/render-cloud-environment

Conversation

@shivamhwp

@shivamhwp shivamhwp commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

What changed

Adds a one-session T3 Code cloud environment powered by Render:

  • a free Render Blueprint with Codex, Claude Code, Git, and GitHub CLI
  • a dedicated Cloud environments screen using the Render mark
  • OpenAI API key and ChatGPT subscription setup tabs
  • in-app pairing from the Render service URL
  • direct cloning of public GitHub repositories into the cloud workspace

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

  • contracts, server, and web typechecks
  • server route suite: 121 tests passed
  • focused Render web tests: 9 tests passed
  • targeted lint and formatting
  • exact Render image build command: vp run --filter t3 build
  • full PR diff secret scan

Model: GPT-5.6; harness: Codex in T3 Code.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

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: 2dee76cc-656b-4b83-b44d-81fc29b36393

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.

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Aug 5, 2026
@shivamhwp shivamhwp changed the title feat(infra): add Render cloud environment demo feat(infra): add Render cloud environment Aug 5, 2026
@shivamhwp
shivamhwp force-pushed the feat/render-cloud-environment branch from d2edb64 to 35ebbc4 Compare August 5, 2026 02:05
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 5, 2026
if (!directoryName) {
return null;
}
const workspaceRoot = input.workspaceRoot.trim().replace(/[\\/]+$/, "");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 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.

Suggested change
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.

Comment on lines +25 to +26
const RENDER_DEPLOY_URL =
"https://dashboard.render.com/blueprint/new?repo=https%3A%2F%2Fgithub.com%2Fpingdotgg%2Ft3code&branch=feat%2Frender-cloud-environment";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 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.

Suggested change
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.

Comment thread apps/web/src/components/settings/CloudEnvironmentsSettings.tsx
Comment thread apps/web/src/components/settings/CloudEnvironmentsSettings.tsx Outdated
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 5, 2026
readonly onDisconnect: (environmentId: EnvironmentId) => void;
}) {
const isConnected = environment.connection.phase === "connected";
const isBusy = busyEnvironmentIds.has(environment.environmentId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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, {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 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.

Comment thread apps/web/src/cloud/renderBootstrap.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +61 to +68
const issued = yield* serverAuth.issuePairingCredential({ label: "Render setup" }).pipe(
Effect.mapError(
() =>
new EnvironmentHttpInternalServerError({
message: "Could not create a Render pairing credential.",
}),
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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

@shivamhwp shivamhwp changed the title feat(infra): add Render cloud environment feat(infra): add Render cloud environment Aug 5, 2026
const environmentLabel =
hostEnvironment.T3CODE_ENVIRONMENT_LABEL?.trim() || "Render cloud environment";

return handlers.handle(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant