← All projects PocketForge
PiSquareLabs · Hackathon proof of concept

PocketForge

Build your own apps. Anytime. Anywhere. Describe an app, import your files, and get an installable offline PWA — generated by an LLM, served from IndexedDB by a service worker so Chrome can install it.

BUILDER PWA (APP/)STORAGE & CLOUDSERVICE WORKER (SW.JS)OUTPUTPrompt + files + options — text/MD/CSV/JSON ≤ 2 MB; theme, language, layout, featuresPrompt + files + optionstext/MD/CSV/JSON ≤ 2 MB;theme, language, layout,featuresllm() client — POST with X-API-Key; up to 4 passes “continue where you stopped”llm() clientPOST with X-API-Key; up to 4passes “continue where youstopped”extractHtml() — strip fence, trim to <!doctype>…</html>, validate, derive titleextractHtml()strip fence, trim to<!doctype>…</html>,validate, derive titleSandboxed preview — iframe srcdoc, restrictive sandbox; chat edits re-enter llm()Sandboxed previewiframe srcdoc, restrictivesandbox; chat edits re-enterllm()LLM API — Render-hosted POST /chat; the key stays in the browser, not in the repoLLM APIRender-hosted POST /chat;the key stays in thebrowser, not in the repoIndexedDB — db “pocketforge”, store projects: html, name, iconsIndexedDBdb “pocketforge”, storeprojects: html, name, iconslocalStorage shim — pf:<id>: prefix per generated app; memory fallback in sandboxlocalStorage shimpf:<id>: prefix pergenerated app; memoryfallback in sandboxserveProject() — index.html (+ install button), manifest.webmanifest, icon-192/512serveProject()index.html (+ installbutton),manifest.webmanifest,icon-192/512Fetch router — same-origin GET only; /p/<id>/ vs app shellFetch routersame-origin GET only;/p/<id>/ vs app shellShell cache — pocketforge-v1, stale-while-revalidate, offline fallbackShell cachepocketforge-v1, stale-while-revalidate, offline fallbackInstallable PWA — opens at /p/<id>/ — Chrome “Install app”Installable PWAopens at /p/<id>/ — Chrome“Install app”Export .zip — hand-rolled stored (uncompressed) zip for any static hostExport .ziphand-rolled stored(uncompressed) zip for anystatic hostmessageshtmlsavegetById/p/<id>/elseserved
Builder UIService workerStorageLLMExternal
No build step, no backend of its own: a static folder with three runtime files plus the app. Hover to trace a request. Export .zip packages the stored project for any static host; each generated app’s localStorage is namespaced by the shim.
The idea

From a sentence to an installed app

Most “AI app builders” need a backend and a deploy step. PocketForge’s trick is that the service worker is the server: generated apps live in IndexedDB and are served at same-origin URLs the browser treats as installable PWAs.

0
files in the whole repo
0
build steps
0
max LLM continuation passes
0
per imported source file
System architecture

Three runtime files and a static host

Layout: index.html (landing), app/ (the builder PWA), sw.js and pf-shared.js (service worker + shared IndexedDB and storage helpers, loaded by both the window and the worker via importScripts).

◌

One shared module

pf-shared.js exposes the IndexedDB wrapper (all/get/put/del), served-HTML builder, manifest builder and storage shim to both contexts.

⚿

Key never in the repo

The API key is pasted into Settings and kept in the browser’s localStorage; requests carry it as X-API-Key. The Render instance may need up to a minute to wake.

⌂

Offline first

The builder shell is cached (pocketforge-v1); generated apps work offline once installed.

Lifecycle

Describe → Generate → Preview → Edit → Install

Click a step.

Prompt + imported files + options

A strict system prompt asks for one self-contained index.html: no CDNs, fonts, remote images or network requests; vanilla JS; phone-first; state in localStorage; source files embedded as initial data — never inventing facts not in the material.

system: “Output ONE self-contained index.html… Nothing external… works fully offline.”

LLM call with continuation

If the model stops for length, the client re-sends the transcript and asks it to continue — up to four passes — and stitches the output. extractHtml trims to a valid document and repairs a missing close tag.

pass 1 finish_reason: length pass 2 “Continue exactly where you stopped.” pass 2 finish_reason: stop

Sandboxed iframe

The generated HTML is shown in an iframe with a restrictive sandbox (allow-scripts allow-forms allow-modals allow-popups allow-downloads), using srcdoc; inside it storage falls back to memory.

<iframe sandbox="allow-scripts …" srcdoc="…generated app…">

Ask for changes

Follow-up requests go through the same llm() path with the conversation history, producing a revised document that replaces the stored project.

you: “make the cards green, add a search box” → new html → IndexedDB → preview refresh

Own URL, own manifest, own icon

Install opens /p/<id>/, served by the service worker from IndexedDB with a generated manifest and canvas-drawn icons; a floating “Install app” button handles beforeinstallprompt. Or export a host-anywhere .zip.

GET /p/k3x9/ → servedHtml(project) GET /p/k3x9/manifest.webmanifest GET /p/k3x9/icon-512.png
Service worker

The router that makes apps installable

REQUESTROUTEGENERATED APPPOCKETFORGE SHELLfetch event — GET, same originfetch eventGET, same originmatches /p/<id>(/…)? — bare /p/<id> → 301 to trailing slashmatches /p/<id>(/…)?bare /p/<id> → 301 to trailingslashIndexedDB lookup — 404 “App not found” if missing; Cache-Control: no-storeIndexedDB lookup404 “App not found” ifmissing; Cache-Control: no-storecache.match — hit → return, revalidate in backgroundcache.matchhit → return, revalidate inbackgroundnetwork — offline + navigate → cached app/index.htmlnetworkoffline + navigate → cachedapp/index.htmlyesnomiss
DecisionStoreNetwork
This is why Chrome can install a generated app: it gets a real URL, a web-app manifest and icons, all synthesised from the stored project.
Isolation, honestly

What the namespace shim does — and doesn’t

All generated apps share the PocketForge origin. Each gets a localStorage namespace (pf:<id>:) by redefining window.localStorage, and falls back to in-memory storage inside the sandboxed preview.

PropertyStatus
Per-app localStorage keysnamespaced
Hard isolation between appsno — same origin; fine for a PoC
Preview sandboxiframe sandbox + srcdoc
Before shippinguse a separate origin per app
Vanilla JSService WorkerIndexedDBWeb App ManifestCanvas iconsStatic zip writerRender-hosted LLM API
← PreviousBad Batch Tracer Next →PROBE