···2727Environment variables:
28282929- `PORT` - Server port (default: 18910)
3030+- `SERVER_PUBLIC_URL` - Public URL for reverse proxy deployments (e.g., https://arabica.example.com)
3031- `ARABICA_DB_PATH` - BoltDB path (default: ~/.local/share/arabica/arabica.db)
3132- `OAUTH_CLIENT_ID` - OAuth client ID (optional, uses localhost mode if not set)
3233- `OAUTH_REDIRECT_URI` - OAuth redirect URI (optional)
···7071```
71727273## Deployment
7474+7575+### Reverse Proxy Setup
7676+7777+When deploying behind a reverse proxy (nginx, Caddy, Cloudflare Tunnel, etc.), set the `SERVER_PUBLIC_URL` environment variable to your public-facing URL:
7878+7979+```bash
8080+# Example with nginx reverse proxy
8181+SERVER_PUBLIC_URL=https://arabica.example.com
8282+SECURE_COOKIES=true
8383+PORT=18910
8484+8585+# The server listens on localhost:18910
8686+# But OAuth callbacks use https://arabica.example.com/oauth/callback
8787+```
8888+8989+The `SERVER_PUBLIC_URL` is used for OAuth client metadata and callback URLs, ensuring the AT Protocol OAuth flow works correctly when the server is accessed via a different URL than it's running on.
9090+9191+### NixOS Deployment
73927493See docs/nix-install.md for NixOS deployment instructions.
7594
+35-13
cmd/server/main.go
···5454 port = "18910"
5555 }
56565757+ // Get public root URL for reverse proxy deployments
5858+ // This allows the server to be accessed via a different URL than it's running on
5959+ // e.g., SERVER_PUBLIC_URL=https://arabica.example.com when behind a reverse proxy
6060+ publicURL := os.Getenv("SERVER_PUBLIC_URL")
6161+5762 // Initialize BoltDB store for persistent sessions and feed registry
5863 dbPath := os.Getenv("ARABICA_DB_PATH")
5964 if dbPath == "" {
···9095 redirectURI := os.Getenv("OAUTH_REDIRECT_URI")
91969297 if clientID == "" && redirectURI == "" {
9393- // Use localhost defaults for development
9494- redirectURI = fmt.Sprintf("http://127.0.0.1:%s/oauth/callback", port)
9595- clientID = "" // Empty triggers localhost mode
9696- log.Info().Msg("Using localhost OAuth mode (for development)")
9898+ // Use public URL if set, otherwise localhost defaults for development
9999+ if publicURL != "" {
100100+ redirectURI = publicURL + "/oauth/callback"
101101+ clientID = publicURL + "/oauth-client-metadata.json"
102102+ log.Info().
103103+ Str("public_url", publicURL).
104104+ Msg("Using public URL for OAuth (reverse proxy mode)")
105105+ } else {
106106+ redirectURI = fmt.Sprintf("http://127.0.0.1:%s/oauth/callback", port)
107107+ clientID = "" // Empty triggers localhost mode
108108+ log.Info().Msg("Using localhost OAuth mode (for development)")
109109+ }
97110 }
9811199112 oauthManager, err := atproto.NewOAuthManager(clientID, redirectURI, sessionStore)
···123136 Msg("OAuth configured")
124137 } else {
125138 log.Info().
139139+ Str("mode", "public").
126140 Str("client_id", clientID).
127141 Str("redirect_uri", redirectURI).
128142 Msg("OAuth configured")
···132146 atprotoClient := atproto.NewClient(oauthManager)
133147 log.Info().Msg("ATProto client initialized")
134148149149+ // Initialize session cache for in-memory caching of user data
150150+ sessionCache := atproto.NewSessionCache()
151151+ stopCacheCleanup := sessionCache.StartCleanupRoutine(10 * time.Minute)
152152+ defer stopCacheCleanup()
153153+ log.Info().Msg("Session cache initialized with background cleanup")
154154+135155 // Determine if we should use secure cookies (default: false for development)
136156 // Set SECURE_COOKIES=true in production with HTTPS
137157 secureCookies := os.Getenv("SECURE_COOKIES") == "true"
138158139139- // Initialize handlers
140140- h := handlers.NewHandler()
141141- h.SetConfig(handlers.Config{
142142- SecureCookies: secureCookies,
143143- })
144144- h.SetOAuthManager(oauthManager)
145145- h.SetAtprotoClient(atprotoClient)
146146- h.SetFeedRegistry(feedRegistry)
147147- h.SetFeedService(feedService)
159159+ // Initialize handlers with all dependencies via constructor injection
160160+ h := handlers.NewHandler(
161161+ oauthManager,
162162+ atprotoClient,
163163+ sessionCache,
164164+ feedService,
165165+ feedRegistry,
166166+ handlers.Config{
167167+ SecureCookies: secureCookies,
168168+ },
169169+ )
148170149171 // Setup router with middleware
150172 handler := routing.SetupRouter(routing.Config{