원클릭으로
bedrud-server
Full Go backend code map. Load for any server/API/auth/DB/model/handler work.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Full Go backend code map. Load for any server/API/auth/DB/model/handler work.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Admin endpoints — users, rooms, queue, settings, invite tokens, webhooks, overview, recordings.
Auth endpoints — JWT flow, local/OAuth/passkey/verify/password-reset/avatar/preferences.
Room endpoints — CRUD, join, moderation, presence, chat upload, stage stubs, recording.
Complete Bedrud API endpoint reference. Dense index of every route, middleware, status code; deep detail in leaf skills.
All DTO definitions, source file index, Swagger reference.
Auth service + middleware — JWT, passkeys, OAuth, session store, rate limiting, recordings gate.
| name | bedrud-server |
| description | Full Go backend code map. Load for any server/API/auth/DB/model/handler work. |
| license | Apache License |
Go module bedrud. Root: server/. Fiber HTTP + GORM + embedded LiveKit + job queue.
Leaf skills (deeper detail when needed):
| Skill | Scope |
|---|---|
bedrud-http | Entrypoints, bootstrap, handlers, routes, lkutil |
bedrud-auth | AuthService, JWT, middleware, rate limits |
bedrud-data | Models, repos, migrations, testutil |
bedrud-jobs | Queue, scheduler, cleanup, recording service/store, chat storage |
bedrud-realtime | Embedded LiveKit binary lifecycle |
bedrud-ops-cli | Cobra CLI, installer, usercli/roomcli, utils |
bedrud-email-cerberus | Email templates + send_email branding |
cmd/bedrud/main.go — Production CLIThin wrapper → cli.Execute(version) (Cobra in internal/cli/). Legacy flags still work (--run, --livekit, --version).
| Command | Calls | Purpose |
|---|---|---|
run / server | server.Run(configPath, version) | Full app (API + optional embedded LK + SPA) |
livekit / --livekit | livekit.RunLiveKit(configPath) | Standalone LK binary |
install / uninstall | install.LinuxInstall / LinuxUninstall | Linux installer (systemd/OpenRC/SysV) |
user * | usercli.* | create/delete/promote/demote/list/info/password/enable/disable |
room * | roomcli.* | list/info/close/suspend/reactivate/kick |
config / settings / invite-token / cert / db | respective CLI | Ops tooling |
version | Version string |
Config: --config, BEDRUD_CONFIG, or CONFIG_PATH. Management cmds default /etc/bedrud/config.yaml.
cmd/server/main.go — Dev API (Air)No Cobra. Wires subsystems + routes + Swagger/Scalar + SPA. Minor drift vs prod (GET /admin/stats still registered here; prod uses overview). Prefer internal/server/server.go as source of truth.
Health: GET /api/health · Ready: GET /api/ready. Docs: /api/swagger/*, /api/scalar. Disable: DISABLE_API_DOCS.
ui.go — Frontend embed//go:embed all:frontend → UI embed.FS. make build copies apps/web/build/* → server/frontend/.
server/internal/)| Package | Role |
|---|---|
server/ | Prod bootstrap Run(configPath, version) |
handlers/ | HTTP handlers (auth, room, users, admin, recording, webhook, cert, queue) |
auth/ | AuthService, JWT, session store, challenge store, email canonicalize |
middleware/ | Protected, RBAC, CSRF bearer-for-mutations, guest reject, email-verify, rate limit, recordings gate |
models/ | GORM models + admin/overview DTOs |
repository/ | Data access (user, room, passkey, settings, invite, prefs, verification, webhook, recording) |
database/ | GORM init (SQLite/Postgres), AutoMigrate + PG FKs |
queue/ | Job enqueue/worker + 8 handlers + email templates |
scheduler/ | gocron: idle rooms, guests, unverified, tokens, queue cleanup, cert, recording retention |
services/ | RoomCleanupService, RecordingService |
storage/ | Chat upload, avatar, recording store |
livekit/ | Embedded LK binary + temp YAML + process mgmt |
lkutil/ | RoomService/Egress clients, AuthContext, system messages |
cli/ | Cobra command surface |
clioutput/ | --json envelopes |
usercli/ / roomcli/ | CLI domain ops |
install/ | LinuxInstall / LinuxUninstall |
utils/ | TLS certs, SMTP, keys, safeio, net |
testutil/ | In-memory SQLite + LiveKit mocks |
templates/ | Legacy HTML (login/index) — SPA usually embedded |
internal/server/server.go)Run(configPath, version) error sequence:
room, user, recording, …)recordingRepo live; recStore/recCfg currently nil until store wired)Init + hydrate ban set from inactive users*)/livekit when internal/api routes + static uploads + SPAhttpPortRecording bootstrap gap (important): handler, service, repo, store, queue types, and webhook processing are implemented, but prod server.Run still has commented blocks for:
NewRecordingStore / egress client / RecordingService / RecordingHandlerprocess_recording, recording_delete/api/rooms/:id/... and admin recording routesWire same pattern as integration tests when enabling. Jobs enqueued without a registered handler → permanent fail.
All API under Fiber group /api (@BasePath /api).
| Area | Prefix | Notes |
|---|---|---|
| Room lifecycle + moderation | /api/room/... (singular) | create, join, list, :roomId/* |
| Recording | /api/rooms/:id/... (plural) | SHIPPED code; routes still commented in bootstrap |
| Auth | /api/auth/... | register, login, passkeys, verify, preferences, OAuth |
| Admin | /api/admin/... | superadmin group |
| LiveKit webhook | POST /api/livekit/webhook | LK JWT, not app JWT |
| Static | /uploads/chat/* (auth), /uploads/avatars/* (public) | |
| Embedded LK proxy | /livekit/* | internal only |
Admin group middleware: Protected → RequireEmailVerified → RequireAccess(superadmin).
/api/health /api/ready
/api/auth/* register, login, guest-login, refresh, logout
me, me/avatar, password, verify*, forgot/reset
passkey/*, preferences, settings (public)
:provider/login|callback
/api/room/* create, join, guest-join, refresh-token, list, archived
:roomId/{kick,mute,ban,video,promote,demote,chat,deafen,
undeafen,ask,spotlight,screenshare,presence,
participant,stage,settings,chat/upload}
DELETE :roomId → 202 room_delete
/api/rooms/:id/recording* (IMPLEMENTED; bootstrap routes commented)
/api/admin/* users*, rooms*, settings*, invite-tokens, webhooks*
overview, queue, cert-info, online-count, livekit/stats
recordings* (IMPLEMENTED; bootstrap commented)
/api/livekit/webhook
/api/cert
/uploads/chat/* /uploads/avatars/*
/livekit/* (internal LK reverse proxy)
| Handler | Route |
|---|---|
BringToStage | POST /api/room/:roomId/stage/:identity/bring |
RemoveFromStage | POST /api/room/:roomId/stage/:identity/remove |
AdminGenerateToken | POST /api/admin/rooms/:roomId/token |
Repo stage helpers (BringToStage/RemoveFromStage on participants) exist; HTTP layer returns 501.
Do not call email, webhook, or recording “stubs” — they are implemented at handler/service/queue layer.
internal/handlers/)| File | Responsibility |
|---|---|
auth.go | OAuth begin/callback (goth) |
auth_handler.go | Local auth, passkeys, profile/avatar, verify, password reset |
room.go | Room lifecycle, moderation, chat upload, admin room ops |
recording_handler.go | Start/stop/list/get/wait/clear + admin list/bulk delete → RecordingService |
users.go | Admin users, bulk, force-logout, verify, async delete |
admin_handler.go | Settings, invite tokens, webhooks, test email, validate |
preferences_handler.go | User JSON prefs ≤4KB |
admin_overview.go | Aggregated KPIs / health |
admin_queue.go | Queue stats + email diagnostics |
cert_handler.go | Cert PEM download + admin cert-info |
livekit_webhook.go | participant_left, room_finished, egress_started, egress_ended |
cooldown.go / errors.go / room_auth.go / models.go | Shared helpers + DTOs |
| Method | Path | Notes |
|---|---|---|
| POST | /auth/register, /login, /guest-login, /refresh | Rate limited |
| POST | /auth/logout | Protected |
| GET/PUT | /auth/me | Protected + email verified |
| POST/DELETE | /auth/me/avatar | Avatar upload |
| PUT | /auth/password | Change password |
| POST/GET | /auth/verify, /verify/status, /verify/resend | Email verification |
| POST | /auth/forgot-password, /reset-password | Reset flow |
| POST | /auth/passkey/* | Register/login/signup begin+finish |
| GET/PUT | /auth/preferences | Prefs |
| GET | /auth/settings | Public flags (incl. recordingsEnabled) |
| GET | /auth/:provider/login|callback | OAuth |
/room)| Method | Path | Purpose |
|---|---|---|
| POST | /room/create | Create LK + DB |
| POST | /room/join, /guest-join | Join + LK token |
| POST | /room/refresh-token | Refresh LK JWT |
| GET | /room/list, /archived | User rooms / archives |
| POST | /room/:roomId/{kick,mute,ban,...}/:identity | Moderation |
| GET | /room/:roomId/presence | Presence (API rate limit) |
| PUT | /room/:roomId/settings | Settings (incl. recordingsAllowed) |
| DELETE | /room/:roomId | 202 · room_delete (archive, Purge=false) |
| POST | /room/:roomId/chat/upload | Chat image |
/rooms — code ready)| Method | Path | Purpose |
|---|---|---|
| POST | /rooms/:id/recording/start|stop | Egress control · RecordingsEnabled |
| GET | /rooms/:id/recordings, .../:rid, .../:rid/wait | List/get/long-poll |
| DELETE | /rooms/:id/recordings, .../:recordingId | Clear |
| GET | /admin/recordings | Admin list |
| POST | /admin/recordings/bulk/delete | 202 · recording_delete |
Users: list, recent, bulk ban/promote/delete, status, accesses, force-logout, password, detail, sessions, verify, delete.
Rooms admin: list, events, close/suspend/reactivate/update, participants, bulk, livekit stats, online-count.
Settings/webhooks/invite-tokens/overview/queue/cert as in route map.
internal/auth/ + middleware/)Password: HashPassword / VerifyPassword — bcrypt(sha256(password)) with legacy plain-bcrypt fallback.
Flows: Register, Login (email-verify gate), GuestLogin, refresh rotate (RotateRefreshToken), Logout (block refresh + revoke access), profile/avatar/email/password, passkeys (WebAuthn), OAuth Init/ReloadProviders.
jwt.go)GenerateTokenPair); purpose tokens: email_verify, password_reset| Fn | Behavior |
|---|---|
Protected | Bearer or access_token cookie → claims in Locals("user"); ban → 403 |
RequireAccess | Hierarchical: superadmin > admin > moderator > user |
RequireBearerForMutations | CSRF: mutations need Authorization header |
RejectGuest | Block guest provider |
RequireEmailVerified | Always DB EmailVerifiedAt (never trust JWT claim) |
Auth/Resend/Guest/APIRateLimiter | Per-IP Fiber limiters |
RecordingsEnabled | 403 if system flag off |
internal/models/)| Model | Table / notes |
|---|---|
User | email+provider unique; Accesses text[]; EmailVerifiedAt; PasswordChangedAt; refresh stored as SHA-256 |
Room | Settings embedded (settings_* incl. RecordingsAllowed, IsPersistent); LastActivityAt; soft DeletedAt archive; active-name unique partial index |
RoomParticipant / RoomPermissions | Stage, moderator, ban, chat block flags |
Passkey | WebAuthn credentials |
BlockedRefreshToken | Hashed refresh blocklist |
SystemSettings | Singleton ID=1 — registration, OAuth, server, LK, CORS, chat, email, recordings, limits (runtime overlay via GetEffectiveSettings) |
InviteToken | Gated registration |
UserPreferences | JSON blob PK=userID |
ChatUpload | Room/user upload metadata (no dedicated repo) |
Job | Queue rows: pending/active/done/failed |
VerificationEvent | sent/resent/success/failed/admin_force/email_change |
Webhook | Outbound subscriptions; events: room.created/ended, participant.joined, recording.completed, ping |
Recording | LiveKit egress lifecycle: pending→started→processing→completed|failed|deleting |
QueueStats / overview DTOs | Admin API shapes (not tables) |
Relationships: User→Passkeys/BlockedTokens/Prefs/Participants; Room→Participants/Permissions/ChatUploads/Recordings; Job & Webhook standalone; SystemSettings singleton.
internal/repository/)| Repo | Highlights |
|---|---|
UserRepository | Upsert, hashed refresh CAS, cascade delete, admin filters/batch, guest/unverified cleanup |
RoomRepository | Create/join capacity, stage/mod flags, soft/hard delete, admin filters, KPIs/trends |
PasskeyRepository | CRUD + counter advance |
SettingsRepository | Get/Save/GetEffectiveSettings |
InviteTokenRepository | Create/list/mark used/delete |
UserPreferencesRepository | Get/Upsert/Delete |
VerificationEventRepository | Record + recent by user |
WebhookRepository | CRUD, ListActive(event), secret rotate |
RecordingRepository | Optimistic status transitions, list/admin, stale/retention queries |
No SQL migration dir — schema via database.RunMigrations() AutoMigrate + Postgres FKs. Skip: BEDRUD_SKIP_MIGRATE=1.
internal/queue/) — 8 job typesWorker polls jobs. Postgres SKIP LOCKED; SQLite two-step + MaxOpenConns(1). Default poll 500ms, concurrency 1, maxAttempts 3, backoff 2^n * 5s cap 1h. Depth cap 10000.
| Type | Payload | Handler | Status |
|---|---|---|---|
user_delete | UserID, Email, RoomIDs | NewUserDeleteHandler | Registered |
room_delete | RoomID, msgs, Purge | archive (false) or cascade (true) | Registered |
room_suspend | RoomID | SuspendRoom | Registered |
chat_upload_s3 | base64 + meta | Store + tracker | Registered |
send_email | To, Subject, TemplateName, Data | SMTP + Cerberus templates | Registered |
dispatch_webhook | URL, Event, Body, Secret | HMAC POST (soft-fail) | Registered |
process_recording | egress/file meta | Download → RecordingStore → complete + webhooks | Implemented; bootstrap commented |
recording_delete | RecordingID, RoomID, RoomName | Hard-delete DB row | Implemented; bootstrap commented |
handler_stubs.go is an index comment only — no stub handlers remain.
Email templates: welcome, verify_email, password_reset, password_changed, room_invite (registered, not yet enqueued by handlers), generic. Branding from config + SystemSettings.
RoomCleanupService| Fn | Behavior |
|---|---|
CascadeDeleteRoom | Stop egress → system msg → LK delete → chat cleanup → delete recordings → HardDeleteRoom |
ArchiveRoom | Soft-delete; keeps recordings |
SuspendRoom | Idle + disconnect; clears recording rows |
DeleteUserRooms | Cascade each owned room |
RecordingService (SHIPPED)Gates: system RecordingsEnabled → room exists → room RecordingsAllowed → max per room → no active.
StartRecording → RoomCompositeEgress MP4 AudioOnly=true.
StopRecording → StopEgress only; webhook egress_ended owns processing enqueue.
Lifecycle: pending → started → (webhook) processing → process_recording → completed|failed.
| Component | Role |
|---|---|
ChatUploadStore | disk/hybrid/inline/s3 (SigV4) |
ChatUploadTracker | Record + DeleteByRoom + quotas |
Avatar | ./data/uploads/avatars, max 2MB |
RecordingStore | Disk or S3; key recordings/{user}/{room}/{id}-….mp4 |
internal/scheduler/)| Cadence | Task |
|---|---|
| 1 min | Expired rooms + idle LK empty rooms (skip persistent) |
| Weekly 03:00 | Delete old guests (7d) |
| Daily 03:30 | Unverified local/passkey accounts |
| Hourly | Blocked refresh tokens + prune revoked access JWTs |
| Daily 03:00 | Queue cleanup (done 7d / failed 30d); stale recordings 7d |
| Daily 09:00 | Self-signed cert renew if ≤30d |
CleanupIntervalHours | Recording retention on archived rooms (needs recStore) |
internal/livekit/)bin/livekit-server (placeholder OK for build; real binary for run)StartInternalServer / RunLiveKit; skip if LIVEKIT_MANAGED=truehttp://localhost:<httpPort>/api/livekit/webhook/livekit reverse-proxy when internalinternal/lkutil/)NewClient (RoomService), NewEgressClient, AuthContext, SendSystemMessage (+ deletedIdentity variant).
handlers/livekit_webhook.go)Verifies LK JWT. Handles participant/room lifecycle + egress → may enqueue process_recording. External LK: configure webhook URL manually to /api/livekit/webhook.
internal/cli/, usercli/, roomcli/, install/)Cobra root: run, livekit, install/uninstall, cert, user, room, config, settings, invite-token, db, version.
--json via clioutput. Installer: LinuxInstall — multi-init (systemd/OpenRC/SysV/container), secrets, /etc/bedrud, /var/lib/bedrud, optional ACME/self-signed/external LK.
usercli: promote/demote/create/delete/list/info/password/active with full cascade delete via cleanup service.
roomcli: list/info/close/suspend/reactivate/kick.
Utils: TLS multi-algo certs, SMTP, LiveKit keypair, SafeCreate, OutboundIP.
cmd/bedrud → internal/cli
├─ server.Run → handlers → repository → models
│ → auth → repository
│ → middleware → auth
│ → queue → services / storage / email
│ → scheduler → repository + livekit + auth prune
│ → services (cleanup, recording)
│ → storage (chat, avatar, recording)
│ → livekit (embed process)
│ → lkutil (RoomService / Egress)
├─ livekit.RunLiveKit
├─ install.LinuxInstall
├─ usercli / roomcli → repository + services + storage + lkutil
└─ utils / database / clioutput
cmd/server → same wiring inline (dev; Swagger)
| Area | Keys / env |
|---|---|
| Server | host, port, httpPort, TLS, ACME, behindProxy, trustedProxies |
| Auth | JWTSecret, TokenDuration, RequireEmailVerification, OAuth, passkey TTL |
| Database | type sqlite|postgres, path/DSN, pool |
| LiveKit | host, internalHost, apiKey/secret (≥32 embedded), external, nodeIP, configPath |
| Queue | pollInterval, maxAttempts, concurrency |
| SMTP + template branding | |
| Chat | uploads backend/disk/s3/inlineMax |
| Recording | maxFileSizeMB, storageDir, maxPerRoom, retentionHours |
| RateLimit | auth/guest/api/resend max + window |
Runtime overrides: many via SystemSettings + GetEffectiveSettings.
/api/room/*; recording is /api/rooms/:id/*.DeletedAt); hard delete via HardDeleteRoom / purge jobs. No AdminDeleteRoom name.idx_rooms_active_name); idle/archived can reuse names.EmailVerifiedAt is client-facing only.MaxOpenConns(1)); pending jobs stuck → check DB/worker map.internal/cli), not manual arg switch in main.LinuxInstall, not Debian-only.make init.cd server && go vet ./...
cd server && go build ./...
cd server && go test -v -count=1 ./...
# race (CI): go test -race ./...
Swagger regen: make swagger-gen (swag CLI). UI: /api/swagger, /api/scalar.