diff --git a/app/web/public-br/index.html b/app/web/public-br/index.html
index 9227157f..43750e5b 100644
--- a/app/web/public-br/index.html
+++ b/app/web/public-br/index.html
@@ -1 +1,3 @@
-u Œ“Tõ5Jk¿“ŸÖË~Éª¼2�Òõ��R€º�´ÖñÞR’’vÃl³p$ƒq§Û<þ‚ æœ‰‡Å`aúŠH=(øX9 F”²,$´©ï�­%ïú@0ìÅØ>OºÍ#îýHò«'öVìààGzàŽÝÈe$cý@™¾týÌ«c^ùj1Úd%Q¸š±ôˆPT	:ÒüoTeˆÂ¤×Ù$�i
\ No newline at end of file
+Æ ŒÔ`Íž±®Tw®³B¬Â¡`y9ûÿc3¦˜u!k
+?AÄ®Óv=þ®Hg›$YH†ø…"
=˜2àYÑ
+4V)á‹mŠ;Dk…]/LuÁ·Gri¯+õtº$ù/}o¥èü�±›(»”é§Ýø"ûãveñ$ñj±_äBÅ¹G}GÒC«JÒ‘ÿ•Y{¡7‹?¯ºÎLU/jÃ¬ÞjêþÔNûu
_¬×m\¸^äÆ?F_V76S
\ No newline at end of file
diff --git a/app/web/public/index.html b/app/web/public/index.html
index b47b0f04..2f376f2f 100644
--- a/app/web/public/index.html
+++ b/app/web/public/index.html
@@ -11,6 +11,7 @@
 <body className="flex-col flex-1 w-full min-h-screen flex opacity-0">
   <div id="root"></div>
   <script src="/index.js"></script>
+  <script>window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { isDisabled: true };</script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/app/web/src/base/load/proxy.ts b/app/web/src/base/load/proxy.ts
index dfa88a40..f8ace16c 100644
--- a/app/web/src/base/load/proxy.ts
+++ b/app/web/src/base/load/proxy.ts
@@ -1,7 +1,7 @@
 (BigInt.prototype as any).toJSON = function (): string {
   return `BigInt::` + this.toString();
 };
-let w = window;
+let w = window as any;
 
 export const fetchViaProxy = async (
   url: string,
@@ -64,7 +64,7 @@ export const fetchViaProxy = async (
       return raw;
     }
   } else {
-    const res = await fetch(`/_proxy`, {
+    const res = await fetch(`${w.basehost ? w.basehost : ""}/_proxy`, {
       method: "POST",
       body: JSON.stringify([
         {
diff --git a/app/web/src/index.tsx b/app/web/src/index.tsx
index 1ec2d41c..d476197d 100644
--- a/app/web/src/index.tsx
+++ b/app/web/src/index.tsx
@@ -9,38 +9,37 @@ import { registerMobile } from "./render/live/logic/mobile";
 import { sworkerAddCache, sworkerRegister } from "./sworker-boot";
 import { w } from "./utils/types/general";
 
-
 const start = async () => {
-	const base = `${location.protocol}//${location.host}`;
-	let react = {
-		root: null as null | ReactRoot,
-	};
-	w.mobile = registerMobile();
+  let react = {
+    root: null as null | ReactRoot,
+  };
+  w.mobile = registerMobile();
 
-	const cur = new URL(location.href);
-	const base_url = `${cur.protocol}//${cur.host}`;
-	w.db = dbProxy(base_url);
+  const cur = new URL(w.basehost || location.href);
+  const base_url = `${cur.protocol}//${cur.host}`;
+  w.db = dbProxy(base_url);
 
-	try {
-		await loadApiProxyDef(base_url, false);
-		w.api = apiProxy(base_url);
-	} catch (e) {
-		console.warn("Failed to load API:", base_url);
-	}
 
-	w.serverurl = base;
+  try {
+    await loadApiProxyDef(base_url, false);
+    w.api = apiProxy(base_url);
+  } catch (e) {
+    console.warn("Failed to load API:", base_url);
+  }
 
-	sworkerRegister(react);
-	defineReact();
-	await defineWindow(false);
-	sworkerAddCache(base);
+  w.serverurl = base_url;
 
-	const el = document.getElementById("root");
+  sworkerRegister(react);
+  defineReact();
+  await defineWindow(false);
+  sworkerAddCache(base_url);
 
-	if (el) {
-		react.root = createRoot(el);
-		react.root.render(<Root />);
-	}
+  const el = document.getElementById("root");
+
+  if (el) {
+    react.root = createRoot(el);
+    react.root.render(<Root />);
+  }
 };
 
 start();
