# Project structure

> Know where routes, shared UI, and content belong.

Source: http://tinyro.bot/docs/project-structure

## A small monorepo

```text
api/
  models/                   Authored JSON Schema wire models
  generated/                Generated self/cloud OpenAPI snapshots
server/
  core/                     Shared Effect services, PostgreSQL and Better Auth
  cloud/                    Cloud-only backend extensions
web/
  web-deploy-cloud/         Node/Start: shared app + cloud-only features
    src/document.tsx        Cloud HTML document and theme
    src/public-shell.tsx    Public navigation, search, account and footer composition
    src/routes/_site.tsx    Public shell route
    src/routeTree.gen.ts     Cloud-generated route tree
    src/server.ts           Backend-first Start server entry
    src/routeTree.gen.ts     Self-generated route tree
    Dockerfile              Multi-stage app-only image
  web-router/
    src/vite.ts             Shared virtual-route/Vite composition
    src/backend.server.ts   Shared backend lifecycle adapter
  web-app/
    src/routes/             Application routes (starts at /app)
  web-community/
    src/experiments.ts      Static gallery metadata; no model imports
    src/demo/               Shared checked, cancellable browser runtime
    src/routes/             Cloud-only gallery, experiments and disabled login
  web-og/
    src/routes/             Cloud-only OG image route
  web-status/
    src/routes/             Cloud-only status page scaffold
  web-shell/
    src/site-shell.tsx      Shared runtime shell for cloud public pages and /app
  web-landing/
    src/routes/             Cloud-only homepage route and metadata
    src/home/               Session-aware SSR homepage
  web-content/
    src/routes/             Docs/blog/changelog metadata and Markdown endpoints
    src/docs/               Documentation screens
    src/blog/               Blog screens
    src/changelog/          Changelog screens and styles
    src/llms.ts             LLM indexes over published content
    src/markdown.ts         Shared Markdown renderer
    src/content.ts          Typed content selectors
    content/docs/           Documentation in Markdown
    content/blog/           Blog posts in Markdown
    content/changelog/      Changelog updates in Markdown
    content-collections.ts  Content schemas and transforms
  api/                      Generated fetch client; public @tinyrobot/api entry
  ui/
    src/components/         Shared UI and public proxies
    src/generated/          Upstream-owned components
  analytics/                Standalone Cloudflare analytics Worker
  github/                   Standalone Cloudflare GitHub App Worker
sdk/py/                  Python SDK (PyPI: tinyrobot)
sdk/js/                  JavaScript/TypeScript SDK (npm: tinyrobot)
experiments/             Isolated tinygrad training/export projects and demo kit
```

## Routes and layouts

Features own route files, loaders, not-found decisions and metadata. `web/web-router` uses virtual routes to compose physical feature directories. Each deployment owns its root route, router factory and generated `src/routeTree.gen.ts`.

`web/web-content` owns docs (including printer monitoring and use cases), blog, changelog, and their shared Markdown/LLM pipeline. `web/web-landing` owns the cloud-only homepage. Public pages nest under the cloud deployment's `_site` route, which supplies navigation, search, account controls, and footer to `web/web-shell`; `/app` and `/printers` use that same shell in compact mode in cloud. `/llms.txt` and `/llms-full.txt` use the published content pipeline. `web/web-app` contains the client-rendered application at `/app` and the private `/printers` monitor. `web/web-community` owns the trusted `/community/` gallery and experiments; `/demos/cartpole` permanently redirects to `/community/cartpole`. `web/web-og` owns the `/og` PNG endpoint, and `web/web-status` owns the `/status` scaffold. Features use TanStack Router directly. Cloud mounts public and shared features; the app-only self-hosted web deployment is removed for now. Landing/public content/community/analytics/OG/status/invite and the cloud backend are excluded from the shared shell and self-hosting, not merely hidden in navigation.

Each deployment has a separate TypeScript program and router registration. Never import both generated route trees into one program. Feature-only checks use generated type-only route declarations. Run the owning deployment's `generate` command after route changes; `pnpm routes:check` checks both trees and declarations without rewriting sources or outputs, before full/affected checks can regenerate them.

## Browser experiment library

Visit [Community](/community) for CartPole, two-link reaching, action-chunk cube
pushing, trajectory denoising, pendulum control, handwritten digits and visual
target following, plus a curated Hugging Face Space. Local models run only after
Start, using WebGPU or explicitly selected JavaScript CPU. The Space is external
and can process inputs remotely.

The [contributor experiment index](https://github.com/yujonglee/tinyrobot/blob/main/experiments/README.md)
links every method, dataset, release gate and reproducible training/export command.
Robotics scenes are synthetic teaching tasks, not hardware-ready controllers;
MNIST results do not guarantee arbitrary handwriting recognition. Training is
isolated Python, never a dependency of web builds or a server inference service.

Splat routes support nested content paths, so `web/web-content/content/docs/guides/example.md` becomes `/docs/guides/example`, with its Markdown counterpart at `/docs/guides/example.md`.

## Backend and generated contracts

The cloud Node deployment sends `/health`, `/openapi.json`, `/api` and `/api/*` to the backend before TanStack Start, including JSON 404s for unknown API paths. Core owns shared auth and Effect resource lifetimes; cloud composes extensions without leaking them into self. Better Auth's supported Promise/pg adapter and native Effect PostgreSQL queries share the database and generated schema, but use separate pools and transaction scopes.

Author wire models in `api/models`, then run `pnpm api:generate` in core → cloud → web client order. Import web clients only through `@tinyrobot/api`; never edit generated models, OpenAPI or client files. See the [backend guide](https://github.com/yujonglee/tinyrobot/blob/main/server/core/README.md) and [API contract guide](https://github.com/yujonglee/tinyrobot/blob/main/api/README.md).

Python serves SDK and robotics work, not HTTP. The SDKs release independently through `py-vX.Y.Z` and `js-vX.Y.Z` tags; web checks need no uv and Python checks need no Node. Nx orchestrates local tasks only, with no remote cache. Website hosting migration and community login activation remain deferred; standalone Workers are not the website host.

## Shared UI

Generic slot-based layout components and UI proxies belong in `web/ui/src/components`. Runtime shell behavior lives in `web/web-shell`; navigation and optional footer/search/account controls are composed by callers. Cloud owns its public composition without adding public routes or dependencies to self-hosting.

Use public imports such as `@tinyrobot/ui/components/button`. Generated shadcn components use Base UI and are wrapped by public proxies. Never edit upstream-owned files in `web/ui/src/generated` directly.

## Framework conventions

This follows [TanStack Start's routing conventions](https://tanstack.com/start/latest/docs/framework/react/guide/routing): file-based routes, nested layouts, typed links, route loaders, and route-level metadata.

TanStack Router's ESLint plugin runs inside **oxlint** through JavaScript plugins for route property ordering and route parameter names. TanStack Start's server/client rules are deferred because they require type-aware parser services that Oxlint's JavaScript plugin API does not provide. There is no separate ESLint command.

