[!NOTE] Sign in to T3 Connect from the app under Settings > Connections.
The relay is the hosted control plane for T3 Connect. It helps clients discover and connect to remote environments, manages the cloud-side records needed for those connections, and delivers optional mobile notifications and Live Activities.
The relay is intentionally not in the hot path for normal T3 Code traffic. After a client connects, regular API and WebSocket traffic goes directly between that client and the selected environment. See the T3 Connect architecture note for the larger system design.
The relay currently owns:
The environment server and relay have separate credentials and trust boundaries. Read Environment Authentication Profile before changing token, credential, or authorization behavior.
alchemy.run.ts defines the deployed Alchemy stack.src/worker.ts wires Cloudflare bindings, runtime layers, queues, and HTTP APIs.src/http/Api.ts contains the relay HTTP handlers and authentication
boundaries.src/environments contains environment linking, credentials, endpoint
provisioning, and connection flows.src/agentActivity contains mobile device registration, activity state,
APNs and FCM delivery, and queue processing.src/auth contains relay token and DPoP proof handling.src/persistence/schema.ts defines persisted relay state. Keep
schema and migration changes together.Shared request and response schemas live in
packages/contracts/src/relay.ts. Shared client-side relay
calls live in
packages/client-runtime/src/relay/managedRelay.ts.
Install dependencies from the repository root, then run relay-focused checks from this directory:
| 1 | vp install |
| 2 | cd infra/relay |
| 3 | vp test run |
| 4 | vp run typecheck |
To run a smaller test set while iterating:
| 1 | vp test run src/environments/EnvironmentLinker.test.ts |
Before considering a change complete, run the repository-wide checks from the root:
| 1 | vp check |
| 2 | vp run typecheck |
Backend changes should include tests. Prefer testing the real business logic with external dependencies represented at their boundary rather than mocking internal behavior.
The relay deploys with the Alchemy CLI (vp run --filter t3code-relay deploy is alchemy deploy
in this directory):
| 1 | vp run --filter t3code-relay deploy |
The stack provisions the Cloudflare Worker and queues, managed endpoint resources, database
connectivity, and relay tracing resources. Copy infra/relay/.env.example to
infra/relay/.env and fill in the deployment-specific values before deploying. Alchemy loads that
file from the relay directory. Runtime secrets include Clerk, APNs, and optional FCM credentials. Set
APNS_ENABLED=false for an Android-only development deployment without Apple credentials. Production adopts
the configured API and tunnel DNS zones as retained Cloudflare resources. Personal stages reference
the production-owned zones.
The prod Alchemy stage owns the retained PlanetScale database and is the shared hosted relay for
stable and nightly clients. Every other stage references that database and provisions an isolated
PlanetScale branch and runtime role for local development, so deploy prod before creating
developer stages:
| 1 | vp run --filter t3code-relay deploy -- --stage prod |
| 2 | vp run --filter t3code-relay deploy -- --env-file .env.local |
Alchemy defaults personal deployments to the dev_$USER stage. Relay custom domains apply the same
DNS-safe sanitization as Alchemy physical resource names, so prod uses
relay.<RELAY_API_ZONE_NAME> and dev_julius uses
relay-dev-julius.<RELAY_API_ZONE_NAME>. Managed environment endpoints are provisioned below
RELAY_TUNNEL_ZONE_NAME, which may be a different Cloudflare zone. Production tunnel hostnames use
prod-<digest>.<RELAY_TUNNEL_ZONE_NAME>; personal stages use
<stage>-<digest>.<RELAY_TUNNEL_ZONE_NAME>. RELAY_DOMAIN remains available as an explicit API
domain override.
The stack's PublishClientConfig action (src/clientConfig.ts) writes the
deployed relay URL and tracing configuration into the repository-root .env, so subsequent source
builds point at the relay that was just deployed without copying values manually. It runs only when
one of those outputs changed, and T3CODE_RELAY_CLIENT_CONFIG_ENV redirects it to another file.
The relay is versioned separately from client releases. .github/workflows/deploy-relay.yml deploys
the shared Alchemy prod stage on every push to main. Stable and nightly release builds both
resolve their static public config from the same
production GitHub environment. Pull requests do not deploy relay stages. Developers can
deploy personal non-production stages locally with any stage name other than prod.
The repository must define these Actions variables shared by relay deployments:
CLOUDFLARE_ACCOUNT_IDPLANETSCALE_ORGANIZATIONAXIOM_ORG_IDThe repository must define these Actions secrets shared by relay deployments:
CLOUDFLARE_API_TOKENPLANETSCALE_API_TOKEN_IDPLANETSCALE_API_TOKENAXIOM_TOKENThe production GitHub environment must define these Actions variables:
RELAY_API_ZONE_NAMERELAY_TUNNEL_ZONE_NAMERELAY_DOMAIN if overriding the derived production relay domainCLERK_PUBLISHABLE_KEYCLERK_JWT_AUDIENCECLERK_JWT_TEMPLATEAPNS_ENVIRONMENTAPNS_TEAM_IDAPNS_KEY_IDAPNS_BUNDLE_IDThe production GitHub environment must define these Actions secrets:
CLERK_SECRET_KEYAPNS_PRIVATE_KEYFCM_SERVICE_ACCOUNT when Android push is enabledThe account-scoped repository credentials are consumed by Alchemy while provisioning relay stages; they
are not bound into the relay Worker. The production deployment uses an Axiom personal access token,
so AXIOM_ORG_ID must accompany AXIOM_TOKEN. The release workflow reads the production relay's
derived public URL and Clerk publishable key from the same environment for downstream desktop, CLI,
and hosted web builds.
See: