import * as NodeCrypto from "node:crypto";import { AuthRelayReadScope, AuthRelayWriteScope, AuthStandardClientScopes, EnvironmentCloudEndpointUnavailableError, EnvironmentCloudLinkStateResult, EnvironmentCloudRelayConfigResult, EnvironmentHttpApi, EnvironmentHttpBadRequestError, EnvironmentHttpConflictError, EnvironmentHttpInternalServerError, EnvironmentHttpUnauthorizedError,} from "@t3tools/contracts";import { RelayCloudEnvironmentHealthProofPayload, RelayCloudEnvironmentHealthRequest, RelayCloudMintCredentialProofPayload, RelayCloudMintCredentialRequest, RelayEnvironmentHealthResponseProofPayload, type RelayEnvironmentHealthResponse as RelayEnvironmentHealthResponseShape, RelayEnvironmentConfigRequest, RelayEnvironmentLinkChallengeResponse, RelayEnvironmentLinkResponse, RelayEnvironmentMintResponseProofPayload, type RelayEnvironmentMintResponse as RelayEnvironmentMintResponseShape, RelayEnvironmentLinkProof, RelayEnvironmentLinkProofPayload, RelayLinkProofRequest, RelayManagedEndpointOrigin,} from "@t3tools/contracts/relay";import { withRelayClientTracing } from "@t3tools/shared/relayTracing";import { normalizeRelayIssuer, RELAY_HEALTH_REQUEST_TYP, RELAY_HEALTH_RESPONSE_TYP, RELAY_LINK_PROOF_TYP, RELAY_MINT_REQUEST_TYP, RELAY_MINT_RESPONSE_TYP, signRelayJwt, verifyRelayJwt,} from "@t3tools/shared/relayJwt";import { isSecureRelayUrl } from "@t3tools/shared/relayUrl";import * as DateTime from "effect/DateTime";import * as Crypto from "effect/Crypto";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import * as Schema from "effect/Schema";import * as HttpEffect from "effect/unstable/http/HttpEffect";import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";import * as ServerSecretStore from "../auth/ServerSecretStore.ts";import { requireEnvironmentScope } from "../auth/http.ts";import * as ServerEnvironment from "../environment/ServerEnvironment.ts";import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts";import { CLOUD_ENDPOINT_RUNTIME_CONFIG, CLOUD_LINKED_USER_ID, CLOUD_MINT_PUBLIC_KEY, encodeEndpointRuntimeConfigJson, PUBLISH_AGENT_ACTIVITY_SECRET, RELAY_ENVIRONMENT_CREDENTIAL_SECRET, RELAY_ISSUER_SECRET, RELAY_URL_SECRET,} from "./config.ts";import { relayUrlConfig } from "./publicConfig.ts";import { readCliDesiredLinkMode, setCliDesiredCloudLink } from "./CliState.ts";import * as CliTokenManager from "./CliTokenManager.ts";import { getOrCreateEnvironmentKeyPairFromSecretStore } from "./environmentKeys.ts";import { traceRelayRequest } from "./traceRelayRequest.ts"; const CLOUD_MINT_NONCE_PREFIX = "cloud-mint-nonce-";const CLOUD_MINT_JTI_PREFIX = "cloud-mint-jti-";const CLOUD_HEALTH_NONCE_PREFIX = "cloud-health-nonce-";const CLOUD_HEALTH_JTI_PREFIX = "cloud-health-jti-";const CLOUD_PROOF_MAX_LIFETIME_SECONDS = 5 * 60;const CLOUD_PROOF_CLOCK_SKEW_SECONDS = 60;const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);const CLOUD_CREDENTIAL_RESPONSE_HEADERS = { "cache-control": "no-store", pragma: "no-cache",} as const; const appendCloudCredentialResponseHeaders = HttpEffect.appendPreResponseHandler( (_request, response) => Effect.succeed(HttpServerResponse.setHeaders(response, CLOUD_CREDENTIAL_RESPONSE_HEADERS)),); const failEnvironmentCloudInternalError = (message: string) => (cause: unknown): Effect.Effect<never, EnvironmentHttpInternalServerError> => Effect.logError(message, { cause }).pipe( Effect.flatMap(() => Effect.fail(new EnvironmentHttpInternalServerError({ message }))), ); const failCloudCliTokenManagerError = (error: CliTokenManager.CloudCliTokenManagerError) => failEnvironmentCloudInternalError(error.message)(error); const requireRelayUrl = relayUrlConfig.pipe( Effect.mapError( () => new EnvironmentHttpInternalServerError({ message: "T3CODE_RELAY_URL must be configured as a secure absolute HTTPS origin.", }), ),); function bytesToString(bytes: Uint8Array): string { return new TextDecoder().decode(bytes);} function stringToBytes(value: string): Uint8Array { return new TextEncoder().encode(value);} export function consumeCloudReplayGuards(input: { readonly secrets: ServerSecretStore.ServerSecretStore["Service"]; readonly names: ReadonlyArray<string>; readonly value: Uint8Array;}) { return Effect.all( input.names.map((name) => input.secrets.create(name, input.value).pipe( Effect.as(true), Effect.catchIf(ServerSecretStore.isSecretStoreError, (error) => ServerSecretStore.isSecretAlreadyExistsError(error) ? Effect.succeed(false) : Effect.fail(error), ), ), ), { concurrency: input.names.length }, ).pipe(Effect.map((created) => created.every(Boolean)));} function normalizePemForSignedPayload(value: string): string { return value.trim();} function normalizeHostname(hostname: string): string { return hostname .trim() .toLowerCase() .replace(/^\[(.*)\]$/, "$1");} function validateCloudMintPublicKey( publicKey: string,): Effect.Effect<void, EnvironmentHttpBadRequestError> { return Effect.try({ try: () => NodeCrypto.createPublicKey(publicKey.replace(/\\n/g, "\n")), catch: () => new EnvironmentHttpBadRequestError({ message: "Cloud mint public key must be a valid Ed25519 public key.", }), }).pipe( Effect.flatMap((key) => key.asymmetricKeyType === "ed25519" ? Effect.void : Effect.fail( new EnvironmentHttpBadRequestError({ message: "Cloud mint public key must be a valid Ed25519 public key.", }), ), ), );} function validateRelayConfigPayload( payload: RelayEnvironmentConfigRequest,): Effect.Effect<void, EnvironmentHttpBadRequestError> { if (!isSecureRelayUrl(payload.relayUrl)) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Relay URL must be a secure absolute HTTPS URL.", }), ); } if (payload.relayIssuer !== undefined && !isSecureRelayUrl(payload.relayIssuer)) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Relay issuer must be a secure absolute HTTPS URL.", }), ); } if (payload.environmentCredential.trim().length === 0) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Relay environment credential is required.", }), ); } if (payload.cloudUserId.trim().length === 0) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Cloud user id is required.", }), ); } return Effect.void;} function validateLinkedCloudUser(input: { readonly secrets: ServerSecretStore.ServerSecretStore["Service"]; readonly cloudUserId: string;}): Effect.Effect<void, EnvironmentAuth.ServerAuthInternalError | EnvironmentHttpConflictError> { return input.secrets.get(CLOUD_LINKED_USER_ID).pipe( Effect.mapError( (cause) => new EnvironmentAuth.ServerAuthLinkedCloudAccountVerificationError({ cause, }), ), Effect.flatMap((existing) => { if (Option.isNone(existing)) { return Effect.void; } const existingCloudUserId = bytesToString(existing.value); return existingCloudUserId === input.cloudUserId ? Effect.void : Effect.fail( new EnvironmentHttpConflictError({ message: "This environment is already linked to a different cloud account. Unlink it before switching accounts.", }), ); }), );} function readInstalledCloudUserId( secrets: ServerSecretStore.ServerSecretStore["Service"],): Effect.Effect<string, EnvironmentAuth.ServerAuthInternalError> { return secrets.get(CLOUD_LINKED_USER_ID).pipe( Effect.mapError( (cause) => new EnvironmentAuth.ServerAuthLinkedCloudAccountReadError({ cause, }), ), Effect.flatMap((bytes) => Option.isSome(bytes) ? Effect.succeed(bytesToString(bytes.value)) : Effect.fail(new EnvironmentAuth.ServerAuthLinkedCloudAccountMissingError({})), ), );} function isLoopbackHostname(hostname: string): boolean { return LOOPBACK_HOSTNAMES.has(normalizeHostname(hostname));} function firstForwardedHeaderValue(value: string | undefined): string | undefined { const first = value?.split(",")[0]?.trim(); return first && first.length > 0 ? first : undefined;} function requestAbsoluteUrl(request: HttpServerRequest.HttpServerRequest): string | null { try { return new URL(request.originalUrl).href; } catch { const host = firstForwardedHeaderValue(request.headers.host) ?? "127.0.0.1"; try { return new URL(request.originalUrl, `http://${host}`).href; } catch { return null; } }} function hasForwardedAuthorityHeaders(request: HttpServerRequest.HttpServerRequest): boolean { return ( firstForwardedHeaderValue(request.headers["x-forwarded-host"]) !== undefined || firstForwardedHeaderValue(request.headers["x-forwarded-proto"]) !== undefined );} function endpointRequestPort(url: URL): number { return Number(url.port || (url.protocol === "https:" ? 443 : 80));} function isAllowedEndpointOrigin(input: { readonly origin: RelayManagedEndpointOrigin; readonly requestUrl: string;}): boolean { if (!isLoopbackHostname(input.origin.localHttpHost)) { return false; } const url = new URL(input.requestUrl); if (!isLoopbackHostname(url.hostname)) { return false; } return input.origin.localHttpPort === endpointRequestPort(url);} // A managed (Cloudflare tunnel) endpoint is provisioned by the relay and must// point at a loopback origin. A manual endpoint is reached out of band (e.g.// Tailscale) or not advertised at all for publish-only links, so it is not// tied to the managed-tunnel scope.export function isSupportedLinkProviderKind(request: RelayLinkProofRequest): boolean { return ( request.endpoint.providerKind === "cloudflare_tunnel" || request.endpoint.providerKind === "manual" );} export function linkProofScopes( request: RelayLinkProofRequest,): RelayEnvironmentLinkProofPayload["scopes"] { return request.endpoint.providerKind === "cloudflare_tunnel" ? ["agent_activity_notifications", "managed_tunnels"] : ["agent_activity_notifications"];} function hasExactScope(input: { readonly scopes: ReadonlyArray<string>; readonly expected: string;}): boolean { return input.scopes.length === 1 && input.scopes[0] === input.expected;} function hasBoundedCloudProofLifetime(input: { readonly iat: number; readonly exp: number; readonly nowSeconds: number;}): boolean { return ( input.exp > input.iat && input.exp - input.iat <= CLOUD_PROOF_MAX_LIFETIME_SECONDS && input.iat <= input.nowSeconds + CLOUD_PROOF_CLOCK_SKEW_SECONDS );} const decodeCloudHealthProof = Schema.decodeUnknownEffect(RelayCloudEnvironmentHealthProofPayload);const decodeCloudMintProof = Schema.decodeUnknownEffect(RelayCloudMintCredentialProofPayload); interface CloudHttpDependencies { readonly secrets: ServerSecretStore.ServerSecretStore["Service"]; readonly environment: ServerEnvironment.ServerEnvironment["Service"]; readonly endpointRuntime: ManagedEndpointRuntime.CloudManagedEndpointRuntime["Service"]; readonly environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"]; readonly cliTokenManager: CliTokenManager.CloudCliTokenManager["Service"]; readonly httpClient: HttpClient.HttpClient;} const cloudHttpDependencies = Effect.gen(function* () { return { secrets: yield* ServerSecretStore.ServerSecretStore, environment: yield* ServerEnvironment.ServerEnvironment, endpointRuntime: yield* ManagedEndpointRuntime.CloudManagedEndpointRuntime, environmentAuth: yield* EnvironmentAuth.EnvironmentAuth, cliTokenManager: yield* CliTokenManager.CloudCliTokenManager, httpClient: yield* HttpClient.HttpClient, } satisfies CloudHttpDependencies;}); const makeCloudLinkProof = Effect.fn("environment.cloud.makeLinkProof")(function* ( dependencies: CloudHttpDependencies, request: RelayLinkProofRequest, requestUrl: string,) { const keyPair = yield* getOrCreateEnvironmentKeyPairFromSecretStore(dependencies.secrets); if ( !isSupportedLinkProviderKind(request) || !isAllowedEndpointOrigin({ origin: request.origin, requestUrl, }) ) { return yield* new EnvironmentHttpBadRequestError({ message: "Invalid managed endpoint origin.", }); } const now = yield* DateTime.now; const expiresAt = DateTime.add(now, { minutes: 5 }); const nowSeconds = Math.floor(now.epochMilliseconds / 1_000); const descriptor = yield* dependencies.environment.getDescriptor; const payload = { iss: `t3-env:${descriptor.environmentId}`, aud: normalizeRelayIssuer(request.relayIssuer), sub: descriptor.environmentId, jti: yield* Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.randomUUIDv4)), iat: nowSeconds, exp: Math.floor(expiresAt.epochMilliseconds / 1_000), challenge: request.challenge, descriptor, environmentId: descriptor.environmentId, environmentPublicKey: normalizePemForSignedPayload(keyPair.publicKey), endpoint: request.endpoint, origin: request.origin, scopes: linkProofScopes(request), } satisfies RelayEnvironmentLinkProofPayload; return yield* signRelayJwt({ privateKey: keyPair.privateKey, typ: RELAY_LINK_PROOF_TYP, payload, }).pipe( Effect.mapError( (cause) => new EnvironmentAuth.ServerAuthCloudLinkJwtSigningError({ cause, }), ), );}); const cloudLinkProofHandler = Effect.fn("environment.cloud.linkProof")( function* (dependencies: CloudHttpDependencies, request: RelayLinkProofRequest) { yield* requireEnvironmentScope(AuthRelayWriteScope); const httpRequest = yield* HttpServerRequest.HttpServerRequest; const requestUrl = requestAbsoluteUrl(httpRequest); if (requestUrl === null || hasForwardedAuthorityHeaders(httpRequest)) { return yield* new EnvironmentHttpBadRequestError({ message: "Invalid managed endpoint origin.", }); } const proof = yield* makeCloudLinkProof(dependencies, request, requestUrl); yield* appendCloudCredentialResponseHeaders; return proof satisfies RelayEnvironmentLinkProof; }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentCloudInternalError(error.message)(error), ), Effect.catchIf( ServerSecretStore.isSecretStoreError, failEnvironmentCloudInternalError("Could not generate environment link proof."), ), Effect.catchTag( "PlatformError", failEnvironmentCloudInternalError("Could not generate environment link proof."), ),); const applyCloudRelayConfig = Effect.fn("environment.cloud.applyRelayConfig")(function* ( dependencies: CloudHttpDependencies, payload: RelayEnvironmentConfigRequest,) { yield* validateRelayConfigPayload(payload); yield* validateLinkedCloudUser({ secrets: dependencies.secrets, cloudUserId: payload.cloudUserId, }); yield* validateCloudMintPublicKey(payload.cloudMintPublicKey); const endpointRuntimeStatus = yield* dependencies.endpointRuntime.applyConfig( payload.endpointRuntime, ); const ok = endpointRuntimeStatus.status === "disabled" || endpointRuntimeStatus.status === "running"; if (!ok) { return yield* new EnvironmentCloudEndpointUnavailableError({ message: "Managed endpoint runtime could not be started.", endpointRuntimeStatus, }); } yield* dependencies.secrets.set(RELAY_URL_SECRET, stringToBytes(payload.relayUrl)); yield* dependencies.secrets.set( RELAY_ISSUER_SECRET, stringToBytes(payload.relayIssuer ?? payload.relayUrl), ); yield* dependencies.secrets.set(CLOUD_LINKED_USER_ID, stringToBytes(payload.cloudUserId)); yield* dependencies.secrets.set( RELAY_ENVIRONMENT_CREDENTIAL_SECRET, stringToBytes(payload.environmentCredential), ); yield* dependencies.secrets.set(CLOUD_MINT_PUBLIC_KEY, stringToBytes(payload.cloudMintPublicKey)); if (payload.endpointRuntime) { const endpointRuntimeJson = yield* encodeEndpointRuntimeConfigJson(payload.endpointRuntime); yield* dependencies.secrets.set( CLOUD_ENDPOINT_RUNTIME_CONFIG, stringToBytes(endpointRuntimeJson), ); } else { yield* dependencies.secrets.remove(CLOUD_ENDPOINT_RUNTIME_CONFIG); } return { ok, endpointRuntimeStatus } satisfies EnvironmentCloudRelayConfigResult;}); const cloudRelayConfigHandler = Effect.fn("environment.cloud.relayConfig")( function* (dependencies: CloudHttpDependencies, payload: RelayEnvironmentConfigRequest) { yield* requireEnvironmentScope(AuthRelayWriteScope); return yield* applyCloudRelayConfig(dependencies, payload); }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentCloudInternalError(error.message)(error), ), Effect.catchIf( ServerSecretStore.isSecretStoreError, failEnvironmentCloudInternalError("Could not persist environment relay configuration."), ), Effect.catchTag( "SchemaError", failEnvironmentCloudInternalError("Could not persist environment relay configuration."), ),Lines 1–500 of 963. Load more
import * as NodeCrypto from "node:crypto"; import { AuthRelayReadScope, AuthRelayWriteScope, AuthStandardClientScopes, EnvironmentCloudEndpointUnavailableError, EnvironmentCloudLinkStateResult, EnvironmentCloudRelayConfigResult, EnvironmentHttpApi, EnvironmentHttpBadRequestError, EnvironmentHttpConflictError, EnvironmentHttpInternalServerError, EnvironmentHttpUnauthorizedError, } from "@t3tools/contracts"; import { RelayCloudEnvironmentHealthProofPayload, RelayCloudEnvironmentHealthRequest, RelayCloudMintCredentialProofPayload, RelayCloudMintCredentialRequest, RelayEnvironmentHealthResponseProofPayload, type RelayEnvironmentHealthResponse as RelayEnvironmentHealthResponseShape, RelayEnvironmentConfigRequest, RelayEnvironmentLinkChallengeResponse, RelayEnvironmentLinkResponse, RelayEnvironmentMintResponseProofPayload, type RelayEnvironmentMintResponse as RelayEnvironmentMintResponseShape, RelayEnvironmentLinkProof, RelayEnvironmentLinkProofPayload, RelayLinkProofRequest, RelayManagedEndpointOrigin, } from "@t3tools/contracts/relay"; import { withRelayClientTracing } from "@t3tools/shared/relayTracing"; import { normalizeRelayIssuer, RELAY_HEALTH_REQUEST_TYP, RELAY_HEALTH_RESPONSE_TYP, RELAY_LINK_PROOF_TYP, RELAY_MINT_REQUEST_TYP, RELAY_MINT_RESPONSE_TYP, signRelayJwt, verifyRelayJwt, } from "@t3tools/shared/relayJwt"; import { isSecureRelayUrl } from "@t3tools/shared/relayUrl"; import * as DateTime from "effect/DateTime"; import * as Crypto from "effect/Crypto"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as HttpEffect from "effect/unstable/http/HttpEffect"; import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"; import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"; import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts"; import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; import { requireEnvironmentScope } from "../auth/http.ts"; import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts"; import { CLOUD_ENDPOINT_RUNTIME_CONFIG, CLOUD_LINKED_USER_ID, CLOUD_MINT_PUBLIC_KEY, encodeEndpointRuntimeConfigJson, PUBLISH_AGENT_ACTIVITY_SECRET, RELAY_ENVIRONMENT_CREDENTIAL_SECRET, RELAY_ISSUER_SECRET, RELAY_URL_SECRET, } from "./config.ts"; import { relayUrlConfig } from "./publicConfig.ts"; import { readCliDesiredLinkMode, setCliDesiredCloudLink } from "./CliState.ts"; import * as CliTokenManager from "./CliTokenManager.ts"; import { getOrCreateEnvironmentKeyPairFromSecretStore } from "./environmentKeys.ts"; import { traceRelayRequest } from "./traceRelayRequest.ts"; const CLOUD_MINT_NONCE_PREFIX = "cloud-mint-nonce-"; const CLOUD_MINT_JTI_PREFIX = "cloud-mint-jti-"; const CLOUD_HEALTH_NONCE_PREFIX = "cloud-health-nonce-"; const CLOUD_HEALTH_JTI_PREFIX = "cloud-health-jti-"; const CLOUD_PROOF_MAX_LIFETIME_SECONDS = 5 * 60; const CLOUD_PROOF_CLOCK_SKEW_SECONDS = 60; const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]); const CLOUD_CREDENTIAL_RESPONSE_HEADERS = { "cache-control": "no-store", pragma: "no-cache", } as const; const appendCloudCredentialResponseHeaders = HttpEffect.appendPreResponseHandler( (_request, response) => Effect.succeed(HttpServerResponse.setHeaders(response, CLOUD_CREDENTIAL_RESPONSE_HEADERS)), ); const failEnvironmentCloudInternalError = (message: string) => (cause: unknown): Effect.Effect<never, EnvironmentHttpInternalServerError> => Effect.logError(message, { cause }).pipe( Effect.flatMap(() => Effect.fail(new EnvironmentHttpInternalServerError({ message }))), ); const failCloudCliTokenManagerError = (error: CliTokenManager.CloudCliTokenManagerError) => failEnvironmentCloudInternalError(error.message)(error); const requireRelayUrl = relayUrlConfig.pipe( Effect.mapError( () => new EnvironmentHttpInternalServerError({ message: "T3CODE_RELAY_URL must be configured as a secure absolute HTTPS origin.", }), ), ); function bytesToString(bytes: Uint8Array): string { return new TextDecoder().decode(bytes); } function stringToBytes(value: string): Uint8Array { return new TextEncoder().encode(value); } export function consumeCloudReplayGuards(input: { readonly secrets: ServerSecretStore.ServerSecretStore["Service"]; readonly names: ReadonlyArray<string>; readonly value: Uint8Array; }) { return Effect.all( input.names.map((name) => input.secrets.create(name, input.value).pipe( Effect.as(true), Effect.catchIf(ServerSecretStore.isSecretStoreError, (error) => ServerSecretStore.isSecretAlreadyExistsError(error) ? Effect.succeed(false) : Effect.fail(error), ), ), ), { concurrency: input.names.length }, ).pipe(Effect.map((created) => created.every(Boolean))); } function normalizePemForSignedPayload(value: string): string { return value.trim(); } function normalizeHostname(hostname: string): string { return hostname .trim() .toLowerCase() .replace(/^\[(.*)\]$/, "$1"); } function validateCloudMintPublicKey( publicKey: string, ): Effect.Effect<void, EnvironmentHttpBadRequestError> { return Effect.try({ try: () => NodeCrypto.createPublicKey(publicKey.replace(/\\n/g, "\n")), catch: () => new EnvironmentHttpBadRequestError({ message: "Cloud mint public key must be a valid Ed25519 public key.", }), }).pipe( Effect.flatMap((key) => key.asymmetricKeyType === "ed25519" ? Effect.void : Effect.fail( new EnvironmentHttpBadRequestError({ message: "Cloud mint public key must be a valid Ed25519 public key.", }), ), ), ); } function validateRelayConfigPayload( payload: RelayEnvironmentConfigRequest, ): Effect.Effect<void, EnvironmentHttpBadRequestError> { if (!isSecureRelayUrl(payload.relayUrl)) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Relay URL must be a secure absolute HTTPS URL.", }), ); } if (payload.relayIssuer !== undefined && !isSecureRelayUrl(payload.relayIssuer)) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Relay issuer must be a secure absolute HTTPS URL.", }), ); } if (payload.environmentCredential.trim().length === 0) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Relay environment credential is required.", }), ); } if (payload.cloudUserId.trim().length === 0) { return Effect.fail( new EnvironmentHttpBadRequestError({ message: "Cloud user id is required.", }), ); } return Effect.void; } function validateLinkedCloudUser(input: { readonly secrets: ServerSecretStore.ServerSecretStore["Service"]; readonly cloudUserId: string; }): Effect.Effect<void, EnvironmentAuth.ServerAuthInternalError | EnvironmentHttpConflictError> { return input.secrets.get(CLOUD_LINKED_USER_ID).pipe( Effect.mapError( (cause) => new EnvironmentAuth.ServerAuthLinkedCloudAccountVerificationError({ cause, }), ), Effect.flatMap((existing) => { if (Option.isNone(existing)) { return Effect.void; } const existingCloudUserId = bytesToString(existing.value); return existingCloudUserId === input.cloudUserId ? Effect.void : Effect.fail( new EnvironmentHttpConflictError({ message: "This environment is already linked to a different cloud account. Unlink it before switching accounts.", }), ); }), ); } function readInstalledCloudUserId( secrets: ServerSecretStore.ServerSecretStore["Service"], ): Effect.Effect<string, EnvironmentAuth.ServerAuthInternalError> { return secrets.get(CLOUD_LINKED_USER_ID).pipe( Effect.mapError( (cause) => new EnvironmentAuth.ServerAuthLinkedCloudAccountReadError({ cause, }), ), Effect.flatMap((bytes) => Option.isSome(bytes) ? Effect.succeed(bytesToString(bytes.value)) : Effect.fail(new EnvironmentAuth.ServerAuthLinkedCloudAccountMissingError({})), ), ); } function isLoopbackHostname(hostname: string): boolean { return LOOPBACK_HOSTNAMES.has(normalizeHostname(hostname)); } function firstForwardedHeaderValue(value: string | undefined): string | undefined { const first = value?.split(",")[0]?.trim(); return first && first.length > 0 ? first : undefined; } function requestAbsoluteUrl(request: HttpServerRequest.HttpServerRequest): string | null { try { return new URL(request.originalUrl).href; } catch { const host = firstForwardedHeaderValue(request.headers.host) ?? "127.0.0.1"; try { return new URL(request.originalUrl, `http://${host}`).href; } catch { return null; } } } function hasForwardedAuthorityHeaders(request: HttpServerRequest.HttpServerRequest): boolean { return ( firstForwardedHeaderValue(request.headers["x-forwarded-host"]) !== undefined || firstForwardedHeaderValue(request.headers["x-forwarded-proto"]) !== undefined ); } function endpointRequestPort(url: URL): number { return Number(url.port || (url.protocol === "https:" ? 443 : 80)); } function isAllowedEndpointOrigin(input: { readonly origin: RelayManagedEndpointOrigin; readonly requestUrl: string; }): boolean { if (!isLoopbackHostname(input.origin.localHttpHost)) { return false; } const url = new URL(input.requestUrl); if (!isLoopbackHostname(url.hostname)) { return false; } return input.origin.localHttpPort === endpointRequestPort(url); } // A managed (Cloudflare tunnel) endpoint is provisioned by the relay and must // point at a loopback origin. A manual endpoint is reached out of band (e.g. // Tailscale) or not advertised at all for publish-only links, so it is not // tied to the managed-tunnel scope. export function isSupportedLinkProviderKind(request: RelayLinkProofRequest): boolean { return ( request.endpoint.providerKind === "cloudflare_tunnel" || request.endpoint.providerKind === "manual" ); } export function linkProofScopes( request: RelayLinkProofRequest, ): RelayEnvironmentLinkProofPayload["scopes"] { return request.endpoint.providerKind === "cloudflare_tunnel" ? ["agent_activity_notifications", "managed_tunnels"] : ["agent_activity_notifications"]; } function hasExactScope(input: { readonly scopes: ReadonlyArray<string>; readonly expected: string; }): boolean { return input.scopes.length === 1 && input.scopes[0] === input.expected; } function hasBoundedCloudProofLifetime(input: { readonly iat: number; readonly exp: number; readonly nowSeconds: number; }): boolean { return ( input.exp > input.iat && input.exp - input.iat <= CLOUD_PROOF_MAX_LIFETIME_SECONDS && input.iat <= input.nowSeconds + CLOUD_PROOF_CLOCK_SKEW_SECONDS ); } const decodeCloudHealthProof = Schema.decodeUnknownEffect(RelayCloudEnvironmentHealthProofPayload); const decodeCloudMintProof = Schema.decodeUnknownEffect(RelayCloudMintCredentialProofPayload); interface CloudHttpDependencies { readonly secrets: ServerSecretStore.ServerSecretStore["Service"]; readonly environment: ServerEnvironment.ServerEnvironment["Service"]; readonly endpointRuntime: ManagedEndpointRuntime.CloudManagedEndpointRuntime["Service"]; readonly environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"]; readonly cliTokenManager: CliTokenManager.CloudCliTokenManager["Service"]; readonly httpClient: HttpClient.HttpClient; } const cloudHttpDependencies = Effect.gen(function* () { return { secrets: yield* ServerSecretStore.ServerSecretStore, environment: yield* ServerEnvironment.ServerEnvironment, endpointRuntime: yield* ManagedEndpointRuntime.CloudManagedEndpointRuntime, environmentAuth: yield* EnvironmentAuth.EnvironmentAuth, cliTokenManager: yield* CliTokenManager.CloudCliTokenManager, httpClient: yield* HttpClient.HttpClient, } satisfies CloudHttpDependencies; }); const makeCloudLinkProof = Effect.fn("environment.cloud.makeLinkProof")(function* ( dependencies: CloudHttpDependencies, request: RelayLinkProofRequest, requestUrl: string, ) { const keyPair = yield* getOrCreateEnvironmentKeyPairFromSecretStore(dependencies.secrets); if ( !isSupportedLinkProviderKind(request) || !isAllowedEndpointOrigin({ origin: request.origin, requestUrl, }) ) { return yield* new EnvironmentHttpBadRequestError({ message: "Invalid managed endpoint origin.", }); } const now = yield* DateTime.now; const expiresAt = DateTime.add(now, { minutes: 5 }); const nowSeconds = Math.floor(now.epochMilliseconds / 1_000); const descriptor = yield* dependencies.environment.getDescriptor; const payload = { iss: `t3-env:${descriptor.environmentId}`, aud: normalizeRelayIssuer(request.relayIssuer), sub: descriptor.environmentId, jti: yield* Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.randomUUIDv4)), iat: nowSeconds, exp: Math.floor(expiresAt.epochMilliseconds / 1_000), challenge: request.challenge, descriptor, environmentId: descriptor.environmentId, environmentPublicKey: normalizePemForSignedPayload(keyPair.publicKey), endpoint: request.endpoint, origin: request.origin, scopes: linkProofScopes(request), } satisfies RelayEnvironmentLinkProofPayload; return yield* signRelayJwt({ privateKey: keyPair.privateKey, typ: RELAY_LINK_PROOF_TYP, payload, }).pipe( Effect.mapError( (cause) => new EnvironmentAuth.ServerAuthCloudLinkJwtSigningError({ cause, }), ), ); }); const cloudLinkProofHandler = Effect.fn("environment.cloud.linkProof")( function* (dependencies: CloudHttpDependencies, request: RelayLinkProofRequest) { yield* requireEnvironmentScope(AuthRelayWriteScope); const httpRequest = yield* HttpServerRequest.HttpServerRequest; const requestUrl = requestAbsoluteUrl(httpRequest); if (requestUrl === null || hasForwardedAuthorityHeaders(httpRequest)) { return yield* new EnvironmentHttpBadRequestError({ message: "Invalid managed endpoint origin.", }); } const proof = yield* makeCloudLinkProof(dependencies, request, requestUrl); yield* appendCloudCredentialResponseHeaders; return proof satisfies RelayEnvironmentLinkProof; }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentCloudInternalError(error.message)(error), ), Effect.catchIf( ServerSecretStore.isSecretStoreError, failEnvironmentCloudInternalError("Could not generate environment link proof."), ), Effect.catchTag( "PlatformError", failEnvironmentCloudInternalError("Could not generate environment link proof."), ), ); const applyCloudRelayConfig = Effect.fn("environment.cloud.applyRelayConfig")(function* ( dependencies: CloudHttpDependencies, payload: RelayEnvironmentConfigRequest, ) { yield* validateRelayConfigPayload(payload); yield* validateLinkedCloudUser({ secrets: dependencies.secrets, cloudUserId: payload.cloudUserId, }); yield* validateCloudMintPublicKey(payload.cloudMintPublicKey); const endpointRuntimeStatus = yield* dependencies.endpointRuntime.applyConfig( payload.endpointRuntime, ); const ok = endpointRuntimeStatus.status === "disabled" || endpointRuntimeStatus.status === "running"; if (!ok) { return yield* new EnvironmentCloudEndpointUnavailableError({ message: "Managed endpoint runtime could not be started.", endpointRuntimeStatus, }); } yield* dependencies.secrets.set(RELAY_URL_SECRET, stringToBytes(payload.relayUrl)); yield* dependencies.secrets.set( RELAY_ISSUER_SECRET, stringToBytes(payload.relayIssuer ?? payload.relayUrl), ); yield* dependencies.secrets.set(CLOUD_LINKED_USER_ID, stringToBytes(payload.cloudUserId)); yield* dependencies.secrets.set( RELAY_ENVIRONMENT_CREDENTIAL_SECRET, stringToBytes(payload.environmentCredential), ); yield* dependencies.secrets.set(CLOUD_MINT_PUBLIC_KEY, stringToBytes(payload.cloudMintPublicKey)); if (payload.endpointRuntime) { const endpointRuntimeJson = yield* encodeEndpointRuntimeConfigJson(payload.endpointRuntime); yield* dependencies.secrets.set( CLOUD_ENDPOINT_RUNTIME_CONFIG, stringToBytes(endpointRuntimeJson), ); } else { yield* dependencies.secrets.remove(CLOUD_ENDPOINT_RUNTIME_CONFIG); } return { ok, endpointRuntimeStatus } satisfies EnvironmentCloudRelayConfigResult; }); const cloudRelayConfigHandler = Effect.fn("environment.cloud.relayConfig")( function* (dependencies: CloudHttpDependencies, payload: RelayEnvironmentConfigRequest) { yield* requireEnvironmentScope(AuthRelayWriteScope); return yield* applyCloudRelayConfig(dependencies, payload); }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentCloudInternalError(error.message)(error), ), Effect.catchIf( ServerSecretStore.isSecretStoreError, failEnvironmentCloudInternalError("Could not persist environment relay configuration."), ), Effect.catchTag( "SchemaError", failEnvironmentCloudInternalError("Could not persist environment relay configuration."), ),