For maintainers. Using T3 Code? See docs/user.
T3 Code uses Vite+. Install the global vp command, install
dependencies, then start the dev stack:
curl -fsSL https://vite.plus | bash # Windows: irm https://vite.plus/ps1 | iex
vp i
vp run devNode 24 is required. Bun is not: the server picks Bun adapters when it detects Bun and falls back to Node otherwise, and nothing in contributor setup needs it.
vp run dev prints a one-time pairing URL. Open it so the first browser navigation is
authenticated.
vp run dev: Starts contracts, server, and web in watch mode.vp run dev --share: Also publishes the web port over HTTPS on this machine's tailnet. The startup pairing URL is built against the shared origin, and the mapping is removed on exit. Shared runs default to Vite's bundled dev mode (T3CODE_BUNDLED_DEV=1): a remote browser pays a network round trip per import level in unbundled dev, which turns a cold module graph into minutes of waterfall. SetT3CODE_BUNDLED_DEV=0to opt a shared run back out.vp run dev --browser: Auto-opens a browser. Off by default. The dev runner writesT3CODE_NO_BROWSERitself from this flag, so settingT3CODE_NO_BROWSER=0in your environment has no effect; use--browser.vp run dev:server: Starts just the server. It runs on Node (node --watch src/bin.ts), so without Bun present it selectsNodePtyAdapterandNodeHttpServer.vp run dev:web: Starts just the Vite dev server for the web app.vp run dev:desktop: Starts the Electron shell against the dev server.vp run dev:marketing: Starts the Astro marketing site.- Pass dev-runner flags directly after the root task name, for example:
vp run dev --home-dir /tmp/t3code-dev
- Dev commands run from a linked git worktree default to that worktree's gitignored
.t3, even whenT3CODE_HOMEis set, storing state in<worktree>/.t3/userdata. Pass--home-dir <path>to choose another isolated directory explicitly. Submodules are not worktrees and keep the normal precedence. - From the main checkout, dev commands implicitly use
~/.t3/dev, keeping development state separate from~/.t3/userdata. An explicit--home-dir <path>stores state under<path>/userdata; the base directory remains available for caches, worktrees, and other shared data.
vp run build: Fans out overapps/*,packages/*,oxlint-plugin-t3code, andscripts. Workspaces that define a build task run one: desktop, marketing, server (which depends on web), and web. Shared packages are consumed and bundled transitively rather than built separately.vp run build:desktop: Builds the desktop pipeline (desktop plus server).vp run start: Runs the production server (serves the built web app as static files).vp check: Vite+ format, lint, and type checks. This repo setstypeCheck: falsein its lint options, so workspace type checking runs separately.vp run typecheck: Strict TypeScript checks for all packages.vp run test: Runs workspace tests.vp run lint:mobile: Mobile native static analysis (scripts/mobile-native-static-check.ts).pnpm pr:ready: Agent publish path — runs the ship gate (vp check,vpr typecheck,vp run test), then marks the open draft PR ready. Do not use rawgh pr readyfrom coding agents; the.tools/bin/ghshim blocks undraft side channels (installed byscripts/install-git-hooks.mjsonprepare).pnpm test:agent-gate: Unit tests for the agent pre-push / PR-state / gh-policy helpers.- Husky:
pre-commitrunspnpm lint-staged(vp fmton staged files +vp lint --fixon staged code files);pre-pushrunsscripts/agent-pre-push.mjs(agents only: static gate —vp check+ typecheck — on draft / no PR; full ship gate including unit tests on ready PRs / publish; staged SHA cache in.run/agent-ship-gate.json). node apps/server/scripts/t3-sqlite-state.ts <query|exec> --base-dir <path> ...: Inspects or seeds an isolated T3 SQLite database; writes create a private backup first.
vp run dist:desktop:artifact --platform <mac|linux|win> --target <target> --arch <arch>: Builds a desktop artifact for a specific platform/target/arch.vp run dist:desktop:dmg: Builds a shareable macOS.dmginto./release. Architecture defaults to the host, so this produces an arm64 DMG on Apple Silicon. Usedist:desktop:dmg:arm64ordist:desktop:dmg:x64, or pass--arch <arm64|x64|universal>, to force one.vp run dist:desktop:linux: Builds a Linux AppImage into./release.vp run dist:desktop:win: Builds a Windows NSIS installer into./release.:arm64and:x64variants exist.
- Default build is unsigned/not notarized for local sharing.
- The DMG build uses
assets/prod/black-macos-1024.pngas the production app icon source. - Desktop production windows load the bundled UI from the
t3code://app/root URL (not a127.0.0.1document URL, and not an explicitindex.htmlpath). - Desktop packaging includes
apps/server/dist(thet3backend) and starts it on loopback with an auth token for WebSocket/API traffic. - Your tester can still open it on macOS by right-clicking the app and choosing Open on first launch.
- To keep staging files for debugging package contents, run:
vp run dist:desktop:dmg --keep-stage - To allow code-signing/notarization when configured in CI/secrets, add:
--signed. - Signed macOS builds also require
T3CODE_APPLE_TEAM_IDandT3CODE_MACOS_PROVISIONING_PROFILE. The passkey RP domain is derived fromT3CODE_CLERK_PUBLISHABLE_KEYunlessT3CODE_CLERK_PASSKEY_RP_DOMAINSoverrides it. - Windows
--signeduses Azure Trusted Signing and expects:AZURE_TRUSTED_SIGNING_ENDPOINT,AZURE_TRUSTED_SIGNING_ACCOUNT_NAME,AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME, andAZURE_TRUSTED_SIGNING_PUBLISHER_NAME. - Azure authentication env vars are also required (for example service principal with secret):
AZURE_TENANT_ID,AZURE_CLIENT_ID,AZURE_CLIENT_SECRET.
dev and dev:web leave VITE_HTTP_URL and VITE_WS_URL unset so the browser resolves the backend
from window.location.origin. Vite proxies /api, /ws, /oauth, and /.well-known to the
server, allowing the same bundle to work from localhost or a tailnet hostname.
Worktrees derive a preferred port offset from their path.
- Default ports: server
13773, web5733 - Shifted ports:
base + offset - Example:
T3CODE_DEV_INSTANCE=branch-a vp run dev:desktop
Offset resolution, in order:
T3CODE_PORT_OFFSET, which must be a non-negative integer. Negative values are rejected.T3CODE_DEV_INSTANCE. An all-digit value is used directly as the offset; any other non-empty value is hashed into one.- The worktree path hash.
Collision scanning depends on the mode. dev:web scans only the web port and shifts only the web
offset. dev:server scans only the server port. dev and dev:desktop scan both and shift them
together as one shared offset. Explicit server or dev-URL overrides remove the corresponding port
from the availability check. Treat the [dev-runner] output as authoritative.