Project Structure

An annotated guide to the directory structure of a tostada project.

Understanding where things live helps you navigate the codebase and know where to add new features.


Root Directory

tostada/
├── client/         # SvelteKit frontend (Vite + Threlte + Three.js)
├── server/         # Phoenix backend (Elixir + Ecto + PostgreSQL)
├── scripts/        # Build and deployment scripts
├── Makefile        # Development commands (dev, test, build, deploy)
├── README.md       # Project documentation
└── .gitignore      # Git ignore rules

Key insight: Tostada uses a monorepo structure. The Makefile coordinates both client and server commands from the root.


Client Structure

The client/ directory contains the SvelteKit single-page application that talks to Phoenix via WebSocket and REST APIs.

client/
├── src/
│   ├── lib/
│   │   ├── socket.ts           # Phoenix channel connection + stores
│   │   ├── index.ts            # Re-export public library code
│   │   └── models/             # (Optional) Generated GLTF components
│   │       └── generated/      # Auto-generated by build-models.sh
│   ├── routes/
│   │   ├── +layout.svelte      # App shell (wraps all routes)
│   │   ├── +page.svelte        # Homepage (full-bleed Canvas)
│   │   └── Scene.svelte        # Default Threlte 3D scene
│   ├── app.css                 # Tailwind imports
│   ├── app.d.ts                # TypeScript ambient declarations
│   ├── app.html                # HTML template (SvelteKit entry point)
│   ├── phoenix.d.ts            # Phoenix.js type definitions
│   └── vite-env.d.ts           # Vite environment types
├── static/
│   ├── favicon.png             # Site favicon
│   └── models/                 # (Optional) Optimized GLB files
├── svelte.config.js            # SvelteKit config (builds to ../server/priv/static/app)
├── vite.config.ts              # Vite dev server + proxies to Phoenix
├── vitest.config.ts            # Unit test configuration
├── package.json                # npm dependencies and scripts
└── tsconfig.json               # TypeScript compiler options

Key Files

