Skip to content

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 like run, install, and livekit.

/config

  • config.go: Defines the Config struct and loads settings from config.yaml or 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 the bedrud install command 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 in server/ui.go. Static files are served directly from memory using Fiber's filesystem middleware.
  • LiveKit Server: Since LiveKit is a complex Go application itself, we embed the pre-compiled livekit-server executable in internal/livekit/bin/. At runtime, Bedrud extracts this binary to /tmp/bedrud-livekit-server and 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 /livekit is intercepted in internal/server/server.go.
  • A Reverse Proxy (using httputil.NewSingleHostReverseProxy) forwards these requests to the internal LiveKit instance (usually running on 127.0.0.1:7880).
  • The proxy strips the /livekit prefix 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:

claims := c.Locals("user").(*auth.Claims)
userID := claims.UserID

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).