# Development ## First checkout Install `vp` using the [root README](../../README.md#install-vp). The checkout requires Node 24; Bun is optional. From the repository root: ```sh vp i vp run dev ``` Open the one-time pairing URL printed by the dev runner. The bare origin does not authenticate a new browser. Prefer a container? See [Dev container](../internals/devcontainer.md) for VS Code and Codespaces setup. ## Choosing a dev process Use `vp run dev` for server and web, or `vp run dev:desktop` for the Electron client. `dev:server` and `dev:web` start those processes separately. See the [mobile README](../../apps/mobile/README.md) for native builds and Metro. Flags go directly after the task name, for example `vp run dev --home-dir /tmp/t3code-dev`. Add `--browser` to open a browser automatically. ### State and ports Linked worktrees default to their own `.t3/userdata`, even when `T3CODE_HOME` is set. The main checkout defaults to `~/.t3/dev/userdata`. An explicit `--home-dir` wins in both cases. Never run a development server against the live `~/.t3/userdata`. See [test data](../../AGENTS.md#test-data) for copying a consistent database snapshot. Read ports from the `[dev-runner]` output. Worktrees derive stable preferences from their paths, but occupied ports can shift them. `T3CODE_PORT_OFFSET` or `T3CODE_DEV_INSTANCE` can select a different preference when needed. ### Sharing and remote debugging `vp run dev --share` publishes the web port over the machine's tailnet and prints a pairing URL for that origin. Give the tester the complete URL, including its token. The dev runner removes its mapping on exit. Leave `VITE_HTTP_URL` and `VITE_WS_URL` unset. Vite proxies the backend through the browser's origin so the same build works over localhost and remote connections. Shared runs enable bundled dev to avoid a network round trip for each import level. `T3CODE_BUNDLED_DEV=0` opts out when debugging bundler differences. Two reload traps matter when changing this setup: - The web entry must dynamically import the app so React refresh initializes before application chunks. Static imports can work on first load and fail after a route split. - Bundled dev rebuilds Tailwind through watched files. Its ordinary Vite hot-update hook expects a server/module graph that Rolldown does not provide. The workarounds live in the [web entry](../../apps/web/src/bootstrap.ts) and [Tailwind plugin](../../apps/web/vite/tailwind.ts). ## Checks Run checks for the files and packages you changed: ```sh vp test run vp lint vp run --filter typecheck ``` Use `vp run lint:mobile` for native mobile changes. CI owns the full suite; see [ci.yml](../../.github/workflows/ci.yml) for its current jobs. The [manual Windows lane](../../.github/workflows/windows-tests.yml) is available for focused Windows investigation while that suite is not a required gate. ### Unused code `vp run knip:check` checks unused files and dependencies across the repo, then unused runtime exports in `apps/desktop`, `apps/web`, and every internal package under `packages/`. CI enforces both checks. Exported types and Effect schemas are allowed without consumers. The schema preprocessor recognizes schema types, including aliases and schema classes; functions that create or decode schemas remain checked. Completely unused files remain checked too. Named exports in web UI component modules are kept as complete component sets. Knip ignores unused exports in `apps/web/src/components/ui/*.tsx`, while still reporting an entire unused file. Use `vp run knip --workspace apps/web` to audit one workspace, including exports, or `vp run knip:production --workspace apps/web` to find code kept alive only by tests. The full export audit still has findings and is not a repo-wide CI gate. Extend the export check's workspace selectors as more workspaces become clean. Review callers before deleting code; production mode can also report development scripts and test fixtures. Runtime-discovered entrypoints and dependency exceptions belong in [knip.jsonc](../../knip.jsonc). ## Desktop artifacts Local artifact builds are unsigned by default and write to `release/`: ```sh vp run dist:desktop:dmg vp run dist:desktop:linux vp run dist:desktop:win ``` DMGs default to the host architecture. Use `--arch` to choose another target and `--keep-stage` to retain packaging files for inspection. Run `vp run dist:desktop:artifact --help` for other options. ### Linux AppImage prerequisites Build on Linux because the browser-secret helper links against the host's libsecret. Install Rust, C/C++ build tools, libsecret development headers, pkg-config, and ImageMagick. Ubuntu and Debian: ```sh sudo apt-get update sudo apt-get install cargo rustc build-essential libsecret-1-dev pkg-config imagemagick ``` Fedora: ```sh sudo dnf install rust cargo gcc gcc-c++ make libsecret-devel pkgconf-pkg-config ImageMagick ``` Arch Linux: ```sh sudo pacman -S rust base-devel libsecret pkgconf imagemagick ``` The C toolchain, pkg-config, and libsecret headers are also needed for Linux desktop development. ### macOS DMG prerequisites Install the Xcode Command Line Tools with `xcode-select --install` and install Rust. For a cross-architecture or universal build, add the requested Rust targets: ```sh rustup target add aarch64-apple-darwin x86_64-apple-darwin ``` ### Windows installer prerequisites Install Rust, Python 3, and Visual Studio Build Tools with **Desktop development with C++**. Include the Windows SDK and the MSVC build tools and Spectre-mitigated libraries for the target architecture. Add its Rust target: ```powershell rustup target add x86_64-pc-windows-msvc # For an ARM64 installer: rustup target add aarch64-pc-windows-msvc ``` NSIS is downloaded by electron-builder. WSL support additionally needs a Linux node-pty prebuild; see the [release runbook](./release.md#windows-payload-topology-and-update-validation). ### Signing and passkeys Add `--signed` after configuring the platform credentials in the [release runbook](./release.md). macOS passkeys need a signed, provisioned app; follow the [Connect setup](./connect-setup.md#desktop-passkeys) for local signing and renderer HMR.