🧠 Deep Solutions
Architectural fixes and established solutions for the homelab deployment.
🔓 Frictionless Authentication
- Captcha: Disabled via
config.json(captcha: { enabled: false }). - Email Bypass: Bypassing email verification requires patching
AuthController.tsto respectstrict_email_verification_required: false(by default it forces confirmation for all non-temp accounts). - Session Cache: Puter aggressively caches user state in Valkey (
sessions:v2:uuid:*andusers:id:*). Direct DB modifications require targeted cache eviction viavalkey-cli -c DEL <key>(Cluster Mode).
🌍 Networking Quirks
- Subdomain Offset: Express.js
subdomain offsetdefaults to 2. Foros.loca.zone(3 parts), this breaks{ subdomain: '' }routing. Solved dynamically inserver.ts:app.set('subdomain offset', domain.split('.').length). - S3 Path Style: AWS S3 SDK uses virtual-hosted style by default, causing
ENOTFOUND puter-thumbnails.s3. Resolved by enforcing"forcePathStyle": truein thethumbnailStoreconfig.
🐳 Docker Build Optimization
- Cache Busting: Modifying backend TypeScript files doesn’t reliably bust the Docker
COPY . .cache layer when usingtouch. Content must be modified to trigger a proper recompilation. - Worker Preamble: The worker build must be sequenced after the
puter-jsbuild completes to avoid race conditions (ENOENT puter.js).
🛠️ Valkey Cache & CROSSSLOT Constraints
When bypassing the hardcoded Puter email confirmation in user table via raw SQL (UPDATE user SET email_confirmed=1), the change is invisible to the frontend because the user object is aggressively cached in the Valkey Cluster. Due to CROSSSLOT restrictions in clustered Redis environments, bulk wildcards (DEL *) fail. You must individually evict keys mapping to the specific hash slots (e.g., valkey-cli -c DEL users:username:lilo) to force a fresh DB read.
🛡️ Nginx Reverse Proxy Perfection
- Wildcard Redirects: Using exact
if ($host = ...)blocks on port 80 fails for wildcard subdomains. Use a catch-alllocation / { return 301 https://$host$request_uri; }to handle infinite subdomains while safely isolating.well-known/acme-challenge/blocks. - SVG CSP Blocking: The Puter API serves dynamic SVG icons with a strict
Content-Security-Policy: default-src 'none'; sandbox;header. Chrome blocks these from rendering in frontend<img>tags. Fix by addingproxy_hide_header Content-Security-Policy;to the Nginx proxy, falling back to Nginx’s native permissive policy. - sub_filter on Compressed Upstreams: Nginx
sub_filtersilently fails when proxying gzipped responses. To inject custom<script>antidotes (e.g., healing poisonedapi_originlocalStorage caches), force plaintext from the backend usingproxy_set_header Accept-Encoding "";. - Quartz Wiki Hosting: Never allow static documentation to use SPA fallbacks. Masking missing assets with
/index.htmlcauses infinite loop rendering. Enforcetry_files $uri $uri.html $uri/ =404;. Additionally, always define a static assetlocation ~* \.(css|js|woff2?|...)$block to bypass aggressive rootlimit_reqzones and prevent false 503s.
⛓️ Immutable Backend Overrides
- Database Intercept Triggers: Puter’s sealed backend forcefully seeds new users with commercial “ghost apps” (e.g.,
editor,browser) within theuser.taskbar_itemsJSON column. Instead of unpacking and rebuilding the core Docker image to change this hardcoded default, use MariaDBBEFORE INSERTandBEFORE UPDATEtriggers to silently intercept the payload and rewrite it (e.g., to[{"name":"dev-center","type":"app"}]). This guarantees UI purity for all new accounts.
📦 Native App Integration
- FOSS App Cloning: The default built-in apps (Code, Editor, Browser, Photopea) point to broken external URLs. We cloned the official
HeyPuterrepositories (vscode-web-puter,browser.js,photopea-integration) to self-host their static assets. - Nginx Static Hosting: A dedicated Nginx server block statically maps domains like
code.site.os.loca.zoneto the cloned directories, utilizing the wildcard TLS certificate. - Database Remapping: MariaDB
UPDATEqueries were executed on theappstable to hijack the defaultindex_urlmappings, resurrecting the apps locally. Absolute URLs in theiconfield were stripped to avoid SSL 4th-level subdomain errors. - OpenRouter Injection: The native LLM requests in Puter’s core engine were rewired to route through OpenRouter by explicitly defining the
openrouterprovider block inconfig.json.