SvelteKit + Skeleton v4 + Fastify + MongoDB, containerized with Docker.
The Web/ directory is the scaffold.
See Web/CLAUDE.md for the full project reference.
| Layer | Package | Version |
|---|---|---|
| Frontend | SvelteKit + Svelte | 2 / 5 |
| Components | Skeleton | v4 |
| CSS | Tailwind | v4 |
| API | Fastify | v5 |
| Database | MongoDB | 7 |
| Auth | @fastify/session + bcryptjs | — |
| Nodemailer (Ethereal dev / SMTP prod) | — | |
| Container | Docker Compose | — |
- Node.js 22+
- Docker Desktop (WSL 2 backend on Windows)
cd Web
cp .env.Web .env
# Edit .env — set SESSION_SECRET at minimum
cd frontend && npm install && cd ..
cd api && npm install && cd ..docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build- Frontend: http://localhost:3000
- API: http://localhost:4000
- MongoDB: localhost:27017
Windows: Bind mounts don't propagate FS events reliably. API changes always require a rebuild; Svelte changes often do too. Never rely on hot-reload for server-side files.
docker compose -f docker-compose.yml -f docker-compose.dev.yml build api
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d apidocker compose up --build -ddocker compose down # stop, keep volumes
docker compose down -v # stop, delete volumes- Auth — login, logout, session management, profile edit, password reset (forgot-password → email token → reset page)
- RBAC — roles + permissions in MongoDB, enforced on API and frontend; Manage Users and Roles admin pages
- Messaging — in-app email-style messaging with threads, replies, inbox/sent/archive, unread badge, Tiptap rich-text editor
- Settings — admin-only key/value config store;
GET /settings,PATCH /settings/:key; typed inputs (string/boolean/number/select) - App branding —
app.namesetting updates the header and browser title in real time; admins can upload a custom logo image to replace the default SVG icon - Audit log — fire-and-forget
logAudit()helper; events across auth/users/roles/messages/settings;GET /auditwith pagination - Dashboard — placeholder with pure-SVG charts
- Chat assistant — Ollama-backed fixed panel (
OLLAMA_URL=http://host.docker.internal:11434); can be disabled viachat.enabledsetting - Theme toggle — dark/light, persisted to localStorage
example/ # The scaffold — clone this for new projects
├── docker-compose.yml
├── docker-compose.dev.yml
├── .env.example
├── CLAUDE.md # Full project reference for AI-assisted dev
├── frontend/ # SvelteKit app — port 3000
│ └── src/
│ ├── routes/
│ │ ├── (marketing)/ # Public marketing pages
│ │ ├── api/ # SvelteKit → Fastify proxy routes
│ │ ├── dashboard/
│ │ ├── messages/
│ │ ├── settings/ # Admin: app configuration
│ │ ├── users/ # Admin: Manage Users
│ │ ├── roles/ # Admin: Manage Roles
│ │ ├── login/
│ │ ├── forgot-password/
│ │ └── reset-password/
│ └── lib/
│ ├── components/ # Shared UI components
│ ├── config/
│ │ ├── logo.ts # Brand name / logo
│ │ └── nav.ts # Sidebar nav items (module-extensible)
│ └── permissions.ts # hasPermission(user, resource, action)
└── api/ # Fastify app — port 4000
└── src/
├── server.ts # Entry point; routes auto-loaded from routes/
├── plugins/ # session, MongoDB, CORS, seed
├── routes/ # One subdirectory per resource (autoloaded)
├── data/
│ └── permissions.json # Default role permissions (module-extensible)
└── lib/ # checkDuplicateUser, email, audit helpers
modules/ # Build-time feature modules
├── commerce/ # E-commerce: products, categories, orders, inventory
│ ├── module.json
│ ├── api/src/routes/commerce/
│ └── frontend/src/routes/commerce/
└── notifications/ # Example stub module
├── module.json
├── api/src/routes/notifications/
└── frontend/src/routes/notifications/
arch.js # Project scaffold CLI (see below)
New projects are created from the scaffold with arch.js:
node arch.js create my-app # scaffold only
node arch.js create my-app --modules notifications # with modules
node arch.js create my-app --modules a,b,c # multiple modules
node arch.js list # available modules
node arch.js info notifications # module manifestarch.js create copies example/ to projects/<name>/, updates the MongoDB database name, and for each module:
- Copies route and frontend files (collision-checked before any write)
- Appends nav entries to
frontend/src/lib/config/nav.ts - Merges resource permissions into
api/src/data/permissions.json - Merges
dependenciesinto bothpackage.jsonfiles - Appends env vars to
.envand.env.example
A module is a directory under modules/ containing module.json and optional api/ / frontend/ subtrees that mirror the scaffold layout exactly.
{
"name": "my-feature",
"description": "Short description",
"nav": [{ "label": "My Feature", "href": "/my-feature", "icon": "Star", "permission": "my_feature.read" }],
"permissions": [{ "resource": "my_feature", "actions": ["create", "read", "update", "delete"] }],
"dependencies": { "frontend": {}, "api": {} },
"env": [{ "key": "MY_API_KEY", "default": "", "description": "Required for My Feature" }]
}Icons must be valid lucide-svelte component names. The permission field is "resource.action" (dot-separated).
| Variable | Default | Notes |
|---|---|---|
SESSION_SECRET |
— | Required. 64-char hex. |
MONGO_DB |
appdb |
|
WEB_PORT |
3000 |
|
API_PORT |
4000 |
|
SMTP_HOST |
(blank) | Blank → Ethereal auto-provisioned in dev |
SMTP_PORT |
587 |
|
SMTP_USER |
— | |
SMTP_PASS |
— | |
SMTP_FROM |
Architectonic <noreply@example.com> |
|
APP_URL |
http://localhost:3000 |
Used in password reset links |
OLLAMA_URL |
http://host.docker.internal:11434 |
Dev overlay only |
Generate SESSION_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"- Use Docker Desktop with the WSL 2 backend.
- Line endings:
git config --global core.autocrlf inputbefore cloning.
- Docker Desktop works out of the box.
- Apple Silicon (M1/M2/M3/M4): all images are multi-arch.
| Package | URL |
|---|---|
| SvelteKit | https://svelte.dev/docs/kit |
| Svelte 5 | https://svelte.dev/docs/svelte |
| Skeleton v4 | https://skeleton.dev |
| Tailwind v4 | https://tailwindcss.com/docs |
| Fastify v5 | https://fastify.dev/docs |
| Nodemailer | https://nodemailer.com |
Build a custom theme: https://themes.skeleton.dev/themes/create — place the CSS file in frontend/ and import it in app.css.