diff --git a/app/web/src/nova/ed/logic/ed-sync.tsx b/app/web/src/nova/ed/logic/ed-sync.tsx
index 98478ce3..fe2f456e 100644
--- a/app/web/src/nova/ed/logic/ed-sync.tsx
+++ b/app/web/src/nova/ed/logic/ed-sync.tsx
@@ -26,9 +26,13 @@ export const edInitSync = (p: PG) => {
     location.href = "/login";
     return <Loading note="logging in" />;
   }
-  p.user.id = session.data.user.id;
-  p.user.username = session.data.user.username;
-
+  if (session?.data?.user) {
+    p.user.id = session.data.user.id;
+    p.user.username = session.data.user.username;
+  } else {
+    p.user.id = "ab1390f5-40d5-448e-a8c3-84b0fb600930";
+    p.user.username = "anonymous";
+  }
   if (!params.page_id) {
     if (location.pathname.startsWith("/vi/")) {
       p.preview.show_loading = false;
@@ -164,7 +168,7 @@ export const edInitSync = (p: PG) => {
   }
   if (!p.sync) {
     clientStartSync({
-      user_id: session.data.user.id,
+      user_id: p.user.id,
       site_id: params.site_id,
       page_id: params.page_id,
       events: {
@@ -250,7 +254,7 @@ export const edInitSync = (p: PG) => {
             if (res) {
               Y.applyUpdate(doc, decompress(res.diff), "sv_remote");
               if (data.type === "page") {
-                delete p.preview.meta_cache[data.id]
+                delete p.preview.meta_cache[data.id];
                 await treeRebuild(p, { note: "sv_remote" });
               } else if (data.type === "comp") {
                 const updated = await updateComponentMeta(p, doc, data.id);
diff --git a/app/web/src/nova/ed/panel/side/style/tools/dynamic-import.ts b/app/web/src/nova/ed/panel/side/style/tools/dynamic-import.ts
index dd64a44f..ccc89a1d 100644
--- a/app/web/src/nova/ed/panel/side/style/tools/dynamic-import.ts
+++ b/app/web/src/nova/ed/panel/side/style/tools/dynamic-import.ts
@@ -1,3 +1,5 @@
+import { w } from "../../../../../../utils/types/general";
+
 function toAbsoluteURL(url: string) {
   const a = document.createElement("a");
   a.setAttribute("href", url); // <a href="hoge.html">
@@ -28,7 +30,7 @@ export function importModule(url: string) {
       resolve((window as any)[vector]);
       destructor();
     };
-    const absURL = toAbsoluteURL(url);
+    const absURL = w.basehost ? w.basehost + url : toAbsoluteURL(url);
     const loader = `import * as m from "${absURL}"; window.${vector} = m;`; // export Module
     const blob = new Blob([loader], { type: "text/javascript" });
     script.src = URL.createObjectURL(blob);
diff --git a/app/web/src/nova/vi/load/load-legacy.tsx b/app/web/src/nova/vi/load/load-legacy.tsx
index fb2a4a80..a6b21aa1 100644
--- a/app/web/src/nova/vi/load/load-legacy.tsx
+++ b/app/web/src/nova/vi/load/load-legacy.tsx
@@ -1,7 +1,7 @@
 import { apiProxy } from "../../../base/load/api/api-proxy";
 import { loadApiProxyDef } from "../../../base/load/api/api-proxy-def";
 import { dbProxy } from "../../../base/load/db/db-proxy";
-import importModule from "../../../render/editor/tools/dynamic-import";
+import importModule from "../../ed/panel/side/style/tools/dynamic-import";
 import { VG } from "../render/global";
 import { viScriptArg } from "../render/script/arg";
 
diff --git a/app/web/src/utils/sync/ws-client.ts b/app/web/src/utils/sync/ws-client.ts
index 39008898..a72b53c4 100644
--- a/app/web/src/utils/sync/ws-client.ts
+++ b/app/web/src/utils/sync/ws-client.ts
@@ -150,7 +150,7 @@ const connect = (
       if (!conf.ws) {
         let reconnect = 0;
         const retry = () => {
-          const url = new URL(location.href);
+          const url = new URL(w.basehost || location.href);
           url.pathname = "/sync";
           url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
 
diff --git a/app/web/src/utils/types/general.ts b/app/web/src/utils/types/general.ts
index 0295a17b..4dd285ee 100644
--- a/app/web/src/utils/types/general.ts
+++ b/app/web/src/utils/types/general.ts
@@ -23,6 +23,7 @@ export type PrasiAPI = {
 export const w = window as unknown as {
   isEditor: boolean;
   isMobile: boolean;
+  basehost?: string;
   basepath: string;
   navigateOverride: (s: string) => string;
   isDesktop: boolean;
diff --git a/pkgs/core/api/_proxy.ts b/pkgs/core/api/_proxy.ts
index 584c2ff8..e237f430 100644
--- a/pkgs/core/api/_proxy.ts
+++ b/pkgs/core/api/_proxy.ts
@@ -1,5 +1,5 @@
-import { g } from "utils/global";
 import { gzipAsync } from "../../../app/srv/ws/sync/entity/zlib";
+import { CORS_HEADERS } from "../server/serve-api";
 
 export const _ = {
   url: "/_proxy/*",
@@ -38,6 +38,6 @@ export const _ = {
       delete headers["content-encoding"];
     }
 
-    return new Response(body, { headers });
+    return new Response(body, { headers: { ...headers, ...CORS_HEADERS } });
   },
 };
diff --git a/pkgs/core/server/api/api-ctx.ts b/pkgs/core/server/api/api-ctx.ts
index 6f3c8a13..56a81898 100644
--- a/pkgs/core/server/api/api-ctx.ts
+++ b/pkgs/core/server/api/api-ctx.ts
@@ -1,4 +1,5 @@
 import { g } from "utils/global";
+import { CORS_HEADERS } from "../serve-api";
 
 const parseQueryParams = (ctx: any) => {
   const pageHref = ctx.req.url;
@@ -69,11 +70,15 @@ export const createResponse = (existingRes: any, body: any) => {
       res.headers.set("content-type", "application/json");
     }
   }
-
+ 
   const cur = existingRes as Response;
   for (const [key, value] of cur.headers.entries()) {
     res.headers.set(key, value);
   }
 
+  for (const [k, v] of Object.entries(CORS_HEADERS)) {
+    res.headers.set(k, v);
+  }
+
   return res;
 };
diff --git a/pkgs/core/server/serve-api.ts b/pkgs/core/server/serve-api.ts
index 2a32daa4..b517e711 100644
--- a/pkgs/core/server/serve-api.ts
+++ b/pkgs/core/server/serve-api.ts
@@ -2,6 +2,11 @@ import { createRouter } from "radix3";
 import { g } from "utils/global";
 import { createResponse } from "./api/api-ctx";
 
+export const CORS_HEADERS = {
+  "access-control-allow-origin": "*",
+  "access-control-allow-headers": "content-type",
+};
+
 export const serveAPI = {
   init: async () => {
     g.router = createRouter({ strictTrailingSlash: false });
@@ -38,6 +43,12 @@ export const serveAPI = {
           });
 
       if (req.method !== "GET") {
+        if (req.method === "OPTIONS") {
+          return new Response("OK", {
+            headers: CORS_HEADERS,
+          });
+        }
+
         if (
           !req.headers.get("content-type")?.startsWith("multipart/form-data")
         ) {
@@ -82,9 +93,13 @@ export const serveAPI = {
       const finalResponse = await current.fn(...args);
 
       if (finalResponse instanceof Response) {
+        for (const [k, v] of Object.entries(CORS_HEADERS)) {
+          finalResponse.headers.set(k, v);
+        }
+
         return finalResponse;
       }
-
+ 
       if (finalResponse) {
         return createResponse(current.res, finalResponse);
       }
@@ -101,6 +116,10 @@ export const serveAPI = {
           res.headers.set(k, v);
         });
 
+        for (const [k, v] of Object.entries(CORS_HEADERS)) {
+          res.headers.set(k, v);
+        }
+
         return res;
       }
 
diff --git a/pkgs/core/server/serve-static.ts b/pkgs/core/server/serve-static.ts
index 4c5f2618..ca3bb4cb 100644
--- a/pkgs/core/server/serve-static.ts
+++ b/pkgs/core/server/serve-static.ts
@@ -3,93 +3,95 @@ import { exists, inspectTreeAsync } from "fs-jetpack";
 import { InspectTreeResult } from "fs-jetpack/types";
 import { join } from "path";
 import { watch } from "fs";
+import { CORS_HEADERS } from "./serve-api";
 
 import mime from "mime";
 import { g } from "utils/global";
 
 const web = {
-	brExists: null as null | boolean,
-	get path() {
-		if (g.mode === "dev") return "static";
-		if (this.brExists === null) {
-			this.brExists = !!exists(dir.path("app/static-br"));
-		}
-		if (this.brExists) return "static-br";
-		else return "static";
-	},
+  brExists: null as null | boolean,
+  get path() {
+    if (g.mode === "dev") return "static";
+    if (this.brExists === null) {
+      this.brExists = !!exists(dir.path("app/static-br"));
+    }
+    if (this.brExists) return "static-br";
+    else return "static";
+  },
 };
 const cache = {
-	static: {} as Record<
-		string,
-		{ type: string; content: any; compression: "" | "br" }
-	>,
+  static: {} as Record<
+    string,
+    { type: string; content: any; compression: "" | "br" }
+  >,
 };
 
 export const serveStatic = {
-	init: async () => {
-		const list = await inspectTreeAsync(dir.path(`app/${web.path}`));
-		const walk = async (
-			list: InspectTreeResult,
-			parent?: InspectTreeResult[],
-		) => {
-			if (list.type === "dir") {
-				for (const item of list.children) {
-					await walk(item, [...(parent || []), list]);
-				}
-			} else {
-				const path = join(...(parent || []).map((e) => e.name), list.name);
-				const file = Bun.file(dir.path(`app/${path}`));
-				if (await file.exists()) {
-					cache.static[path.substring(web.path.length)] = {
-						type: mime.getType(path) || "application/octet-stream",
-						compression: g.mode === "prod" ? "br" : "",
-						content: await file.arrayBuffer(),
-					};
-				}
-			}
-		};
-		if (list) {
-			await walk(list);
-		}
+  init: async () => {
+    const list = await inspectTreeAsync(dir.path(`app/${web.path}`));
+    const walk = async (
+      list: InspectTreeResult,
+      parent?: InspectTreeResult[]
+    ) => {
+      if (list.type === "dir") {
+        for (const item of list.children) {
+          await walk(item, [...(parent || []), list]);
+        }
+      } else {
+        const path = join(...(parent || []).map((e) => e.name), list.name);
+        const file = Bun.file(dir.path(`app/${path}`));
+        if (await file.exists()) {
+          cache.static[path.substring(web.path.length)] = {
+            type: mime.getType(path) || "application/octet-stream",
+            compression: g.mode === "prod" ? "br" : "",
+            content: await file.arrayBuffer(),
+          };
+        }
+      }
+    };
+    if (list) {
+      await walk(list);
+    }
 
-		if (g.mode === "dev") {
-			watch(dir.path(`app/static`), async (_, filename) => {
-				if (filename) {
-					const path = join("static", filename);
-					const file = Bun.file(dir.path(`app/${path}`));
-					if (await file.exists()) {
-						cache.static[`/${filename}`] = {
-							type: mime.getType(path) || "application/octet-stream",
-							compression: g.mode === "prod" ? "br" : "",
-							content: await file.arrayBuffer(),
-						};
-					}
-				}
-			});
-		}
-	},
-	exists: (url: URL) => {
-		return !!cache.static[url.pathname];
-	},
-	serve: (url: URL) => {
-		let file = cache.static[url.pathname];
-		if (file) {
-			return new Response(file.content, {
-				headers: {
-					...{ "content-type": file.type },
-					...(file.compression ? { "content-encoding": file.compression } : {}),
-				},
-			});
-		}
+    if (g.mode === "dev") {
+      watch(dir.path(`app/static`), async (_, filename) => {
+        if (filename) {
+          const path = join("static", filename);
+          const file = Bun.file(dir.path(`app/${path}`));
+          if (await file.exists()) {
+            cache.static[`/${filename}`] = {
+              type: mime.getType(path) || "application/octet-stream",
+              compression: g.mode === "prod" ? "br" : "",
+              content: await file.arrayBuffer(),
+            };
+          }
+        }
+      });
+    }
+  },
+  exists: (url: URL) => {
+    return !!cache.static[url.pathname];
+  },
+  serve: (url: URL) => {
+    let file = cache.static[url.pathname];
+    if (file) {
+      return new Response(file.content, {
+        headers: {
+					...CORS_HEADERS,
+          ...{ "content-type": file.type },
+          ...(file.compression ? { "content-encoding": file.compression } : {}),
+        },
+      });
+    }
 
-		file = cache.static["/index.html"];
-		if (file) {
-			return new Response(file.content, {
-				headers: {
-					...{ "content-type": file.type },
-					...(file.compression ? { "content-encoding": file.compression } : {}),
-				},
-			});
-		}
-	},
+    file = cache.static["/index.html"];
+    if (file) {
+      return new Response(file.content, {
+        headers: {
+          ...{ "content-type": file.type },
+          ...(file.compression ? { "content-encoding": file.compression } : {}),
+        },
+      });
+    }
+  },
 };
