OpenIWeb is an open-source personal application node for people who don't want to learn containers, databases, or network operations. An AI coding agent (Codex, Claude Code, …) deploys and operates applications on the node through MCP; the owner manages everything from a browser console with a single key.
Applications may be copied from the internet or generated by AI, so they are untrusted by default. Isolating every application from the node control plane and from each other is the product's security bottom line.
public Internet
|
iweb-kernel :8080 the only published port (a single Rust binary)
+-- api.<base> -> Kernel control API (same router/auth as loopback)
+-- admin.<base> -> per-app celld :8787 (Admin console)
+-- mcp.<base>/mcp -> per-app celld :8797 (MCP endpoint)
+-- <app>.<base> -> per-app celld (IWEB_CELLD_PORTS)
+-- <base>/<app>/app -> path alias for the same application
|
RustFS (S3-compatible, loopback-only, no console)
|
iweb-workspace / iweb-cells-<app> / iweb-apps / iweb-system
One OpenIWeb installation is one owner's personal node. Everything except the Kernel ingress — RustFS, the control API, every celld listener — stays on container-internal loopback and is never published.
What's inside
| Layer | Choice | Why |
|---|---|---|
| Ingress & control plane | Rust kernel (kernel-rs/, ~4MB static binary) |
One published port, host routing, recovery authority, owner-key auth, per-app proxy with WebSocket upgrade tunnels |
| Trusted application runtime | celld v0.3 (Cloudflare Workers API) | Image-seeded fleet apps, one process per app, watchdog soft limits |
| Untrusted application runtime | iweb-wasmd (Wasmtime, wasi:http 0.2 components) | The only runtime admission path: engine-enforced isolation, host services, no socket capability |
| Object storage | RustFS (S3-compatible, MinIO lineage) | Single-node friendly, low memory envelope, loopback-only |
| Console | SvelteKit + shadcn-svelte static app served as celld native assets | Replaceable like any app; never a secret configuration screen |
Idle memory envelope: ≤ 240 MB RssAnon for the whole node (spec:
openspec/specs/node-boundary/).
Quick start
cp .env.example .env
# Set a unique CELLD_NODE, IWEB_BASE_HOST, a long random IWEB_API_TOKEN,
# and the MinIO-compatible root + celld S3 secrets.
docker compose up -d --build
curl -H "Host: $IWEB_BASE_HOST" http://127.0.0.1:9010/_iweb/health
Prebuilt images are also published to
ghcr.io/jixoai/openiweb
(multi-arch, built by CI on every release) if you prefer docker run or
compose pull over building locally — set IWEB_IMAGE, default in
.env.example.
IWEB_BASE_HOST is a hostname suffix only (no scheme/port/path). The
container publishes one port (8080 → map it however you like). TLS is
terminated in front of the node (1Panel, Caddy, nginx, …); the kernel routes
by HTTP Host header only.
Open the console at https://admin.<base>/ and log in with any valid owner
key (the bootstrap IWEB_API_TOKEN, or a delegated key issued in the
console — see below).
Demo applications
The image ships three reference applications that exercise the runtime end-to-end (see 中文说明 for screenshots-level detail):
| App | Host | Demonstrates |
|---|---|---|
| hello | hello.<base> |
Pure static site via celld's wrangler assets interface — no worker code |
| search | search.<base> |
D1 (SQLite) database search with parameterized SQL |
| collab | collab.<base>, collab-b.<base> |
Frontend/backend split; two celld instances share one Durable Object for cross-instance realtime collaboration over WebSocket |
Owner keys & audit
One identity, many revocable tokens (GitHub PAT model). In the console's Keys & Audit view you can:
- issue delegated keys (
iwb_<id>_<secret>) with absolute expiry, - copy a ready-to-paste Chinese deployment prompt containing the MCP endpoint and key for an AI agent,
- ban a key instantly (in-flight monitor sockets close; new requests get 401),
- read an append-only, per-key-attributed audit trail of every control-plane operation (including rejected attempts).
The bootstrap IWEB_API_TOKEN always authorizes and cannot be banned — it is
the credential face of the api.<base> recovery law: even if every delegated
key and the Admin app are compromised, the owner can still reach the Kernel
control API directly.
MCP
mcp.<base>/mcp is a protected system application. Every JSON-RPC request —
including initialize and tools/list — must carry
Authorization: Bearer <owner-key> (bootstrap or delegated). Tools cover
workspace read/write/delete and domain listing/registration. The worker
forwards the credential per request and never stores it.
Point an MCP client at it like this:
{ "mcpServers": { "iweb": { "url": "https://mcp.<base>/mcp",
"headers": { "Authorization": "Bearer <owner-key>" } } } }
Development
bun install # workspace tooling (tests are bun-native)
bun test # full battery (489 tests; proxy-immune via bunfig preload)
cd kernel-rs && cargo test && cargo clippy --all-targets # Rust battery
KERNEL_TEST_COMMAND=$PWD/kernel-rs/target/debug/iweb-kernel \
bun test tests/kernel-recovery.test.ts # black-box contract suite (also: node kernel/index.js)
openspec validate --all --strict # spec discipline
Installs always resolve from the workspace root bun.lock (lockfileVersion 2):
running bun install from inside apps/admin-console or any workspace package
walks up to the root and uses the root lock, so a subdirectory install cannot
bypass the root lock's --frozen-lockfile verification. Stale nested locks
(e.g. the pre-monorepo apps/admin-console/bun.lock) are dead weight and were
removed; the Docker build likewise installs from the root before the app
sources are even copied.
Cross-implementation contract tests drive the Rust kernel and the frozen JS
reference kernel (kernel/index.js) through the same black-box suites
(tests/kernel-recovery.test.ts, tests/kernel-browser-contract.test.ts,
tests/owner-keys.test.ts), so wire-format drift cannot ship silently. A
dedicated suite validates every kernel response against the Admin console's
own zod schemas.
Multi-arch images: Dockerfile (arm64) and Dockerfile.amd64 (x86_64, with
builder knobs CARGO_BUILD_JOBS and CRATES_MIRROR=rsproxy for constrained
hosts).
Project shape
kernel-rs/ Rust kernel (ingress, control API, proxy, keys, audit, monitor)
apps/workers/ celld applications: admin, mcp, notes, hello, search, collab
apps/admin-console/ SvelteKit console (built into the image as celld assets)
supervisor/ wasm execution supervisor (in-container, journals execution only)
kernel-rs/wasmd/ iweb-wasmd: the Wasmtime host for admitted components
packages/contracts/ shared cross-implementation contract vectors
packages/worker-shared/ worker-safe helpers (HTML escaping, JSON responses)
scripts/ node operations (probe matrix, backup, migration, portless …)
openspec/ the product law: specs/, active changes/, archive/
tests/ bun-native batteries incl. browser-contract suites
Security boundary
OpenIWeb runs a two-tier trust model. celld is the trusted tier: fleet
applications (admin, mcp, notes, hello, search, collab) enter the node only
through node images you build, run one process per app, and are bounded by a
userspace resource watchdog (soft-limit SIGKILL plus per-app restart). There
is no celld runtime admission and celld is never a hostile multi-tenant
boundary. wasm is the untrusted tier and the only runtime admission path:
arbitrary, network-sourced, or AI-generated packages execute as wasi:http 0.2
components under Wasmtime with engine-enforced limits (no socket/TLS/fs
capability, host-mediated egress, fuel/epoch/store caps) and host services
(KV/SQL/Logging) as the data plane — all self-contained in the node container.
The residual trust in wasmd/Wasmtime is explicit; law lives in
openspec/specs/application-sandbox/.
Never place secrets in the workspace: credentials live only in node environment or Kernel-issued keys.
Current limitations
- TLS/wildcard certificates are a deployment concern (kernel is HTTP Host-routing inside the container).
- Monitor metrics are per-Kernel-lifecycle, not durable history.
notesis deployed but not routed (user routes target the wasm tier only).- wasm publication stays fail-closed behind its acceptance record and switch; celld publication does not exist (image-only supply).
中文文档见 README-zh.md。完整行为规格见
openspec/specs/。
