···22222323## Architecture
24242525-- **`/src`** - Main backend (OAuth, site management, custom domains)
2626-- **`/hosting-service`** - Microservice that serves cached sites from disk
2525+- **`/apps/main-app`** - Main backend (OAuth, site management, custom domains)
2626+- **`/apps/hosting-service`** - Microservice that serves cached sites from disk
2727- **`/cli`** - Rust CLI for direct PDS uploads
2828-- **`/public`** - React frontend
2828+- **`/apps/main-app/public`** - React frontend
2929+- **`/packages`** - Shared packages
29303031### How it works
3132
+22
apps/hosting-service/src/lib/firehose.ts
···2020 private idResolver: IdResolver
2121 private isShuttingDown = false
2222 private lastEventTime = Date.now()
2323+ private cacheCleanupInterval: NodeJS.Timeout | null = null
23242425 constructor(
2526 private logger?: (msg: string, data?: Record<string, unknown>) => void
2627 ) {
2728 this.idResolver = new IdResolver()
2929+ this.startCacheCleanup()
2830 }
29313032 private log(msg: string, data?: Record<string, unknown>) {
···3234 log(`[FirehoseWorker] ${msg}`, data || {})
3335 }
34363737+ private startCacheCleanup() {
3838+ // Clear IdResolver cache every hour to prevent unbounded memory growth
3939+ // The IdResolver has an internal cache that never expires and can cause heap exhaustion
4040+ this.cacheCleanupInterval = setInterval(() => {
4141+ if (this.isShuttingDown) return
4242+4343+ this.log('Clearing IdResolver cache to prevent memory leak')
4444+4545+ // Recreate the IdResolver to clear its internal cache
4646+ this.idResolver = new IdResolver()
4747+4848+ this.log('IdResolver cache cleared')
4949+ }, 60 * 60 * 1000) // Every hour
5050+ }
5151+3552 start() {
3653 this.log('Starting firehose worker')
3754 this.connect()
···4057 stop() {
4158 this.log('Stopping firehose worker')
4259 this.isShuttingDown = true
6060+6161+ if (this.cacheCleanupInterval) {
6262+ clearInterval(this.cacheCleanupInterval)
6363+ this.cacheCleanupInterval = null
6464+ }
43654466 if (this.firehose) {
4567 this.firehose.destroy()