Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/internals/t3-connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ The version comes from running `cloudflared version`, not from the folder name.
Connectors from before `--no-autoupdate` replaced themselves in place, so a
managed folder can hold another release. Such a binary still runs as a fallback
while the pin installs. Every binary needs `CLOUDFLARED_MIN_VERSION`, the oldest
release that accepts every flag the connector is started with. Raise it whenever
a new flag is added.
release that accepts every flag the connector is started with and rejects the
compromised eMudhra intermediates. No binary below it runs, even as a fallback,
so a host without an eligible one stays off the relay until it has one. Raise it
whenever a new flag or security fix needs it.

## OAuth traps

Expand Down
23 changes: 13 additions & 10 deletions packages/shared/src/relayClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ describe("RelayClient", () => {
),
);

it.effect.skipIf(windowsHost)("skips a PATH binary older than the minimum version", () => {
it.effect.skipIf(windowsHost)("skips binaries older than the minimum version", () => {
const versions: Record<string, string> = {};
return run(
(baseDir) =>
Expand All @@ -506,7 +506,10 @@ describe("RelayClient", () => {
const newPath = `${baseDir}/new/cloudflared`;
yield* writeExecutable(oldPath);
yield* writeExecutable(newPath);
versions[oldPath] = "2023.8.2";
const managed = managedPathFor(baseDir, "2026.10.0");
yield* writeExecutable(managed);
versions[managed] = "2026.10.0";
versions[oldPath] = "2026.10.2";
versions[newPath] = RelayClient.CLOUDFLARED_MIN_VERSION;
const manager = yield* RelayClient.makeCloudflaredRelayClient({ baseDir });
const env = { PATH: `${baseDir}/old:${baseDir}/new` };
Expand Down Expand Up @@ -536,7 +539,7 @@ describe("RelayClient", () => {
Effect.gen(function* () {
const overridePath = `${baseDir}/override/cloudflared`;
yield* writeExecutable(overridePath);
versions[overridePath] = "2025.6.0";
versions[overridePath] = "2026.10.2";
const manager = yield* RelayClient.makeCloudflaredRelayClient({ baseDir });
const env = { PATH: "", T3CODE_CLOUDFLARED_PATH: overridePath };
const withEnv = Effect.provideService(
Expand Down Expand Up @@ -569,9 +572,9 @@ describe("RelayClient", () => {
const newerServer = managedPathFor(baseDir, "2099.1.0");
versions[newerServer] = "2099.1.0";
yield* writeExecutable(newerServer);
versions[pathBinary] = "2026.9.3";
versions[pathBinary] = "2026.10.3";
versions[older] = "2025.9.0";
versions[newer] = "2026.1.0";
versions[newer] = "2026.10.3";
yield* writeExecutable(pathBinary);
yield* writeExecutable(older);
yield* writeExecutable(newer);
Expand All @@ -584,7 +587,7 @@ describe("RelayClient", () => {
Effect.provideService(HostProcess.Environment, { PATH: `${baseDir}/bin` }),
);

expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.1.0" });
expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.10.3" });

yield* writeExecutable(pinned);
expect(yield* resolveWith).toMatchObject({
Expand All @@ -599,11 +602,11 @@ describe("RelayClient", () => {
).toEqual(["2026.1.0", RelayClient.CLOUDFLARED_VERSION, "2099.1.0"]);

yield* fileSystem.remove(pinned);
expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.1.0" });
expect(yield* resolveWith).toMatchObject({ source: "managed", version: "2026.10.3" });
yield* fileSystem.remove(`${baseDir}/tools/cloudflared/2026.1.0`, {
recursive: true,
});
expect(yield* resolveWith).toMatchObject({ source: "path", version: "2026.9.3" });
expect(yield* resolveWith).toMatchObject({ source: "path", version: "2026.10.3" });
}),
{ versions },
);
Expand All @@ -618,15 +621,15 @@ describe("RelayClient", () => {
(baseDir) =>
Effect.gen(function* () {
const pinned = managedPathFor(baseDir, RelayClient.CLOUDFLARED_VERSION);
versions[pinned] = "2026.9.3";
versions[pinned] = "2026.10.3";
yield* writeExecutable(pinned);
const manager = yield* RelayClient.makeCloudflaredRelayClient({ baseDir });
const resolved = yield* manager.resolve;
expect(resolved).toEqual({
status: "available",
executablePath: pinned,
source: "managed",
version: "2026.9.3",
version: "2026.10.3",
});
expect(
resolved.status === "available" && RelayClient.isPinnedManagedRelayClient(resolved),
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/relayClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { isWindowsRenameLock } from "./atomicWrite.ts";
import * as HostProcess from "./HostProcess.ts";

export const CLOUDFLARED_VERSION = "2026.10.4";
// Minimum for --output support; older connectors exit on that flag.
export const CLOUDFLARED_MIN_VERSION = "2025.6.1";
// Minimum for --output support and the eMudhra fix, which 2026.10.2 macOS builds lack.
export const CLOUDFLARED_MIN_VERSION = "2026.10.3";
const CLOUDFLARED_PATH_ENV_NAME = "T3CODE_CLOUDFLARED_PATH";

const CLOUDFLARED_DOWNLOAD_TIMEOUT = "10 minutes";
Expand Down
Loading