The Appy Pie AI app builder alternative where the agent signs in as you, using cookies from the Chrome you already use.
Appy Pie is a template picker. It has no VM, no browser, and no way to carry your logged-in state anywhere. mk0r ships a paired browser extension that forwards cookies through /api/cookie-bridge/ingest into the in-VM Chromium, where a CDP Storage.setCookies call lets the agent (and anything it builds) act as you on sites you already use.
What every page about Appy Pie skips
Read the top results for appypie ai app builder. Every one of them describes the same loop: describe your idea, the AI picks a template, a draft app is generated in sixty seconds, Appy Pie submits it to Play and the App Store. These pages are correct. They are also silent on what the AI can actually do for the user after the app exists.
Appy Pie's output is a published mobile app. It does not have a browser, it does not run an agent, and it has no idea that the user has a Chrome with saved logins for Notion, Linear, Stripe, or anywhere else. Even the most generous reading of Appy Pie's AI does not include a path where the builder takes action on sites where the user has an account.
mk0r does the opposite thing. It ships a browser extension that pairs to the user's sandbox, forwards cookies from the real Chrome into an in-VM Chromium over CDP, and lets the agent use those cookies. The rest of this page is where that lives in the code.
Anchor fact
The VM endpoint /inject-cookies (src/core/vm-scripts.ts line 747) opens a WebSocket to http://127.0.0.1:9222/json/version inside the E2B sandbox, reads webSocketDebuggerUrl, connects the WS, and issues a CDP Storage.setCookies with the cookies the extension sent.
File map: src/lib/cookie-bridge.ts lines 1 to 48 (primitives, TTLs, limits), src/app/api/cookie-bridge/pair/route.ts (signed-in mint of the pairing code), src/app/api/cookie-bridge/redeem/route.ts (single-use transaction, 24-hour bearer token), src/app/api/cookie-bridge/ingest/route.ts (token verification, cookie sanitization, VM forward), src/core/vm-scripts.ts lines 305 to 420 (injectCookies over CDP) and line 747 (the in-VM /inject-cookies route).
The bridge, start to finish
Four steps, four files. Each step is a real HTTP route or a real function in the repo. The pairing code lives in Firestore for ten minutes and is redeemed exactly once. After that, the browser extension uses its bearer token to push cookies on demand.
Mint a pairing code
POST /api/cookie-bridge/pair from the mk0r web app. You must be signed in and own the sessionKey. Returns a human-readable XXXX-XXXX code valid for 10 minutes.
src/app/api/cookie-bridge/pair/route.ts:46
generatePairingCode() → 8 chars, alphabet excludes I, O, 0, 1Redeem the code inside the extension
The browser extension POSTs /api/cookie-bridge/redeem with the code. A Firestore transaction marks redeemedAt and returns a 32-byte base64url bearer token, SHA-256 hashed on the server.
src/app/api/cookie-bridge/redeem/route.ts:37
tx.update(pairing, { redeemedAt: now }), tx.set(tokens/<hash>)Ingest cookies from the real browser
The extension POSTs /api/cookie-bridge/ingest with { sessionKey, cookies[] } and the bearer token. The route sanitizes each cookie down to the CDP-friendly fields, then forwards them to the in-VM ACP bridge.
src/app/api/cookie-bridge/ingest/route.ts:108
fetch(`$`${session.acpUrl}/inject-cookies`, { ... })Inject into Chromium over CDP
Inside the VM, /inject-cookies opens a WS to Chromium's CDP at 127.0.0.1:9222 and issues Storage.setCookies. Batch first for speed, per-cookie fallback if the batch has a reject.
src/core/vm-scripts.ts:352 (injectCookies)
ws.send({ id, method: 'Storage.setCookies', params })The CDP call, verbatim
This is the function inside the VM that turns a list of cookies from the user's browser into a single Chromium state. The batch path is the fast path. The per-cookie path is the fallback when Chromium rejects the batch (usually because one cookie has a bad attribute like a missing secure flag on a __Host- prefix).
Overall 60-second timeout (line 360). The WS is closed the moment the call resolves, so every ingest is a one-shot connection, not a persistent channel.
The shape of a bridged cookie
The ingest route accepts a lot of browser-extension dialects. It sanitizes each cookie down to eight fields, drops unknowns, and coerces sameSite into the CDP vocabulary before the WS call.
Parties to a single ingest
Five actors from click to cookie in the VM. The extension never talks to Chromium directly, which means the VM never has to accept an inbound CDP connection from the internet. The ACP bridge on port 3002 is the only thing that reaches port 9222, and that is a loopback hop.
One ingest call, one Storage.setCookies
The four numbers that pin the design
Read directly out of the source, not invented for this page.
The pairing alphabet has 0 characters and excludes I, O, 0, and 1. That gives 08 possible codes, which is enough that a random guess inside the 10-minute window is not a practical attack, without needing a long ugly string for the user to read.
Sources: src/lib/cookie-bridge.ts line 6 (PAIRING_TTL_MS), line 7 (TOKEN_TTL_MS), line 8 (MAX_COOKIES_PER_REQUEST), line 10 (CODE_ALPHABET); src/core/vm-scripts.ts line 310 (GET to port 9222).
The data path, as a diagram
Three sources on the left: the browser extension, the signed-in web app that mints the pairing code, and Firestore, which stores codes and tokens. Two destinations on the right: the ACP bridge inside the VM and the Chromium CDP it talks to.
Cookies in, CDP out
The security properties the bridge is built around
The bearer token is never stored plaintext on the server
generateBridgeToken() returns 32 random bytes as base64url plus a SHA-256 hash. Only the hash lives in cookie_bridge_tokens. If someone dumps Firestore, they cannot replay requests.
Pairing codes are single-use
Redeem is a Firestore runTransaction that sets redeemedAt atomically. A second POST with the same code gets 409. There is no race that lets two extensions claim one code.
Ingest checks ownership, not just the token
If the session already belongs to a user, the token's userId has to match. A stolen token cannot pivot to a different owner's sandbox. Mismatches return 403.
Cookie shape is sanitized twice
First the Next.js route drops unknown fields and coerces types (ingest/route.ts lines 85 to 101). Then the VM script re-normalizes sameSite and omits unset attributes before calling CDP (vm-scripts.ts lines 326 to 350).
Appy Pie path vs mk0r path
Two very different interpretations of “AI app builder.” The difference is not which one has nicer templates. The difference is whether the AI can do anything with the user's existing life on the web.
The agent vs the user's logged-in state
You describe an app idea. Appy Pie picks a template and fills it in. The output is a published mobile app. There is no agent after publish, no browser to carry your logins into, and no hook for the builder to read from any site you are already signed in to.
- No VM, no browser, no CDP surface
- Templates, not an agent runtime
- Your existing logins are irrelevant
- Submit to Play / App Store, done
What this unlocks, and what it still does not do
With cookies bridged into the VM
- Open a logged-in dashboard and screenshot it without you logging in again.
- Scrape data from a site you already authenticate to for the generated app.
- Test a feature against your real account without embedding your password.
- Use the same cookie set across every tab the agent opens in the VM.
What cookie bridging still does not solve
- OAuth flows that require popups or a second device are still interactive.
- HTTP-only cookies from other tabs that the extension cannot read stay out.
- Session tokens that bind to a specific IP can still reject the VM's egress.
Give the agent your browser, not your password
Pair the mk0r extension once. The code is good for 10 minutes. The token is good for 24 hours. Every cookie you forward lives inside the VM's Chromium and expires with the session.
Open mk0r →Frequently asked questions
What does Appy Pie do about logged-in state on other sites?
Nothing. Appy Pie is a no-code template builder. The app you get is generated from templates, published to Play and the App Store, and has no browser of its own and no way to import your browser identity from anywhere else. If you need an AI app that can act as you on sites you already use, a template builder is the wrong category.
What specifically does mk0r's cookie bridge transfer?
Whatever cookies the paired browser extension decides to send, sanitized on arrival. The ingest route at src/app/api/cookie-bridge/ingest/route.ts lines 85 to 101 keeps only name, value, domain, path, secure, httpOnly, sameSite, and expires. Other fields are dropped. Up to 2000 cookies per request (MAX_COOKIES_PER_REQUEST in src/lib/cookie-bridge.ts line 8).
How does the bridge put those cookies into the VM's browser?
Inside the E2B sandbox, the ACP bridge exposes POST /inject-cookies on port 3002 (src/core/vm-scripts.ts line 747). It opens a WebSocket to Chromium's browser-level CDP (GET http://127.0.0.1:9222/json/version, read webSocketDebuggerUrl, connect the WS), then issues Storage.setCookies. It tries a batch first; if the batch is rejected it falls back to per-cookie setCookies so one bad cookie does not sink the rest. See src/core/vm-scripts.ts lines 352 to 420.
How is pairing protected from other users grabbing your session?
Three layers. First, pair requires an authenticated web session and an ownership check on the sessionKey (src/app/api/cookie-bridge/pair/route.ts lines 22 to 44). Second, the 8-character pairing code lives in Firestore for 10 minutes and is redeemable exactly once: the redeem endpoint runs a Firestore transaction that marks redeemedAt (src/app/api/cookie-bridge/redeem/route.ts lines 37 to 75). Third, the returned bearer token is never stored plaintext. Only its SHA-256 hash lives in cookie_bridge_tokens, and the token rotates every 24 hours (src/lib/cookie-bridge.ts lines 28 to 36).
What stops the extension from sending cookies to the wrong session?
Every ingest call has to match both its bearer token and the sessionKey in its body. The route looks up the token's hash in Firestore, verifies the sessionKey is the one bound at redeem time, and also verifies session ownership if the session already belongs to a user (src/app/api/cookie-bridge/ingest/route.ts lines 56 to 83). Mismatches return 401 or 403.
Does the agent see these cookies too, or only the preview browser?
The cookies go into the browser-level cookie store of the one Chromium running inside the VM, so every tab the agent opens there inherits them. The agent uses the same Chromium for Playwright automation, so an agent task like 'open app.notion.so and screenshot the kanban' runs with your logged-in state present. The in-VM server cannot see the actual cookie values after the fact, they live in Chromium.
What is the actual CDP command used to inject the cookies?
Storage.setCookies with the CDP param shape built by toCdpCookieParam in src/core/vm-scripts.ts lines 335 to 350: name, value, domain, path, optional secure, httpOnly, sameSite normalized to None/Strict/Lax, and expires as a number. One WebSocket connection per ingest call, closed as soon as the batch (or per-cookie fallback) returns, with a 60-second overall timeout.
Can I revoke the bridge?
The bearer token expires after 24 hours by default (TOKEN_TTL_MS in src/lib/cookie-bridge.ts line 7). Deleting the row in Firestore's cookie_bridge_tokens collection invalidates it immediately. Re-pairing mints a new token bound to a new sessionKey.
Do I need a mk0r account to try this?
You need to be signed in on the web app to mint a pairing code (the pair route calls requireAuthOrError). You do not need an Appy Pie style email capture to try the builder itself; the anonymous path mints a browser UUID and boots a sandbox without credentials. Cookie bridging is the one feature that cares about identity, because it is moving your real browser state into the VM.
An AI app builder that can reach the sites you already use, because the agent's browser has your cookies.
Open mk0r