-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Feasibility Study: Zero-Install Browser Version of MeticAI
Executive Summary
Can MeticAI run as a third-party-hosted, zero-install web app that communicates directly with the Meticulous machine from the browser?
Answer: No — not with a publicly hosted HTTPS site. The browser's mixed content policy universally blocks HTTPS pages from making HTTP requests to local network devices. This is an absolute, cross-browser, un-bypassable security restriction. However, alternative architectures exist that achieve similar goals.
The Critical Question: Browser ↔ Machine Communication
What Works ✅
-
Machine CORS is fully open. The Meticulous backend (
base_handler.py) already sets:Access-Control-Allow-Origin: * Access-Control-Allow-Headers: * Access-Control-Allow-Methods: GET,POST,OPTIONS,DELETESocket.IO also has
cors_allowed_origins="*". OPTIONS preflight handler returns 204. -
Browser-compatible TypeScript client exists.
@meticulous-home/espresso-apiv0.10.11 provides 47+ methods covering all machine operations. Dependencies areaxios+socket.io-client— both work in browsers. This package could directly replace our PythonpyMeticuloususage for client-side communication. -
Type definitions available.
@meticulous-home/espresso-profilev0.4.2 provides zero-dependency TypeScript profile types, parsing, and variable resolution.
What Blocks It ❌
Blocker 1: Mixed Content (CRITICAL — All Browsers)
Any publicly hosted web app must use HTTPS (GitHub Pages, Vercel, Netlify, etc. all enforce this). The Meticulous machine runs HTTP only on the local network.
https://meticai.app → http://192.168.1.x:8080
(public HTTPS) (private HTTP)
All modern browsers block this as "mixed content." This applies to:
fetch()/XMLHttpRequestcalls- WebSocket connections (
wss://→ws://) - Socket.IO connections (uses both)
- Image/resource loading
There is no workaround that's cross-browser. This has been enforced since ~2020 and is part of the W3C Mixed Content specification.
Blocker 2: Private Network Access (Chrome, Solvable but Moot)
Chrome's Private Network Access specification additionally requires a CORS preflight with Access-Control-Allow-Private-Network: true for public-to-private requests. The machine doesn't include this header, though it would be a trivial 1-line addition to base_handler.py.
However, this is moot — even if the PNA preflight succeeds, mixed content blocking happens first and kills the request.
Chrome has an experimental targetAddressSpace: "private" fetch option with a user permission prompt for HTTPS → HTTP mixed content, but it's:
- Chrome-only (no Firefox, Safari, or other browser support)
- Still experimental / behind flags
- Requires the PNA header on the device
- Poor UX (repeated permission prompts)
Alternative Architectures
Option A: Machine-Hosted Frontend ⭐ RECOMMENDED
Deploy the MeticAI React frontend as static files served by the Meticulous machine's own web server (Tornado on port 80).
User browser → http://meticulous.local/meticai → Same-origin API calls
How it works:
- Frontend is a static build artifact (
dist/folder from Vite) - Served as a route on the machine's existing Tornado web server
- All API calls are same-origin — no CORS, no PNA, no mixed content
- AI features: user enters their own Gemini API key in settings, calls made directly from browser to Google Cloud (HTTPS → HTTPS, no issues)
- Real-time telemetry: Socket.IO client connects to same-origin
:8080
Pros:
- True "zero install" from user's perspective (just navigate to URL)
- Works in ALL browsers
- No cloud infrastructure needed
- No API key security concerns (user manages their own key)
- Existing React codebase reusable with minimal changes
Cons:
- Requires MeticulousHome cooperation to distribute as a machine "app"
- Updates tied to machine firmware or a sideloading mechanism
- Machine needs enough storage/CPU for static file serving (trivial)
Required MeticulousHome changes:
- Add a static file route to Tornado for
/meticai/* - Add
Access-Control-Allow-Private-Network: truetobase_handler.py(future-proofing)
Option B: Capacitor/Native App (Already Planned — #253)
Our existing plan for milestone 2.4. Sidesteps ALL browser restrictions via native networking.
Pros: Best UX, mDNS discovery, full feature parity, App Store distribution
Cons: Requires install, App Store review, separate build pipeline
Option C: Cloud Relay / Tunnel
Machine establishes outbound WebSocket to a cloud relay server. Browser connects to same relay. Commands and telemetry proxied through the cloud.
Pros: Works from any browser on any network (even remote access)
Cons: Latency, cloud infrastructure costs, privacy concerns, requires machine outbound connectivity, complex architecture
Option D: Progressive Web App from Machine
Combine Option A with PWA capabilities:
- Serve frontend from machine with a service worker for offline support
- Register as installable PWA (add to home screen)
- Cache frontend assets aggressively; only API calls need the machine
Pros: App-like experience without app stores, auto-updates when machine firmware updates
Cons: Still requires machine-side hosting
Feature Portability Matrix
If we pursue Option A (machine-hosted) or Option B (Capacitor), which features can run client-side vs needing a server?
| Feature | Browser-Direct | Needs Server | Notes |
|---|---|---|---|
| Profile CRUD | ✅ | — | espresso-api REST calls |
| Start / Stop / Preheat | ✅ | — | espresso-api action methods |
| Real-time telemetry | ✅ | — | socket.io-client to machine |
| Shot history browsing | ✅ | — | REST API + .zst decompression in JS (via fzstd) |
| Shot data visualization | ✅ | — | Recharts already client-side |
| Profile validation | ✅ | — | espresso-profile package (pure TS) |
| Variable resolution | ✅ | — | espresso-profile has processProfileVariables() |
| Pour-over mode | ✅ | — | Timer + machine commands |
| Profile generation (AI) | — | Gemini API call; can use user-provided key from browser | |
| Shot analysis (AI) | — | Same — user-provided key enables browser-direct | |
| Image generation (AI) | — | Imagen API; same pattern | |
| Espresso compass (AI) | — | Gemini call; same pattern | |
| mDNS discovery | ❌ | ✅ | Browsers can't do mDNS; user enters IP or uses .local |
| Scheduled shots | — | Browser must remain open; no wake timers | |
| Data persistence | ✅ | — | IndexedDB replaces file-based storage |
| Shot annotations | ✅ | — | IndexedDB or machine-side storage |
Key insight: AI features can work browser-side if users provide their own Gemini API key (which they already do in settings). The Google AI JavaScript SDK (@google/genai) works in browsers. No server-side proxy needed.
Features We Can Live Without
- mDNS auto-discovery: User enters machine IP manually or uses
meticulous.local— perfectly acceptable - Server-side scheduled shots: Browser-based timer works while app is open; true scheduled shots need the machine-side scheduler
- MQTT bridge layer: Use Socket.IO directly via
espresso-apiinstead of our MQTT→WebSocket chain
Recommended Path Forward
Phase 1: Machine-Hosted PWA (Option A + D)
- Refactor frontend to optionally use
@meticulous-home/espresso-apidirectly instead of going through our FastAPI backend - Add Gemini browser client using
@google/genaiJS SDK with user-provided API key - Build static frontend and package for machine distribution
- Coordinate with MeticulousHome to add a static file route or "apps" mechanism
- Add PWA manifest for installable app experience
Phase 2: Capacitor App (Option B, #253)
- Same refactored frontend as Phase 1
- Add Capacitor shell with mDNS plugin for auto-discovery
- Distribute via App Store / sideload
Both phases share the same core: a frontend that can talk directly to the machine without our Python backend. The Python backend becomes optional (useful for Docker self-hosted deployment but not required for browser/app use).
Technical Research References
- W3C Mixed Content Specification
- Chrome Private Network Access (PNA)
- Chrome PNA Update Blog
- MeticulousHome/meticulous-backend
base_handler.py— CORS headers confirmedAccess-Control-Allow-Origin: * @meticulous-home/espresso-api— Browser-compatible TS client (axios + socket.io-client)@meticulous-home/espresso-profile— Profile types- MeticAI Issue research: MeticulousHome repository analysis — findings & impact for milestones 2.2–2.4 #283 — MeticulousHome repository analysis
- MeticAI Issue iOS app: native client via Capacitor + MachineDirectAdapter #253 — Capacitor app milestone
Research conducted: Analysis of Meticulous machine API CORS headers, Chrome Private Network Access specification, W3C Mixed Content standard, MeticulousHome TypeScript API packages, and MeticAI backend architecture.