> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Development

> Learn how to preview changes locally.

<Info>
  **Prerequisite**: You should have installed [Node.js](https://nodejs.org)
  (version 22 or higher, matching the repo's `engines.node` of `>=22`).
</Info>

## Installation

Step 1. Install [bun](https://bun.sh/docs/installation) on your machine (if you don't already have it yet), by running the following command:

**macOS/Linux:**

```bash theme={null}
curl -fsSL https://bun.sh/install | bash
```

**Windows:**

```bash theme={null}
powershell -c "irm bun.sh/install.ps1 | iex"
```

## Why Bun?

We chose bun as our runtime and package manager based on its [design goals](https://bun.sh/docs#design-goals), which align perfectly with our platform's needs:

* **4x Faster Startup**: Bun processes start significantly faster than Node.js, improving development experience and CI/CD performance
* **Built-in TypeScript & JSX Support**: No need for additional transpilation setup - bun natively executes `.ts`, `.tsx`, and `.jsx` files
* **All-in-One Toolkit**: Combines runtime, package manager, bundler, test runner, and script runner in a single executable
* **Web Standards Compatibility**: Implements modern Web APIs like `fetch`, `WebSocket`, and `ReadableStream` out of the box
* **Node.js Compatibility**: Drop-in replacement for Node.js with full compatibility for existing projects
* **Better Performance**: Powered by JavaScriptCore engine with reduced memory usage and faster execution

These benefits make bun an ideal choice for our monorepo architecture and development workflow.

Step 2. **Configure Tiptap Pro Registry:**

<Warning>This step is no longer needed.</Warning>

Step 3. After configuring Tiptap Pro registry, you can install all dependencies by running the following command:

<CodeGroup>
  ```bash standard theme={null}
  bun install
  ```

  ```bash short-hand theme={null}
  bun i
  ```
</CodeGroup>

This repository's Bun configuration enforces a minimum release age of 1 day for
new npm package versions. If Bun skips a just-published release during install,
wait for the package to age past the gate or choose an older published version.

To complete the initial setup, please restart your IDE so that it can recognize the newly installed dependencies. Additionally, some recommended VS Code Extensions may only work after restarting your IDE.

<Tip>
  If you're using VS Code, you can install following the recommended extensions
  that will help you with the development process:
  [Biome](https://marketplace.visualstudio.com/items?itemName=biomejs.biome)
  (this repo standardizes on Biome for linting and formatting via `biome.json`,
  not ESLint or Prettier),
  [Vitest](https://marketplace.visualstudio.com/items?itemName=vitest.explorer),
  [Tailwind CSS
  IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss),
  [Version
  Lens](https://marketplace.visualstudio.com/items?itemName=pflannery.vscode-versionlens),
  [Error
  Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens),
  [Pretty TypeScript
  Errors](https://marketplace.visualstudio.com/items?itemName=YoavBls.pretty-ts-errors),
  [Material Icon
  Theme](https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme).
</Tip>

### Zed

This monorepo tracks project-level Zed settings in `.zed/settings.json`.
Opening the repository root in Zed can otherwise trigger expensive scans across
generated output and local dependency trees, especially `node_modules`,
`.turbo`, `.next`, package `dist` directories, Flutter build output, Rust
`target`, and Python virtualenv/cache folders.

Keep large generated directories in `file_scan_exclusions`. Zed's
`file_scan_exclusions` setting replaces the default list instead of extending
it, so include Zed's default VCS/system exclusions whenever adding project
exclusions. The project uses TypeScript 7 RC's `tsc` for compiler checks and a
single TypeScript language server path for TypeScript, TSX, and JavaScript;
avoid enabling competing TypeScript language servers for the same language
unless you are deliberately comparing language server behavior.

## Development

### Start Next.js apps

To develop all apps and packages (without requiring a local setup), run the following command:

```bash bun theme={null}
bun dev
```

<Info>
  This command will start all Next.js apps in development mode. You can access
  the platform app by visiting the following URL:
  [https://tuturuuu.localhost](https://tuturuuu.localhost)
</Info>

Local app development uses Portless, so each app gets a stable Tuturuuu
subdomain instead of relying on its fallback port. The app package `dev`
scripts run the repo's Portless-safe wrapper, and the underlying framework
command remains available through `dev:app` for direct port-based debugging.

The wrapper only removes stale static aliases for the same app when their
default backend port is closed, then starts Portless with the original app
config. This fixes old worktree aliases such as
`zalo-qr-chat-setup.tuturuuu.localhost` without cleaning certificates, deleting
unrelated routes, or touching other apps' active aliases. Package-local
`bun dev --force` delegates to `portless run --name <app> --force` so Portless
can take over a live route when you explicitly request it.

Root app-specific commands such as `bun dev:chat`, `bun dev:calendar`, and
`bun dev:edu` reuse app servers that are already running. The launcher checks
active Portless routes first, then the app's default localhost port; if it finds
a direct localhost listener, it tries to register a matching Portless alias so
cross-app auth can still use the `*.tuturuuu.localhost` URL. Pass `--force` or
`--no-reuse` when you intentionally want the command to include already-running
app workspaces. Force mode starts the package `dev --force` path so Portless can
take over the matching route instead of leaving a stale static alias in place.

`bun dev:tanstack-web` starts the TanStack Start migration frontend at
`https://tanstack.tuturuuu.localhost`, with fallback port `7824`. It is the
dedicated frontend for the Next.js-to-TanStack migration and should call
Rust-owned APIs through `packages/internal-api` helpers instead of direct
browser access to protected `apps/web` routes. See
`platform/architecture/tanstack-rust-migration` for the route manifest, Docker,
E2E, benchmark gates, and Cloudflare Workers preparation.

For the Cloudflare-compatible preview shape, start the Worker entrypoints
instead of the normal Portless dev wrapper:

```bash theme={null}
bun wrangler dev --config apps/backend/wrangler.jsonc
bun --cwd apps/tanstack-web run preview:cloudflare
```

Use the same environment variable names as deployment:
`BACKEND_INTERNAL_TOKEN`, `BACKEND_PUBLIC_ORIGIN`, and
`BACKEND_INTERNAL_URL`. Keep values in your shell or ignored local env files;
do not add literal tokens or account-specific origins to docs, Wrangler config,
package manifests, or source. After deploying preview Workers, run
`bun smoke:cloudflare` with `BACKEND_WORKER_ORIGIN`,
`TANSTACK_WEB_WORKER_ORIGIN`, and `BACKEND_INTERNAL_TOKEN` to verify backend
health/readiness, protected migration status, missing/invalid token rejection,
and the TanStack root shell.

`bun dev:web` is intentionally lean: by default it starts only `apps/web` and
does not start the `@tuturuuu/types` or `@tuturuuu/supabase` package watch
builds. Use this default for app-route and UI work so the local process tree
stays small. When you are actively editing those package sources and need their
`dist` output rebuilt live, opt in explicitly:

```bash theme={null}
bun dev:web --with-shared-watchers
```

You can also pass `--no-shared-watchers` to other root app launchers when you
want the same low-memory posture for a focused app session.

`apps/web` does not mount React Query Devtools in its default development
provider. Keep the default off for faster cold route compiles and lower
Turbopack memory pressure; add a local, temporary devtools mount only while
debugging query cache behavior, then remove it before committing.

Keep the always-mounted public shell small. The mobile menu drawer, marketing
footer body, report-problem dialog, and authenticated dropdown-only affordances
should stay behind dynamic imports when possible, and shell icons should use
`@tuturuuu/icons/lucide-static` instead of the root `@tuturuuu/icons` entrypoint.
This prevents the public `/login` compile graph from pulling large Radix,
footer, or full icon-package chunks into every `bun dev:web` session.

`bun dev:chat` also manages the local chat realtime sidecar. It reuses
`localhost:7817` when a sidecar is already running; otherwise it starts
`apps/chat-realtime` so `apps/web` can proxy chat SSE traffic for `apps/chat`.

| App           | Local URL                                  |
| ------------- | ------------------------------------------ |
| Platform      | `https://tuturuuu.localhost`               |
| Calendar      | `https://calendar.tuturuuu.localhost`      |
| Chat          | `https://chat.tuturuuu.localhost`          |
| CMS           | `https://cms.tuturuuu.localhost`           |
| Drive         | `https://drive.tuturuuu.localhost`         |
| External      | `https://external.tuturuuu.localhost`      |
| Finance       | `https://finance.tuturuuu.localhost`       |
| Hive          | `https://hive.tuturuuu.localhost`          |
| Hive Realtime | `https://realtime.hive.tuturuuu.localhost` |
| Infra         | `https://infra.tuturuuu.localhost`         |
| Inventory     | `https://inventory.tuturuuu.localhost`     |
| Learn         | `https://learn.tuturuuu.localhost`         |
| Mail          | `https://mail.tuturuuu.localhost`          |
| Meet          | `https://meet.tuturuuu.localhost`          |
| Mind          | `https://mind.tuturuuu.localhost`          |
| Nova          | `https://nova.tuturuuu.localhost`          |
| Playground    | `https://playground.tuturuuu.localhost`    |
| QR            | `https://qr.tuturuuu.localhost`            |
| Rewise        | `https://rewise.tuturuuu.localhost`        |
| Shortener     | `https://shortener.tuturuuu.localhost`     |
| Tasks         | `https://tasks.tuturuuu.localhost`         |
| Teach         | `https://teach.tuturuuu.localhost`         |
| Track         | `https://track.tuturuuu.localhost`         |

When debugging without Portless, run an app's `dev:app` script directly. If
auth redirects or absolute links must stay on the raw listener port, set
`BASE_URL`, `WEB_APP_URL`, or the app-specific URL environment variable to that
legacy fallback port for the direct debug session.

Keep runtime defaults, auth redirects, and cross-app links on the Portless
origins during local development. Use the shared local app URL helpers instead
of hard-coding `localhost` fallbacks in satellite app constants or proxies, so a
browser session that starts on `tasks.tuturuuu.localhost` stays under the
`tuturuuu.localhost` namespace through login and return URLs.

For a native production-mode smoke test of `apps/web`, run the native build and
then start it through the same Portless hostname:

```bash theme={null}
bun run build:web
cd apps/web
bun run start
```

`bun run start` delegates to Portless and runs the built Next.js server through
`start:app`, so the browser URL remains `https://tuturuuu.localhost` while the
server listens on the backend port that Portless injects through `PORT`.

Use `bun dev:edu` when working on the education apps together. It starts Learn,
Teach, their shared packages, and the central web app so cross-app login can
complete locally.

Next.js still prints the internal listener port, for example
`http://localhost:4803`, because Portless assigns a free backend port to the app
process. App `dev:app` scripts also print a `Portless URL:` line from the
`PORTLESS_URL` environment variable; use that URL in the browser:

```text theme={null}
Portless URL: https://tuturuuu.localhost
```

If Turbo cannot prompt for the Portless sudo password while starting app dev
scripts, run the Portless setup helper first:

```bash theme={null}
bun portless:setup
```

The helper starts the Portless HTTPS proxy on port `443` before Turbo launches
package `dev` scripts. It no-ops when the proxy is already responding and skips
in non-interactive shells or CI. `bun setup` runs this helper after dependency
installation so fresh local checkouts are ready before the build step, then
invokes Turborepo through the repo-local `turbo:local` helper instead of any
globally installed Turbo binary. To install Portless as an OS startup service
instead of only starting the current proxy daemon, run:

```bash theme={null}
bun portless:setup -- --service
```

### Diagnose a broken local environment

When a local app suddenly cannot reach another app — for example the Inventory
landing page throws `InternalApiError: 404` because its server-side call to
`https://tuturuuu.localhost/api/...` is not routed — run the doctor:

```bash theme={null}
bun doctor
```

It checks the Node runtime, Docker daemon, local Supabase ports, local Redis
ports, apps/web Redis env wiring, app-local Supabase env consistency, whether
the Portless proxy is responding on port `443`, and the health of every
registered Portless route. The report uses colored `OK`, `WARN`, and `FAIL`
statuses in interactive terminals. Supabase env mismatch warnings are compact
by default so secret-like values stay out of the normal output; use the verbose
view when you need redacted per-file fingerprints:

```bash theme={null}
bun doctor --verbose
```

The most common failure after overlapping or crashed dev sessions is a
**stale routing table**: the proxy still points a live app route at a dead
dev-server port, so cross-app internal API calls return `404`. The doctor flags
those routes as `DEAD` and exits non-zero. Static aliases for apps you simply
are not running are reported as warnings, not failures, and include an exact
cleanup command. An initialized but empty Portless route table is treated as
healthy because no apps may be running yet.

Apply the safe automatic Portless repair (start the proxy, or reset it when
routes are stale) and re-run the checks:

```bash theme={null}
bun doctor --fix
```

`bun setup` deliberately no-ops when a proxy is already responding, so it does
**not** clear a stale routing table on its own. To force a clean restart — stop
the proxy, prune orphaned dev servers from crashed sessions, then start a fresh
proxy that apps re-register against — run:

```bash theme={null}
bun portless:reset
```

After a reset, restart your dev servers (for example `bun dev:inventory`) so
each app re-registers its route with the new proxy.

If `bun doctor` still warns about an inactive static alias after a reset, that
alias was created with `portless alias` and is not deleted by proxy resets.
Start the app if you still use the alias, or remove the alias shown in the
doctor output:

```bash theme={null}
bunx portless alias --remove <alias-name>
```

If the doctor warns that app env files use mismatched Supabase values, choose
the intended source app and keep these three keys together:
`NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, and
`SUPABASE_SECRET_KEY`. For quick local development, copy `apps/web/.env.local`
to app-local `.env.local` files:

```bash theme={null}
bun dev:sync:apps
```

Preview the target files first with:

```bash theme={null}
bun dev:sync:apps:dry-run
```

This sync compares app env files only. The root `.env.local` can intentionally
target a different Docker or deployment workflow without keeping the app
mismatch warning alive.

### Diagnose web dev memory and cache pressure

When `apps/web` feels slow or Activity Monitor reports high Node.js usage, start
with the dev diagnostic before moving routes into another app:

```bash theme={null}
bun diagnose:dev:web
```

Read the report by pressure type:

* `Live Next dev process RSS` is the memory held by the running `next dev`
  process tree. If this is high, inspect the top processes and the latest trace
  spans before changing architecture.
* `apps/web/.next/dev/cache/turbopack` is generated Turbopack filesystem cache.
  Multi-GB cache growth can make disk usage look like a live memory leak even
  when process RSS is modest.
* `.turbo/cache` is Turborepo task cache. It is separate from the live Next.js
  dev server and should not be treated as app RSS.
* `Build memory policy` reports Docker and `next build` memory budgets. Those
  caps explain build behavior only; do not compare them directly to `next dev`
  RSS.
* `Likely pressure` calls out whether the current evidence points to live RSS,
  cache growth, watcher limits, or build-only memory settings.

If the report points at Turbopack cache growth, stop the web dev server and
preview the targeted cleanup first:

```bash theme={null}
bun clean:dev:web --dry-run
```

Then remove the web Next dev output:

```bash theme={null}
bun clean:dev:web
```

Use `bun clean:dev:web --all-next-dev --dry-run` when several app dev caches
exist and you want to inspect every `apps/*/.next/dev` target before deleting
them. Add `--include-turbo-cache` only when the report also calls out
`.turbo/cache` growth after branch or dependency graph churn. Avoid the broad
root `bun clean` command for this problem because it deletes dependency and
build state that is unrelated to the current dev-server slowdown.

Then restart the targeted app command you were using, such as `bun dev:web`.
Avoid deleting unrelated app output while other agents or local sessions are
working in the same checkout.

If live RSS is the real pressure, use the latest `.next/dev/trace` spans and
the matching process tree to reduce the compiled import graph first. Favor
narrow package subpath imports and lazy-loaded settings panels before splitting
an app boundary. A dedicated infrastructure app only reduces `apps/web` memory
when infrastructure pages leave the web route graph; running `apps/web` and an
infrastructure app together can increase total RAM.

Native local dev leaves Watchpack polling off unless the environment explicitly
sets `WATCHPACK_POLLING`. This keeps file watching lighter on macOS and Linux.
If the diagnostic reports watcher `EMFILE` errors or route discovery stops
updating on your machine, restart with polling enabled:

```bash theme={null}
WATCHPACK_POLLING=true bun dev:web
```

If the doctor warns that apps/web is missing Redis env, configure the local SRH
bridge and start the Redis stack in one step:

```bash theme={null}
bun redis:setup
```

`bun redis:setup` writes `UPSTASH_REDIS_REST_URL=http://localhost:8079` and the
matching local token into `apps/web/.env.local`, then runs `bun redis:start`.
Restart `bun dev:web` after setup so the app picks up the new env values.

To clear local Redis data while keeping the same local env wiring, run:

```bash theme={null}
bun redis:reset
```

This starts the local Redis stack if needed, then runs `FLUSHALL` inside the
Redis container.

To stop the local dev environment, run:

```bash theme={null}
bun stop
```

This stops this repo's Portless-registered Next.js dev servers, prunes Portless
routes, removes stale static aliases for this project, stops local Supabase,
and finishes by running `bun doctor`. Preview the cleanup first with:

```bash theme={null}
bun stop --dry-run
```

### Start Local Supabase Instance

To start a local supabase instance (database), run the following command:

```bash bun theme={null}
bun sb:start
```

<Info>
  This command will start a local supabase instance on your machine. You can
  access the supabase instance by visiting the following URL:
  [http://localhost:8003](http://localhost:8003)
</Info>

<Warning>
  You need to have Docker installed and running on your machine to start a local
  supabase instance.
</Warning>

### Stop Local Supabase Instance

To stop the local supabase instance, run the following command:

```bash standard theme={null}
bun sb:stop
```

`bun stop` is broader than Supabase. It stops this repo's local dev servers,
cleans stale Portless aliases, stops Supabase, and verifies the result with
`bun doctor`.

### Better Development Experience

In case you want to run all local development servers, run the following command:

```bash bun theme={null}
bun devx
```

<Info>
  Running `devx` will:

  1. Stop the currently running supabase instance and save current data as backup (if there is any)
  2. Install all dependencies
  3. Start a new supabase instance (using backed up data)
  4. Start all Next.js apps in development mode

  If you want to have the same procedure without the backup, you can run `bun devrs` instead. This will:

  1. Stop the currently running supabase instance (if there is any)
  2. Install all dependencies
  3. Start a new supabase instance (with clean data from seed.sql)
  4. Start all Next.js apps in development mode
</Info>

<Tip>
  In case you don't want to run a local supabase instance, you can run `bun dev`
  instead.
</Tip>

### Local development

#### Seed accounts

There are 5 seed accounts that are already set up for local development:

1. [local@tuturuuu.com](mailto:local@tuturuuu.com)
2. [user1@tuturuuu.com](mailto:user1@tuturuuu.com)
3. [user2@tuturuuu.com](mailto:user2@tuturuuu.com)
4. [user3@tuturuuu.com](mailto:user3@tuturuuu.com)
5. [user4@tuturuuu.com](mailto:user4@tuturuuu.com)

You can use any of these accounts to log in to the app and quickly test the functionality of the app, since they are already set up with the necessary data.

#### Authentication

A local mail server (**InBucket**) is automatically set up by Supabase to handle authentication emails.

<Info>
  You can access the mail server by visiting the following URL:
  [http://localhost:8004](http://localhost:8004)
</Info>

## Build

To build all apps and packages, run the following command:

```bash bun theme={null}
bun run build
```

## Test

To run all tests, run the following command:

```bash bun theme={null}
bun run test
```

To run the Flutter mobile quality gate, use:

```bash bun theme={null}
bun check:mobile
```

`bun check:mobile` runs Dart format, Flutter analyze, and Flutter tests for
`apps/mobile`. Flutter test output is streamed by default so slow suites still
show progress instead of appearing stuck.

<Warning>
  Tests are still a work in progress. We're currently working on adding tests to all packages to ensure the best quality possible.
</Warning>

## Git Conventions

Tuturuuu uses standardized conventions for Git commits and branch naming. For more information, see the [Git Conventions](/build/development-tools/git-conventions) guide to learn how to format your commits and branch names to align with our workflow.
