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
| File | Purpose |
|---|---|
src/lib/socket.ts | Exports connectSocket(), pingSocket(), and stores (socketStatus, lastSocketError). The foundation for all real-time features. |
src/routes/+page.svelte | The main SPA page. Contains a full-viewport <Canvas> with the 3D scene and auth navigation. |
src/routes/Scene.svelte | Default Threlte scene with an animated box. This is where you build your 3D game world. |
src/app.css | Tailwind CSS imports. Customize theme here or add global styles. |
vite.config.ts | Dev proxy configuration. Routes /api, /users, /socket, /live, and /assets to Phoenix during development. |
svelte.config.js | SvelteKit 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.sveltefiles insrc/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
| File | Purpose |
|---|---|
lib/tostada/application.ex | Supervision tree. Add your GameServer here as a child process. |
lib/tostada_web/router.ex | Defines all HTTP routes. Check here to see available endpoints. |
lib/tostada_web/endpoint.ex | Configures socket mounts (/socket for UserSocket, /live for LiveView). |
lib/tostada_web/channels/user_socket.ex | WebSocket authentication. Supports both Phoenix.Token (native clients) and session cookies (web). Add new channel topics here. |
lib/tostada_web/channels/app_channel.ex | Starting point for game channels. Extend this or create new channel modules. |
lib/tostada_web/presence.ex | Phoenix.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 inuser_socket.ex - REST API endpoints: Add controllers to
lib/tostada_web/controllers/api/and routes torouter.ex - Database tables: Generate migrations with
mix ecto.gen.migration create_games, edit inpriv/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 | Script | Purpose |
|---|---|
build-models.sh | Converts .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.sh | Run once after scaffolding a new project. Sets up database, installs dependencies, builds client. |
Configuration Files
Root-Level
| File | Purpose |
|---|---|
Makefile | Development commands. Run make help to see all available targets. This is your primary interface for dev/build/test. |
.gitignore | Tells Git to ignore node_modules/, deps/, _build/, etc. |
Client-Level
| File | Purpose |
|---|---|
package.json | npm dependencies (SvelteKit, Threlte, Three.js, Phoenix.js) and scripts. |
svelte.config.js | SvelteKit adapter-static, builds to Phoenix’s static directory. |
vite.config.ts | Dev proxy (routes /api, /socket, etc. to Phoenix), TypeScript resolver, test setup. |
tsconfig.json | TypeScript compiler options. Strict mode enabled. |
Server-Level
| File | Purpose |
|---|---|
mix.exs | Project definition. Lists dependencies (Phoenix, Ecto, Presence, Swoosh, etc.). |
config/dev.exs | Dev settings: HTTP port (4000), database connection, live reload. |
config/runtime.exs | Reads 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 cacheclient/node_modules/— npm dependenciesclient/src/lib/models/generated/— Auto-generated model components (frommake models.build)client/static/models/— Optimized GLB files copied fromserver/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
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
- Create GenServer in
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/
- Create store file in
Test:
- Run
make devto start both servers - Open
http://localhost:5173for client with hot reload - Phoenix runs on
http://localhost:4000(API + WebSocket)
- Run
Adding Database Tables
- Generate migration:
cd server && mix ecto.gen.migration create_games - Edit migration file in
priv/repo/migrations/ - Run migration:
make db.migrate - Create schema module in
lib/tostada/(e.g.,games/game.ex)
Adding 3D Models
Place
.gltfor.glbfiles inserver/priv/static/obj/Run
make models.buildImport generated components:
<script> import { DuckModel } from '$lib/models/generated'; </script> <DuckModel position={[0, 0, 0]} />
Next Steps
Now that you understand the project structure:
- Read the Makefile Reference to learn all available commands
- Explore the Channels + Stores Guide to start building multiplayer features
- Check the Auth System Reference to understand user authentication
Questions about file organization? Check the tostada GitHub repo for the latest structure.