Prerequisite: You should have installed Node.js
(version 22 or higher, matching the repo’s
engines.node of >=22).Installation
Step 1. Install bun on your machine (if you don’t already have it yet), by running the following command: macOS/Linux:Why Bun?
We chose bun as our runtime and package manager based on its 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.jsxfiles - 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, andReadableStreamout 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
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:bun
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
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:
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:
--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 |
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 |
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:
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:
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:
Diagnose a broken local environment
When a local app suddenly cannot reach another app — for example the Inventory landing page throwsInternalApiError: 404 because its server-side call to
https://tuturuuu.localhost/api/... is not routed — run the doctor:
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:
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:
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:
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:
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:
.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
Whenapps/web feels slow or Activity Monitor reports high Node.js usage, start
with the dev diagnostic before moving routes into another app:
Live Next dev process RSSis the memory held by the runningnext devprocess tree. If this is high, inspect the top processes and the latest trace spans before changing architecture.apps/web/.next/dev/cache/turbopackis 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/cacheis Turborepo task cache. It is separate from the live Next.js dev server and should not be treated as app RSS.Build memory policyreports Docker andnext buildmemory budgets. Those caps explain build behavior only; do not compare them directly tonext devRSS.Likely pressurecalls out whether the current evidence points to live RSS, cache growth, watcher limits, or build-only memory settings.
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:
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:
FLUSHALL inside the
Redis container.
To stop the local dev environment, run:
bun doctor. Preview the cleanup first with:
Start Local Supabase Instance
To start a local supabase instance (database), run the following command:bun
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
Stop Local Supabase Instance
To stop the local supabase instance, run the following command:standard
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:bun
Running
devx will:- Stop the currently running supabase instance and save current data as backup (if there is any)
- Install all dependencies
- Start a new supabase instance (using backed up data)
- Start all Next.js apps in development mode
bun devrs instead. This will:- Stop the currently running supabase instance (if there is any)
- Install all dependencies
- Start a new supabase instance (with clean data from seed.sql)
- Start all Next.js apps in development mode
Local development
Seed accounts
There are 5 seed accounts that are already set up for local development: 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.You can access the mail server by visiting the following URL:
http://localhost:8004
Build
To build all apps and packages, run the following command:bun
Test
To run all tests, run the following command:bun
bun
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.