Sambike Fullstack — Architecture (Detailed)
This document describes the full architecture of the Sambike bike-sharing fullstack
project: technologies, languages, tools, data flow, API structure, database schema, and
operational details.
Table of Contents
1. High-Level Overview
2. Traffic & Request Flow
3. Languages & Tooling
4. Backend (Detailed)
5. Database & Entities
6. API Structure
7. WebSocket (Real-Time)
8. Authentication & Authorization
9. Frontend (Detailed)
10. DevOps & Infrastructure
11. Configuration & Environment
12. Repo Layout
1. High-Level Overview
Layer Technology Purpose
React 19 + Vite 7 + Web app (rider, station staff, admin)
Frontend TypeScript
NestJS 11 + TypeScript REST API, WebSockets, business
Backend logic
Database PostgreSQL 16 Persistent data
Reverse Caddy 2 HTTPS, routing, static assets,
proxy WebSocket
Orchestration Docker Compose Run all services (prod + dev override)
External services: Chapa (payments), AfroMessage (SMS), FAYDA (identity/auth),
Cloudinary (media).
2. Traffic & Request Flow
2.1 Client → Caddy → Services
┌─────────────┐
│ Client │
│ (Browser) │
└──────┬──────┘
│
HTTP/HTTPS (80/443)
│
┌──────▼──────┐
│ Caddy │ Reverse proxy, TLS, routing
│ ([Link])
└──────┬──────┘
│
┌─────────────────┼─────────────────┐
│ │ │
/health /api/* /[Link]/*
/api/docs (REST) /bike-sharing/*
│ │ │
│ ┌────▼────┐ ┌────▼────┐
│ │ Backend │ │ Backend │
│ │ :3000 │ │ :3000 │
│ └────┬────┘ └─────────┘
│ │
│ PostgreSQL :5432
│ │
/assets/* (postgres)
/* (SPA)
│
┌────▼────┐
│Frontend │
│ :5173 │
└─────────┘
2.2 Caddy Routing Priority (first match wins)
1. /health — Returns 200 OK (health checks).
2. /api/* — Proxied to backend:3000 (REST API).
3. /[Link]/* — Proxied to backend ([Link] transport).
4. /bike-sharing/* — Rewritten to /[Link]{path} and proxied to backend
(WebSocket namespace path).
5. /assets/* — Proxied to frontend:5173 (static assets).
6. /* (catch-all) — Proxied to frontend:5173 (SPA).
Security headers (HSTS, X-Frame-Options, X-XSS-Protection, X-Content-Type-Options,
Referrer-Policy, CSP) and gzip encoding are applied at the Caddy level.
3. Languages & Tooling
Language Where used Version / notes
Backend src/, frontend src/, backend TS 5.8–5.9 (backend 5.9,
TypeScript test/ frontend 5.8)
Config files (Vite, ESLint, Jest), build
JavaScript output ES modules
TypeORM migrations, raw queries (if
SQL PostgreSQL dialect
any)
Bash/Shell [Link], [Link] POSIX sh where possible
Package managers: pnpm (backend and frontend). [Link]: 20 (Alpine in Docker).
4. Backend (Detailed)
4.1 Framework & Runtime
NestJS 11 — Modular structure, dependency injection, guards, interceptors, pipes,
filters.
Express — HTTP server via @nestjs/platform-express.
[Link] 20 — Runtime (Alpine in Docker).
pnpm — Lockfile [Link]; packageManager: "pnpm@10.15.0" in backend.
4.2 Global Application Behavior
Global prefix: All HTTP routes use api/v1 except the root GET / (status).
Validation: ValidationPipe with transform: true, whitelist: true,
forbidNonWhitelisted: true (DTOs with class-validator ).
Response shape: All responses go through:
LoggingInterceptor — Request/response logging.
ResponseWrapperInterceptor — Consistent envelope (e.g. { data, message
} ).
SerializationInterceptor — class-transformer serialization.
Errors: ResponseExceptionFilter — Standardized error payloads.
CORS: origin: true, methods GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,
credentials: true .
Header: X-Powered-By: SamBikes (Express default disabled, then set explicitly).
4.3 API Documentation
Swagger/OpenAPI — @nestjs/swagger; available only when NODE_ENV !==
"production" .
Docs URL: GET /api/docs (UI at /api/docs).
Config: Title “Bike Sharing System API”, Bearer auth, version 1.0.0; description
includes WebSocket connection example (namespace, auth header).
4.4 Database & ORM
PostgreSQL 16 — Image postgres:16-alpine; port 5432; healthcheck via
pg_isready .
TypeORM 0.3 — Entities, migrations, repositories. synchronize: false; migrations
only.
Connection: Prefer DATABASE_URL; fallback to DB_HOST, DB_PORT, DB_USERNAME,
DB_PASSWORD , DB_DATABASE . Pool: max 10, min 1, application_name: "sambike" .
Migrations: CLI via typeorm-ts-node-commonjs; config
src/config/[Link] . Scripts: migration:generate , migration:run ,
migration:run:prod , migration:revert , migration:show , migration:create .
Seeders: Custom scripts in src/seeders/ — seed:admin, seed:station,
seed:bike , seed:ride , seed:staff , seed:all ; run via ts-node with tsconfig-
paths .
4.5 Logging
Winston via nest-winston — App logger replaces default Nest logger.
Config: createWinstonConfig in src/config/[Link]:
Level: LOG_LEVEL (default: debug in development, info otherwise).
Console: Always; development uses colorized printf, production uses JSON-style.
File: When LOG_FILE_ENABLED=true or in production: logs/[Link] (error),
logs/[Link] (all), logs/[Link] (info). Rotate by LOG_MAX_SIZE (default
5MB), LOG_MAX_FILES (default 5).
4.6 Rate Limiting (Throttler)
@nestjs/throttler — Global ThrottlerGuard; named throttlers from
getThrottlerConfig :
Name Development (per IP) Production (env-driven)
default 1000 / 1 min THROTTLE_LIMIT / THROTTLE_TTL
THROTTLE_AUTH_LIMIT /
auth 50 / 1 min
THROTTLE_AUTH_TTL
THROTTLE_PAYMENT_LIMIT /
payment 100 / 1 min
THROTTLE_PAYMENT_TTL
THROTTLE_ADMIN_LIMIT /
admin 2000 / 1 min
THROTTLE_ADMIN_TTL
Controllers can override with @Throttle() / @SkipThrottle().
4.7 Backend Feature Modules & Controllers
Module Controller path(s) Purpose
API status, version,
Root GET / (no prefix) uptime,
environment
Login, register,
Auth api/v1/auth JWT, FAYDA,
password reset
User management
Users api/v1/users
(admin)
Riders api/v1/riders
Rider profile, wallet,
ride history
CRUD stations
Stations api/v1/stations (admin); transfers,
income
Public station list,
Public stations api/v1/public/stations search, nearby (no
auth)
Bike CRUD, status,
Bikes api/v1/bikes
assignment
Start/end ride,
Rides api/v1/rides history, export
(e.g. Excel)
Return code
Return codes (under ride module)
generation/validation
Pricing
Pricing api/v1/pricing-configs
configuration
Dashboard stats
Dashboard api/v1/dashboard
(role-based)
Chapa, webhook,
api/v1/payments , payments/tracking ,
Payments manual payments,
payments/webhook , manual-payments
tracking
Receipts for
Receipts api/v1/receipts
payments
Income analytics api/v1/income-analytics Income analytics
System config api/v1/admin/geofence , admin/gps
Geofence and GPS
config
Testimonials and
Testimonials api/v1/testimonials , api/v1/faq
FAQ
Contact api/v1/contact
Contact form
messages
Founders api/v1/founders Founders content
Products api/v1/products Products
Blog api/v1/blog
Blog
posts/categories
Notification log /
Notifications api/v1/notifications
preferences
Image
Cloudinary (used by other modules)
upload/storage
4.8 Caching & Scheduling
Cache: @nestjs/cache-manager + cache-manager — Used where needed
(e.g. config, sessions).
Scheduling: @nestjs/schedule — Cron/interval jobs if defined in services.
4.9 Testing (Backend)
Jest — Unit and E2E.
Supertest — HTTP E2E against the app.
E2E DB: SQLite or test PostgreSQL via [Link] /
test/[Link] .
Scripts: test, test:unit, test:integration, test:e2e, test:e2e:watch,
test:cov , test:all , test:ci .
E2E specs: test/*.[Link] (auth, bike, dashboard, notification, payment, ride,
rider, station, etc.).
4.10 Backend Docker Build & Entrypoint
Dockerfile: Multi-stage — base (Node 20 Alpine, pnpm) → dependencies → build
(nest build) → production (prod deps + dist). Migrations/seeders need src/config,
src/migrations , src/entities , src/seeders , src/auth/[Link] ; dev
deps ts-node, typescript, tsconfig-paths, @types/node added for running them.
Entrypoint: [Link] — Wait for Postgres (pg_isready), run pnpm
migration:run:prod , run pnpm run seed:admin , then exec node dist/[Link]
(or the path used in the image).
Healthcheck: HTTP GET to backend root; expect 200.
5. Database & Entities
5.1 Entity List (TypeORM)
Entity Table Purpose
Accounts: email, password, role,
User users
FAYDA fields, reset token
Rider profile; wallet balance; 1:1
Rider riders
User
Name, address, lat/lng, geofence
Station stations
radius, active
qrCode, gpsTrackerId, status,
Bike bikes
currentStation, battery, position
rider, bike, start/end time,
Ride rides stations, distance, duration,
status, amount
ride, amount, method
(CASH/CHAPA), subMethod
Payment payments
(TELEBIRR/MPESA/CBE_BIRR),
status, manual approval
PaymentReceipt payment_receipts Receipts linked to payments
PricingConfig (pricing) Pricing rules
StationReturnCode (return codes) Return codes for bike returns
AuditLog audit_logs Audit trail
NotificationLog notification_logs Notifications sent
System-wide config
SystemConfig (config)
(e.g. geofence, GPS)
ContactMessage (contact) Contact form submissions
Testimonial, FAQ (testimonials) Testimonials and FAQs
Founder, Product (content) Founders, products
BlogPost, BlogCategory (blog) Blog content
5.2 Core Entity Details
User: id (UUID), name, nationalId, phone, email, password (excluded in
serialization), isVerified, role (enum: rider | station_staff | administrator),
assignedStationId , FAYDA fields (faydaUin , faydaPhoto , faydaQrCode ,
faydaUserData , isFaydaVerified ), passwordResetToken ,
passwordResetExpires , timestamps. 1:1 Rider.
Rider: id, userId, walletBalance, isActive, timestamps. 1:1 User; 1:N Rides.
Station: id, name, address, latitude, longitude, geofenceRadius, isActive,
timestamps. 1:N Bikes; 1:N Rides (start/end).
Bike: id, qrCode, gpsTrackerId, status (AVAILABLE | IN_USE |
MAINTENANCE), currentStationId, currentLatitude, currentLongitude,
batteryLevel , lastMaintenanceAt , timestamps. N:1 Station; 1:N Rides.
Ride: id, riderId, bikeId, startTime, endTime, distanceKm, durationMinutes,
status (ONGOING | COMPLETED | CANCELLED), startStationId ,
endStationId , lastActivityAt , totalAmount , timestamps. N:1 Rider, Bike,
startStation, endStation; 1:N Payments.
Payment: id, rideId, amount, method (CASH | CHAPA), subMethod, status
(PENDING | COMPLETED | FAILED | REFUNDED), transactionId,
externalTransactionId , failureReason , approvedBy , approvedAt ,
isManualPayment , timestamps.
6. API Structure
Base URL (via Caddy): [Link] → backend; versioned
under api/v1.
Full base: [Link]
Auth: Bearer JWT in Authorization header for protected routes. Some routes
(e.g. public/stations, auth/login, auth/register) are public.
Response envelope: Handled by interceptors (e.g. data, message). Errors from
ResponseExceptionFilter .
Docs: GET /api/docs (Swagger UI) when not in production.
7. WebSocket (Real-Time)
7.1 Transport & Namespace
Library: [Link] (server: @nestjs/[Link], [Link]; client:
[Link]-client ).
Namespace: WS_NAMESPACE (default /bike-sharing). Caddy exposes both
/[Link]/* and /bike-sharing/* (rewritten to [Link] path) to the backend.
7.2 Connection & Auth
Client must send a valid JWT: either Authorization: Bearer <token> in handshake
headers or [Link] in handshake auth object. Gateway verifies JWT via
JwtService and loads user via AuthService ; on failure it disconnects the client.
On success: [Link], [Link] set; client joins room user:
<userId> . If role is station_staff and assignedStationId is set, client also joins
station:<stationId> .
7.3 Server → Client Events
Event When / purpose
connected
Right after successful connection
(clientId, userId, timestamp).
Response to client ping
pong
(timestamp).
Ride started (emitted to rider via
ride:started
emitToUser ).
ride:ended Ride ended (emitted to rider).
ride:cancelled Ride cancelled (emitted to rider).
Payment approved (emitted to
payment:approved
rider).
Cash payment pending (broadcast
cash_payment_pending
to station room).
Cash payment approved (e.g. to
cash_payment_approved
rider or station).
7.4 Client → Server Messages
ping — Server responds with pong .
7.5 WebSocket Service (Backend)
WebSocketService : attachServer(server) , broadcastToRoom(room, event,
data) , emitToUser(userId, event, data) , emitToRole(role, event, data) ,
getConnectionCount() . Used by Ride and Payment services to push real-time
updates.
7.6 Frontend WebSocket Client
File: frontend/src/services/[Link].
Connection: Uses VITE_API_BASE_URL (origin) and namespace /bike-sharing;
JWT from [Link]("access_token") in Authorization header;
transports ['websocket'], reconnection enabled.
Listeners: payment:approved, payment:failed, cash_payment_pending,
cash_payment_approved , ride:started , ride:ended ; with matching off* methods
to remove listeners.
8. Authentication & Authorization
8.1 User Roles
rider — End user; rides, wallet, profile.
station_staff — Assigned to a station; can manage returns, manual payments, station-
scoped views; joins WebSocket room station:<id>.
administrator — Full admin; users, stations, bikes, rides, payments, config, etc.
8.2 Auth Mechanisms
JWT — Issued on login; used in Authorization: Bearer <token> for REST and
WebSocket. Config: JWT_SECRET, JWT_EXPIRES_IN.
Local strategy — Email/password login; password hashed with bcrypt.
FAYDA — External identity/verification; fayda-auth-sdk and utils in src/utils/;
user fields faydaUin, faydaPhoto, faydaQrCode, faydaUserData,
isFaydaVerified .
Password reset — OTP (6-digit) and expiry stored on User; likely used with
AfroMessage for SMS.
8.3 Request-Scoped Helpers
Express app holds RiderRepository and RideRepository for decorators such as
CurrentRider and ValidateRideOwnership to resolve current rider/ride from JWT
and DB.
9. Frontend (Detailed)
9.1 Stack
React 19 — UI.
Vite 7 — Dev server (port 5173, host: "[Link]"), build; path alias @ → src.
TypeScript — [Link], [Link], [Link].
pnpm — Package manager.
9.2 Styling & UI
Tailwind CSS 4 — @tailwindcss/vite in Vite config.
shadcn/ui — [Link]; Radix-based primitives: accordion, avatar, badge,
breadcrumbs, button, card, chart, checkbox, dialog, dropdown-menu, form, input,
label, navigation-menu, progress, select, separator, sheet, table, tabs, textarea, toast.
Radix UI — Accordion, checkbox, dialog, dropdown, label, navigation, select,
separator, slot, tabs.
Icons: Lucide React, Tabler Icons.
Motion — motion (e.g. AnimatePresence) for page transitions.
Recharts — Dashboard/analytics charts.
Sonner — Toasts. class-variance-authority, clsx, tailwind-merge — Component
variants and class names.
9.3 Data & Forms
React Router 7 — BrowserRouter, Routes, Route, useLocation;
AnimatePresence with location for transitions.
Axios — HTTP client to VITE_API_BASE_URL (e.g. /api/v1).
React Hook Form + Zod + @hookform/resolvers — Form state and validation.
[Link] client — Real-time via [Link].
9.4 Maps
Google Maps — @googlemaps/js-api-loader; VITE_GOOGLE_MAPS_API_KEY; types
@types/[Link] . Used in rider map, station picker, etc.
9.5 Routes (Frontend)
Path Page / content
Home: Hero, Stations section, How it works, Key features,
/
FAQ, CTA, Footer
/team TeamPage + Footer
/contact ContactPage + Footer
/how-it-works How it works content + Footer
/register RegisterPage + Footer
/login LoginPage + Footer
/forgot-password ForgotPasswordPage + Footer
/profile ProfilePage
/dashboard DashboardPage (role-based)
/dashboard/stations StationsPage
/dashboard/bikes BikesPage
/dashboard/staff StaffPage
/dashboard/rides RidesPage
/dashboard/income-
IncomeAnalyticsPage
analytics
/dashboard/payments PaymentPage
/dashboard/payment-
PaymentManagementPage
management
/dashboard/staff-
StaffBikesPage
bikes
/dashboard/staff-
StaffPaymentPage
payments
/dashboard/staff-
StaffPaymentManagementPage
payment-management
/dashboard/staff-
StaffIncomeAnalyticsPage
income-analytics
* 404 + link to home
9.6 Key Frontend Structure
pages/ — One component per route (e.g. LoginPage, RegisterPage, DashboardPage,
StationsPage, BikesPage, RidesPage, PaymentPage, ProfilePage, Staff* pages, etc.).
components/ — Shared and feature components: Navbar, HeroSection,
HowItWorksSection, KeyFeaturesSection, FAQSection, CTASection, SiteFooter,
StationsSection; dashboard (AdminDashboard, RiderDashboard, StaffDashboard,
StationStaffDashboard, sidebar, metrics, charts, tables); ride flow (ScanUnlockScreen,
PaymentScreen, ActiveRideScreen, AgentCodeScreen, ManualCodeScreen,
StartRideScreen, ReturnCodeModal); station/bike dialogs (StationsAddDialog,
StationsEditDialog, BikeEditDialog, etc.); maps (GoogleMapsRiderMap,
FallbackRiderMap, SimpleRiderMap, MapPicker, StationMarker); forms
(e.g. ChangePasswordDialog, RiderDetailsDialog); ui/ — shadcn primitives.
services/ — API wrappers: usersService, stationsService, bikesService, ridesService,
ridersService, paymentService, dashboardService, incomeAnalyticsService,
publicStationsService, staffBikesService, staffPaymentService,
staffIncomeAnalyticsService, reportsService, websocketService.
hooks/ — e.g. useRideFlow for ride flow state.
types/ — bike, dashboard, incomeAnalytics, payment, profile, report, ride, rideFlow,
staff, station.
lib/ — googleMapsLoader, url, utils (e.g. cn).
9.7 Frontend Docker
Dockerfile: Multi-stage — base → dev (pnpm install, pnpm run dev --host
[Link] ) → build (build args: VITE_API_BASE_URL , VITE_WS_URL ,
VITE_GOOGLE_MAPS_API_KEY , then pnpm run build ) → production (serve static
dist with serve -s dist -l 5173 ). Healthcheck: wget to
[Link] .
10. DevOps & Infrastructure
10.1 Docker Compose Services
Image / Depends
Service Ports (host) Notes
build on
Volumes: postgres_data; init scripts
postgres:16- DB_PORT:-
postgres — from ./init-scripts;
alpine 5432→5432
healthcheck pg_isready.
Env: DB_, JWT_, CHAPA_,
AFROMESSAGE_, FAYDA_,
build postgres LOG_, THROTTLE_*,
backend expose 3000
./backend (healthy) WS_NAMESPACE. Volumes:
./backend/logs. Healthcheck: HTTP
GET root.
Build args: VITE_API_BASE_URL,
build VITE_WS_URL,
frontend expose 5173 backend
./frontend VITE_GOOGLE_MAPS_API_KEY.
Healthcheck: wget :5173.
frontend, Volumes: Caddyfile, caddy_data,
caddy:2- 80, 443,
caddy backend caddy_config. Healthcheck: GET
alpine 443/udp
(healthy) /health.
All on network sambike-network. Restart policy unless-stopped for postgres, backend,
frontend, caddy.
10.2 Dev Override ([Link])
Caddy: Uses [Link]; ports 80 only; CADDY_ADMIN=[Link]:2019.
Backend: Command pnpm start:dev; volumes mount ./backend/src and
./backend/logs ; env overrides for CORS/DB; NODE_OPTIONS: --max-old-space-
size=1024 .
Frontend: Build target dev; volumes mount src, public, [Link], config files;
env VITE_API_BASE_URL, VITE_WS_URL for localhost.
10.3 [Link]
Detects docker-compose or docker compose; with first arg dev/--dev/-d uses
[Link] overlay. Checks Docker and Compose; can create .env
from example; runs compose up (and other commands as scripted). Entry:
./[Link] (prod) or ./[Link] dev (dev).
11. Configuration & Environment
11.1 Environment Variables (Grouped)
Database
DB_HOST , DB_PORT , DB_USERNAME , DB_PASSWORD , DB_DATABASE — PostgreSQL
connection.
DATABASE_URL — Optional full URL (e.g. postgresql://user:pass@host:port/db );
used if set.
Application
NODE_ENV — development | production.
PORT — Backend listen port (default 3000).
JWT
JWT_SECRET — Signing secret.
JWT_EXPIRES_IN — e.g. 30d .
Chapa (payments)
CHAPA_SECRET_KEY , CHAPA_WEBHOOK_SECRET .
AfroMessage (SMS)
AFROMESSAGE_AUTH_TOKEN , AFROMESSAGE_IDENTIFIER_ID ,
AFROMESSAGE_SENDER_NAME .
FAYDA
FAYDA_API_KEY , FAYDA_BASE_URL .
Logging
LOG_LEVEL — e.g. info, debug.
LOG_FILE_ENABLED — true | false.
LOG_MAX_SIZE , LOG_MAX_FILES — File rotation.
Throttle
THROTTLE_TTL , THROTTLE_LIMIT — Default throttler.
THROTTLE_AUTH_TTL , THROTTLE_AUTH_LIMIT .
THROTTLE_PAYMENT_TTL , THROTTLE_PAYMENT_LIMIT (and admin variants if used).
WebSocket
WS_NAMESPACE — e.g. /bike-sharing .
WEBSOCKET_CORS_ORIGINS — Allowed origins for [Link].
Frontend (build-time)
VITE_API_BASE_URL — API origin (e.g. [Link] ).
VITE_WS_URL — WebSocket URL (e.g. [Link] or ws
for dev).
VITE_GOOGLE_MAPS_API_KEY — Google Maps API key.
11.2 Files
Root: .env (from .[Link]); used by Compose and optionally by backend when
run locally.
Backend: backend/[Link]; same vars as needed by backend in Docker.
Frontend: Env vars prefixed with VITE_ in .env or passed as Docker build args.
12. Repo Layout
sambike-fullstack/
├── backend/
│ ├── src/
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── auth/ # Auth, users, JWT, FAYDA
│ │ ├── bike/
│ │ ├── ride/ # Rides, return-code
│ │ ├── rider/
│ │ ├── station/ # [Link], public-
[Link]
│ │ ├── payment/ # Chapa, webhook, manual, tracking, admin
│ │ ├── payment/ # Chapa, webhook, manual, tracking, admin
│ │ ├── dashboard/
│ │ ├── websocket/ # gateway, service
│ │ ├── receipt/
│ │ ├── income-analytics/
│ │ ├── pricing/
│ │ ├── system-config/ # geofence, gps
│ │ ├── notification/
│ │ ├── testimonials/ # testimonials, faq
│ │ ├── contact/
│ │ ├── founders/
│ │ ├── products/
│ │ ├── blog/
│ │ ├── cloudinary/
│ │ ├── entities/
│ │ ├── migrations/
│ │ ├── seeders/
│ │ ├── config/ # database, logger, throttler
│ │ ├── common/ # interceptors, filters, logger
│ │ └── utils/
│ ├── test/ # E2E specs, mocks, setup
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── Dockerfile
│ ├── [Link]
│ └── [Link]
├── frontend/
│ ├── src/
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── pages/
│ │ ├── components/
│ │ │ ├── ui/ # shadcn
│ │ │ ├── dashboard/
│ │ │ ├── rideFlow/
│ │ │ └── ...
│ │ ├── services/
│ │ ├── hooks/
│ │ ├── types/
│ │ ├── lib/
│ │ └── assets/
│ ├── public/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── Dockerfile
│ └── [Link]
├── [Link]
├── [Link]
├── Caddyfile
├── [Link]
├── [Link]
├── .[Link]
└── [Link]
This document is the single detailed reference for the Sambike fullstack architecture, tools,
languages, and behavior. Update it when adding services, routes, or env vars.