FilePurpose
src/lib/socket.tsExports connectSocket(), pingSocket(), and stores (socketStatus, lastSocketError). The foundation for all real-time features.
src/routes/+page.svelteThe main SPA page. Contains a full-viewport <Canvas> with the 3D scene and auth navigation.
src/routes/Scene.svelteDefault Threlte scene with an animated box. This is where you build your 3D game world.
src/app.cssTailwind CSS imports. Customize theme here or add global styles.
vite.config.tsDev proxy configuration. Routes /api, /users, /socket, /live, and /assets to Phoenix during development.
svelte.config.jsSvelteKit adapter-static builds to ../server/priv/static/app/ so Phoenix can serve the SPA at /app/*.

Where to Add Your Code

  • 3D scenes: Extend or replace Scene.svelte
  • Game logic/stores: Add files to src/lib/ (e.g., src/lib/stores/game.ts, src/lib/input.ts)
  • New routes: Add +page.svelte files in src/routes/ (though most games use a single route with store-driven scene switching)
  • Components: Create src/lib/components/ for reusable UI or 3D components

Server Structure

The server/ directory contains the Phoenix application that handles authentication, WebSocket connections, database persistence, and game server logic.

server/
├── lib/
│   ├── tostada/
│   │   ├── application.ex      # Supervision tree (starts all processes)
│   │   ├── repo.ex             # Ecto repository (database interface)
│   │   ├── mailer.ex           # Email sending (Swoosh)
│   │   ├── release.ex          # Production release tasks (migrations)
│   │   └── accounts/           # User authentication domain
│   │       ├── user.ex         # User schema (Ecto.Schema)
│   │       ├── user_token.ex   # Auth tokens (session, socket, email)
│   │       ├── user_notifier.ex # Email templates
│   │       └── scope.ex        # Permission scopes (:admin, :user)
│   ├── tostada_web/
│   │   ├── endpoint.ex         # HTTP/WebSocket entry point
│   │   ├── router.ex           # URL routing (/, /api/*, /app/*, /admin, /users/*)
│   │   ├── telemetry.ex        # Metrics and monitoring
│   │   ├── gettext.ex          # Internationalization (i18n)
│   │   ├── presence.ex         # Phoenix.Presence for player tracking
│   │   ├── user_auth.ex        # Session auth plugs (fetch_current_user, require_user)
│   │   ├── channels/           # WebSocket channels
│   │   │   ├── user_socket.ex  # Socket connection (dual auth: token + session)
│   │   │   └── app_channel.ex  # "app:lobby" channel (ping, whoami)
│   │   ├── controllers/        # HTTP request handlers
│   │   │   ├── page_controller.ex        # Marketing homepage (/)
│   │   │   ├── spa_controller.ex         # Serves /app/index.html for SPA routes
│   │   │   ├── user_*_controller.ex      # Auth pages (login, register, settings)
│   │   │   └── api/                      # REST API
│   │   │       ├── auth_controller.ex    # /api/auth/* (register, login, logout)
│   │   │       └── user_controller.ex    # /api/me, /api/socket-token
│   │   ├── live/               # LiveView (admin panel)
│   │   │   └── admin/
│   │   │       └── user_live/  # User management interface
│   │   ├── plugs/              # Custom middleware
│   │   │   ├── cors.ex         # CORS headers (dev only)
│   │   │   ├── require_admin.ex # Admin-only routes
│   │   │   └── static_assets.ex # Serve /obj/* GLTF files
│   │   └── components/         # LiveView components (Heex templates)
│   └── tostada_web.ex          # Web module imports (using TostadaWeb imports)
├── priv/
│   ├── static/                 # Static files served by Phoenix
│   │   ├── app/                # SvelteKit build output (auto-generated)
│   │   ├── assets/             # CSS/JS for auth pages (Tailwind + esbuild)
│   │   └── obj/                # (Optional) GLTF/GLB source files
│   ├── repo/
│   │   ├── migrations/         # Database schema changes (timestamped .exs files)
│   │   └── seeds.exs           # Sample data for development
│   └── gettext/                # Translation files (.po)
├── test/
│   ├── support/                # Test helpers (fixtures, case templates)
│   ├── tostada/                # Domain logic tests
│   └── tostada_web/            # Controller, channel, LiveView tests
├── scripts/
│   └── deploy/                 # Production deployment scripts
│       ├── build.sh            # Build release tarball
│       ├── deploy.sh           # Run migrations + restart
│       ├── full-deploy.sh      # git pull + build + deploy
│       ├── nginx.conf          # Nginx reverse proxy template
│       └── systemd.service     # systemd service file template
├── config/
│   ├── config.exs              # Base config (imports env-specific configs)
│   ├── dev.exs                 # Development settings
│   ├── test.exs                # Test settings
│   ├── prod.exs                # Production settings
│   └── runtime.exs             # Runtime environment variables
├── mix.exs                     # Elixir project definition + dependencies
└── mix.lock                    # Locked dependency versions

Key Files

FilePurpose
lib/tostada/application.exSupervision tree. Add your GameServer here as a child process.
lib/tostada_web/router.exDefines all HTTP routes. Check here to see available endpoints.
lib/tostada_web/endpoint.exConfigures socket mounts (/socket for UserSocket, /live for LiveView).
lib/tostada_web/channels/user_socket.exWebSocket authentication. Supports both Phoenix.Token (native clients) and session cookies (web). Add new channel topics here.
lib/tostada_web/channels/app_channel.exStarting point for game channels. Extend this or create new channel modules.
lib/tostada_web/presence.exPhoenix.Presence configured with Tostada.PubSub. Use this to track online players.
priv/repo/migrations/Database schema. Run make db.migrate to apply changes.

Where to Add Your Code

  • Game server logic: Create lib/tostada/game_server.ex (or similar), add to supervision tree
  • New channels: Add modules to lib/tostada_web/channels/ and register in user_socket.ex
  • REST API endpoints: Add controllers to lib/tostada_web/controllers/api/ and routes to router.ex
  • Database tables: Generate migrations with mix ecto.gen.migration create_games, edit in priv/repo/migrations/

Scripts Directory

The scripts/ directory contains automation for development and deployment.

scripts/
├── build-models.sh       # GLTF → Svelte component pipeline
└── init.sh               # First-time project setup
ScriptPurpose
build-models.shConverts .gltf/.glb files from server/priv/static/obj/ into Svelte components in client/src/lib/models/generated/. Also creates a registry file for lazy loading. Run with make models.build.
init.shRun once after scaffolding a new project. Sets up database, installs dependencies, builds client.

Configuration Files

Root-Level

FilePurpose
MakefileDevelopment commands. Run make help to see all available targets. This is your primary interface for dev/build/test.
.gitignoreTells Git to ignore node_modules/, deps/, _build/, etc.

Client-Level

FilePurpose
package.jsonnpm dependencies (SvelteKit, Threlte, Three.js, Phoenix.js) and scripts.
svelte.config.jsSvelteKit adapter-static, builds to Phoenix’s static directory.
vite.config.tsDev proxy (routes /api, /socket, etc. to Phoenix), TypeScript resolver, test setup.
tsconfig.jsonTypeScript compiler options. Strict mode enabled.

Server-Level

FilePurpose
mix.exsProject definition. Lists dependencies (Phoenix, Ecto, Presence, Swoosh, etc.).
config/dev.exsDev settings: HTTP port (4000), database connection, live reload.
config/runtime.exsReads environment variables at runtime. Configure production secrets here (DATABASE_URL, SECRET_KEY_BASE).

Build Outputs (Ignored by Git)

These directories are auto-generated. Don’t edit files in them directly.

Client

  • client/.svelte-kit/ — SvelteKit build cache
  • client/node_modules/ — npm dependencies
  • client/src/lib/models/generated/ — Auto-generated model components (from make models.build)
  • client/static/models/ — Optimized GLB files copied from server/priv/static/obj/

Server

  • server/_build/ — Compiled Elixir bytecode (BEAM files)
  • server/deps/ — Hex dependencies (Phoenix, Ecto, etc.)
  • server/priv/static/app/ — SvelteKit build output (served at /app/*)
  • server/priv/static/assets/ — Tailwind/esbuild output for auth pages

Common Workflows

Adding a New Feature

  1. Server-side state:

    • Create GenServer in server/lib/tostada/ (e.g., game_server.ex)
    • Add to supervision tree in application.ex
    • Create channel module in server/lib/tostada_web/channels/
    • Register channel topic in user_socket.ex
  2. Client-side integration:

    • Create store file in client/src/lib/stores/ (e.g., game.ts)
    • Create connection module in client/src/lib/ (e.g., game.ts)
    • Update or create scene components in client/src/routes/
  3. Test:

    • Run make dev to start both servers
    • Open http://localhost:5173 for client with hot reload
    • Phoenix runs on http://localhost:4000 (API + WebSocket)

Adding Database Tables

  1. Generate migration: cd server && mix ecto.gen.migration create_games
  2. Edit migration file in priv/repo/migrations/
  3. Run migration: make db.migrate
  4. Create schema module in lib/tostada/ (e.g., games/game.ex)

Adding 3D Models

  1. Place .gltf or .glb files in server/priv/static/obj/

  2. Run make models.build

  3. Import generated components:

    <script>
    import { DuckModel } from '$lib/models/generated';
    </script>
    
    <DuckModel position={[0, 0, 0]} />

Next Steps

Now that you understand the project structure:


Questions about file organization? Check the tostada GitHub repo for the latest structure.