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,DELETE
Socket.IO also has cors_allowed_origins="*". OPTIONS preflight handler returns 204.
-
Browser-compatible TypeScript client exists. @meticulous-home/espresso-api v0.10.11 provides 47+ methods covering all machine operations. Dependencies are axios + socket.io-client — both work in browsers. This package could directly replace our Python pyMeticulous usage for client-side communication.
-
Type definitions available. @meticulous-home/espresso-profile v0.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() / XMLHttpRequest calls
- 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: true to base_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-api instead of our MQTT→WebSocket chain
Recommended Path Forward
Phase 1: Machine-Hosted PWA (Option A + D)
- Refactor frontend to optionally use
@meticulous-home/espresso-api directly instead of going through our FastAPI backend
- Add Gemini browser client using
@google/genai JS 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
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.
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:Socket.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.
All modern browsers block this as "mixed content." This applies to:
fetch()/XMLHttpRequestcallswss://→ws://)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: truefor public-to-private requests. The machine doesn't include this header, though it would be a trivial 1-line addition tobase_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: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).
How it works:
dist/folder from Vite):8080Pros:
Cons:
Required MeticulousHome changes:
/meticai/*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:
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?
espresso-apiREST callsespresso-apiaction methodssocket.io-clientto machine.zstdecompression in JS (via fzstd)espresso-profilepackage (pure TS)espresso-profilehasprocessProfileVariables().localKey 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
meticulous.local— perfectly acceptableespresso-apiinstead of our MQTT→WebSocket chainRecommended Path Forward
Phase 1: Machine-Hosted PWA (Option A + D)
@meticulous-home/espresso-apidirectly instead of going through our FastAPI backend@google/genaiJS SDK with user-provided API keyPhase 2: Capacitor App (Option B, #253)
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
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 typesResearch 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.