#!/usr/bin/env python3 """Forgeverse Bridge — let https://forgeverse.org use the AI running on YOUR computer. Local AI servers (FCC, ComfyUI, Ollama, LM Studio) usually don't answer browser requests from other websites (no CORS), so a page on forgeverse.org can't reach them. This bridge listens on 127.0.0.1 only and forwards: http://127.0.0.1:8765/fcc/... -> http://127.0.0.1:8082/... (free-claude-code) http://127.0.0.1:8765/comfy/... -> http://127.0.0.1:8188/... (ComfyUI) http://127.0.0.1:8765/ollama/... -> http://127.0.0.1:11434/... (Ollama) http://127.0.0.1:8765/lmstudio/... -> http://127.0.0.1:1234/... (LM Studio) http://127.0.0.1:8765/llamacpp/... -> http://127.0.0.1:8080/... (llama.cpp server) It adds CORS headers ONLY for the allowed origins (forgeverse.org by default) and refuses requests from any other website, so a random page you visit can't use your machine. Nothing is sent anywhere except the local services above. Python 3.8+, standard library only, nothing to install: python3 forgeverse-bridge.py python3 forgeverse-bridge.py --route comfy=http://192.168.1.50:8188 # a GPU box on your LAN python3 forgeverse-bridge.py --route myllm=http://127.0.0.1:5000 # anything else License: MIT. Part of Forgeverse (https://forgeverse.org). """ import argparse import http.client import json import socket import sys from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from urllib.parse import urlsplit VERSION = "1.0.0" DEFAULT_ROUTES = { "fcc": "http://127.0.0.1:8082", "comfy": "http://127.0.0.1:8188", "ollama": "http://127.0.0.1:11434", "lmstudio": "http://127.0.0.1:1234", "llamacpp": "http://127.0.0.1:8080", } DEFAULT_ORIGINS = ["https://forgeverse.org", "https://www.forgeverse.org"] HOP = {"connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailers", "transfer-encoding", "upgrade", "host", "origin", "referer", "content-length"} def build_handler(routes, origins): class Bridge(BaseHTTPRequestHandler): server_version = f"ForgeverseBridge/{VERSION}" protocol_version = "HTTP/1.1" def log_message(self, fmt, *args): sys.stderr.write("[bridge] %s %s\n" % (self.address_string(), fmt % args)) # ── CORS ── def _origin_ok(self): origin = self.headers.get("Origin") return origin is None or origin in origins def _cors(self): origin = self.headers.get("Origin") if origin in origins: self.send_header("Access-Control-Allow-Origin", origin) self.send_header("Vary", "Origin") self.send_header("Access-Control-Allow-Private-Network", "true") self.send_header("Access-Control-Expose-Headers", "*") def _plain(self, code, payload): body = json.dumps(payload).encode() self.send_response(code) self._cors() self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_OPTIONS(self): if not self._origin_ok(): return self._plain(403, {"error": "origin not allowed"}) self.send_response(204) self._cors() self.send_header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") self.send_header("Access-Control-Allow-Headers", self.headers.get("Access-Control-Request-Headers", "*") or "*") self.send_header("Access-Control-Max-Age", "600") self.send_header("Content-Length", "0") self.end_headers() def _status(self): table = {} for name, target in routes.items(): u = urlsplit(target) try: socket.create_connection((u.hostname, u.port or 80), timeout=0.6).close() table[name] = {"target": target, "up": True} except OSError: table[name] = {"target": target, "up": False} return {"bridge": "forgeverse", "version": VERSION, "routes": table} def _proxy(self): if not self._origin_ok(): return self._plain(403, {"error": "origin not allowed", "allowed": origins}) path = self.path if path in ("/", "/status"): return self._plain(200, self._status()) name, _, rest = path.lstrip("/").partition("/") target = routes.get(name) if not target: return self._plain(404, {"error": f"no route '{name}'", "routes": sorted(routes)}) u = urlsplit(target) upstream_path = (u.path.rstrip("/") + "/" + rest) if rest else (u.path or "/") length = int(self.headers.get("Content-Length") or 0) body = self.rfile.read(length) if length else None fwd = {k: v for k, v in self.headers.items() if k.lower() not in HOP} conn_cls = http.client.HTTPSConnection if u.scheme == "https" else http.client.HTTPConnection conn = conn_cls(u.hostname, u.port, timeout=3600) try: conn.request(self.command, upstream_path, body=body, headers=fwd) resp = conn.getresponse() except OSError as e: return self._plain(502, {"error": f"{name} is not reachable at {target}", "detail": str(e)}) self.send_response(resp.status, resp.reason) self._cors() for k, v in resp.getheaders(): if k.lower() not in HOP and not k.lower().startswith("access-control-"): self.send_header(k, v) if self.command == "HEAD": self.send_header("Content-Length", "0") self.end_headers() conn.close() return self.send_header("Transfer-Encoding", "chunked") self.end_headers() try: while True: chunk = resp.read1(65536) if hasattr(resp, "read1") else resp.read(65536) if not chunk: break self.wfile.write(b"%x\r\n%s\r\n" % (len(chunk), chunk)) self.wfile.flush() self.wfile.write(b"0\r\n\r\n") except (BrokenPipeError, ConnectionResetError): pass finally: conn.close() do_GET = do_POST = do_PUT = do_PATCH = do_DELETE = do_HEAD = _proxy return Bridge def main(): ap = argparse.ArgumentParser(description="Forgeverse Bridge: expose your local AI to forgeverse.org only.") ap.add_argument("--port", type=int, default=8765) ap.add_argument("--route", action="append", default=[], metavar="NAME=URL", help="add or override a route, e.g. comfy=http://192.168.1.50:8188") ap.add_argument("--allow-origin", action="append", default=[], metavar="ORIGIN", help="extra allowed web origin (default: forgeverse.org only)") a = ap.parse_args() routes = dict(DEFAULT_ROUTES) for r in a.route: name, _, url = r.partition("=") if not name or not url.startswith(("http://", "https://")): ap.error(f"bad --route {r!r}; use NAME=http://host:port") routes[name.strip()] = url.strip().rstrip("/") origins = DEFAULT_ORIGINS + a.allow_origin srv = ThreadingHTTPServer(("127.0.0.1", a.port), build_handler(routes, origins)) print(f"Forgeverse Bridge {VERSION} on http://127.0.0.1:{a.port} (allowed: {', '.join(origins)})") for n, t in routes.items(): print(f" /{n:<9} -> {t}") print("Leave this window open while you use forgeverse.org. Ctrl+C to stop.") try: srv.serve_forever() except KeyboardInterrupt: pass if __name__ == "__main__": main()