Backend Code Structure¶
Bedrud uses a clean and organized folder structure. Here is a map of the server/ directory to help you find your way.
Project Roadmap & Tree¶
Bedrud is structured as a mono-repo. The server/ directory is the heart of the "Appliance", while apps/ contains the various client-side implementations.
bedrud/
├── apps/ # Client Applications
│ ├── web/ # Svelte 5 Frontend (Source)
│ ├── android/ # Native Android Application
│ └── ios/ # Native iOS Application
├── server/ # Go Backend (The "Appliance")
│ ├── cmd/ # CLI Entry points (run, install)
│ ├── internal/ # Private application code
│ │ ├── auth/ # JWT, Passkeys, OAuth logic
│ │ ├── handlers/ # HTTP Controllers
│ │ ├── models/ # Database Schemas
│ │ ├── repository/ # GORM Data Access Layer
│ │ └── livekit/ # Media Server Management
│ ├── frontend/ # (Generated) Compiled web assets
│ ├── config.yaml # Configuration template
│ └── Makefile # Build & Deploy automation
└── docs/ # System Documentation (MkDocs)
Directory Map¶
/cmd/bedrud¶
main.go: The entry point of the application. It handles CLI commands likerun,install, andlivekit.
/config¶
config.go: Defines theConfigstruct and loads settings fromconfig.yamlor Environment Variables.livekit.yaml: Default configuration for the embedded LiveKit server.
/internal¶
This is where the core logic lives.
/auth: contains logic for JWT generation, OAuth providers, and Passkey (WebAuthn) registration/login./database: manages the connection to SQLite or PostgreSQL and runs migrations./handlers: Fiber HTTP handlers. This is where you find the logic for each API endpoint (e.g.,auth_handler.go,room_handler.go)./models: GORM database models. These files define the tables in your database./repository: The "Data Access Layer". Instead of writing DB queries in handlers, we put them here to keep the code clean./livekit: Integration with the LiveKit Go SDK. It manages room creation and generates "Join Tokens" for participants./middleware: Custom Fiber middleware for authentication checks, logging, and CORS./server: The glue code that brings everything together. It initializes the database, repositories, and starts the Fiber server./install: Logic for thebedrud installcommand which automates systemd and TLS setup on Linux./scheduler: Background tasks (if any)./utils: Small helper functions (e.g., password hashing, random strings).
/migrations¶
- contains SQL or Go files for database schema updates.
/docs (within server)¶
- contains Swagger/OpenAPI documentation files (generated by
swag).
Technical Deep Dive¶
1. Binary Embedding (The "Trick")¶
Bedrud uses the //go:embed directive to bundle files into the compiled binary.
- Frontend: The entire Svelte
dist/folder is embedded inserver/ui.go. Static files are served directly from memory using Fiber'sfilesystemmiddleware. - LiveKit Server: Since LiveKit is a complex Go application itself, we embed the pre-compiled
livekit-serverexecutable ininternal/livekit/bin/. At runtime, Bedrud extracts this binary to/tmp/bedrud-livekit-serverand launches it as a background process (internal/livekit/server.go).
2. LiveKit Reverse Proxy¶
To avoid opening multiple ports (signaling, API, etc.), Bedrud routes all LiveKit signaling traffic through its main HTTP(S) port.
- Any request starting with
/livekitis intercepted ininternal/server/server.go. - A Reverse Proxy (using
httputil.NewSingleHostReverseProxy) forwards these requests to the internal LiveKit instance (usually running on127.0.0.1:7880). - The proxy strips the
/livekitprefix before forwarding, allowing LiveKit to function as if it were the root application.
3. Middleware Context & Locals¶
The backend uses Fiber's .Locals to pass data between middleware and handlers.
- Auth Middleware (internal/middleware/auth.go): Validates the JWT and stores the Claims object in c.Locals("user").
- Handlers: Can access the current user's ID and permissions using:
4. Configuration Overrides¶
While Bedrud uses a config.yaml file, almost every setting can be overridden using Environment Variables. This is essential for Docker and CI/CD environments.
| Variable | Description |
|---|---|
SERVER_PORT |
The port the backend listens on (default: 8090). |
SERVER_ENABLE_TLS |
Boolean (true/false) to enable HTTPS. |
SERVER_DOMAIN |
Your production domain (used for ACME and Passkey RP ID). |
DB_TYPE |
sqlite or postgres. |
DB_PATH |
Path to the .db file (if using SQLite). |
LIVEKIT_HOST |
The public URL for LiveKit (e.g., https://meet.example.com/livekit). |
LIVEKIT_API_KEY |
Key for LiveKit authentication. |
JWT_SECRET |
Secret key used to sign Access Tokens. |
Coding Standards and Patterns¶
To keep the backend maintainable, we follow these patterns:
1. Repository Pattern¶
Handlers should not talk to the database directly. Use a Repository. This makes the code easier to test and allows us to change database logic without touching the API handlers.
2. Standardized Error Handling¶
API handlers should return clear error messages.
- Use c.Status(fiber.StatusBadRequest).JSON(...) for validation errors.
- Use c.Status(fiber.StatusUnauthorized).JSON(...) for auth errors.
- Use c.Status(fiber.StatusInternalServerError).JSON(...) for database or server errors.
3. Structured Logging¶
We use Zerolog for logging.
- log.Info(): For important events (e.g., server started).
- log.Error(): For failures.
- log.Debug(): For detailed development information.
Avoid using fmt.Println for logging in core logic.
4. Naming Conventions¶
- Files: snake_case (e.g.,
user_handler.go). - Structs/Functions: PascalCase (e.g.,
GetUserByEmail). - Variables: camelCase (e.g.,
hashedPassword).