···11-# Deleted - Remove this file
11+# Witness Cache
22+33+Paul Frazee:
44+55+I'm increasingly convinced that many Atmosphere backends start with a local "witness cache" of the repositories.
66+A witness cache is a copy of the repository records, plus a timestamp of when the record was indexed (the "witness time") which you want to keep
77+88+The key feature is: you can replay it
99+1010+With local replay, you can add new tables or indexes to your backend and quickly backfill the data. If you don't have a witness cache, you would have to do backfill from the network, which is slow
1111+1212+RocksDB or other LSMs are good candidates for a witness cache (good write throughput)
1313+1414+Clickhouse and DuckDB are also good candidates (good compression ratio)
1515+1616+## TODO
···4444 }
4545}
46464747-// GetRecentBrews fetches recent activity (brews and other records) from all registered users
4747+// GetRecentRecords fetches recent activity (brews and other records) from all registered users
4848// Returns up to `limit` items sorted by most recent first
4949-func (s *Service) GetRecentBrews(ctx context.Context, limit int) ([]*FeedItem, error) {
4949+func (s *Service) GetRecentRecords(ctx context.Context, limit int) ([]*FeedItem, error) {
5050 dids := s.registry.List()
5151 if len(dids) == 0 {
5252 log.Debug().Msg("feed: no registered users")
+13-1
internal/handlers/handlers.go
···9898func (h *Handler) HandleFeedPartial(w http.ResponseWriter, r *http.Request) {
9999 var feedItems []*feed.FeedItem
100100 if h.feedService != nil {
101101- feedItems, _ = h.feedService.GetRecentBrews(r.Context(), 20)
101101+ feedItems, _ = h.feedService.GetRecentRecords(r.Context(), 20)
102102 }
103103104104 if err := bff.RenderFeedPartial(w, feedItems); err != nil {
···12631263 log.Error().Err(err).Msg("Failed to render profile page")
12641264 }
12651265}
12661266+12671267+// HandleNotFound renders the 404 page
12681268+func (h *Handler) HandleNotFound(w http.ResponseWriter, r *http.Request) {
12691269+ // Check if current user is authenticated (for nav bar state)
12701270+ didStr, err := atproto.GetAuthenticatedDID(r.Context())
12711271+ isAuthenticated := err == nil && didStr != ""
12721272+12731273+ if err := bff.Render404(w, isAuthenticated, didStr); err != nil {
12741274+ http.Error(w, "Page not found", http.StatusNotFound)
12751275+ log.Error().Err(err).Msg("Failed to render 404 page")
12761276+ }
12771277+}
+29-1
internal/middleware/logging.go
···11package middleware
2233import (
44+ "net"
45 "net/http"
66+ "strings"
57 "time"
6879 "arabica/internal/atproto"
810911 "github.com/rs/zerolog"
1012)
1313+1414+// getClientIP extracts the real client IP address from the request,
1515+// checking X-Forwarded-For and X-Real-IP headers for reverse proxy setups.
1616+func getClientIP(r *http.Request) string {
1717+ // Check X-Forwarded-For header (can contain multiple IPs: client, proxy1, proxy2)
1818+ if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
1919+ // Take the first IP (the original client)
2020+ if idx := strings.Index(xff, ","); idx != -1 {
2121+ return strings.TrimSpace(xff[:idx])
2222+ }
2323+ return strings.TrimSpace(xff)
2424+ }
2525+2626+ // Check X-Real-IP header (single IP set by some proxies)
2727+ if xri := r.Header.Get("X-Real-IP"); xri != "" {
2828+ return strings.TrimSpace(xri)
2929+ }
3030+3131+ // Fall back to RemoteAddr (strip port if present)
3232+ ip, _, err := net.SplitHostPort(r.RemoteAddr)
3333+ if err != nil {
3434+ // RemoteAddr might not have a port
3535+ return r.RemoteAddr
3636+ }
3737+ return ip
3838+}
11391240// LoggingMiddleware returns a middleware that logs HTTP request details with structured logging
1341func LoggingMiddleware(logger zerolog.Logger) func(http.Handler) http.Handler {
···4573 Str("query", r.URL.RawQuery).
4674 Int("status", rw.statusCode).
4775 Dur("duration", duration).
4848- Str("remote_addr", r.RemoteAddr).
7676+ Str("client_ip", getClientIP(r)).
4977 Str("user_agent", r.UserAgent()).
5078 Int64("bytes_written", rw.bytesWritten).
5179 Str("proto", r.Proto)
+3
internal/routing/routing.go
···8383 fs := http.FileServer(http.Dir("web/static"))
8484 mux.Handle("GET /static/", http.StripPrefix("/static/", fs))
85858686+ // Catch-all 404 handler - must be last, catches any unmatched routes
8787+ mux.HandleFunc("/", h.HandleNotFound)
8888+8689 // Apply middleware in order (last added is executed first)
8790 // 1. Apply OAuth middleware to add auth context to all requests
8891 handler := cfg.OAuthManager.AuthMiddleware(mux)
+3
justfile
···44run-production:
55 @LOG_FORMAT=json SECURE_COOKIES=true go run cmd/server/main.go
6677+test:
88+ @go test ./... -cover -coverprofile=cover.out
99+710style:
811 @tailwindcss -i web/static/css/style.css -o web/static/css/output.css --minify
+12
templates/404.tmpl
···11+{{define "content"}}
22+<div class="max-w-4xl mx-auto">
33+ <div class="bg-white rounded-lg shadow-md p-8 text-center">
44+ <div class="text-6xl mb-4">404</div>
55+ <h2 class="text-2xl font-bold text-gray-800 mb-4">Page Not Found</h2>
66+ <p class="text-gray-600 mb-6">The page you're looking for doesn't exist or has been moved.</p>
77+ <a href="/" class="inline-block bg-brown-600 text-white py-3 px-6 rounded-lg hover:bg-brown-700 transition">
88+ Back to Home
99+ </a>
1010+ </div>
1111+</div>
1212+{{end}}