Skip to content

OpenIWeb v0.1.0: a lower self-hosting barrier, simpler node management

jixoai v0.1.0 openiweb

The first public release. Self-hosting without learning containers or operations: hand an MCP endpoint and one key to an AI coding agent, and applications deploy on your own node; one port open to the internet, untrusted code confined to a sandbox, every delegated key instantly revocable. Whole-node idle memory ≤ 240 MB.

Dieser Beitrag ist auf Englisch verfasst. Auf Chinesisch lesen →

$ curl -H "Host: $IWEB_BASE_HOST" http://127.0.0.1:9010/_iweb/health

OpenIWeb 0.1.0 is out (2026-09-06, the first public release). It is an open-source personal application node: self-hosting without containers, databases, or network-operations knowledge; hand the MCP endpoint and one key to an AI coding agent (Codex, Claude Code, …) and applications deploy and run on your own node; you manage everything from a browser console with one key.

Until now, running personal apps on your own server meant one of two roads: learn the whole operations stack, from containers and databases to reverse proxies, or hand your data to someone's PaaS. AI coding agents can already do most of that work; what they lack is a controlled entry: one endpoint, one key, and one clear boundary. OpenIWeb is a node built around that entry; the repo's first commit landed on 2026-08-11, and the tag came less than a month later.

One port open to the public internet

On a self-managed server, the database, the admin panels, and object storage each listen on their own socket, and every one of them is exposure you have to guard. OpenIWeb pulls the whole node behind a single port. kernel-rs (~4MB Rust static binary) listens on :8080, handling host routing, owner-key auth, and node recovery, and proxying traffic per application with WebSocket upgrade tunnels. It is the node's only published port; every other listener (RustFS, the control API, and each celld) stays on container-internal loopback, never published.

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

TLS terminates in front of the node (1Panel, Caddy, or nginx); the kernel routes by HTTP Host header only. Whole-node idle memory stays under 240 MB (RssAnon), and the bound is written into the spec (openspec/specs/node-boundary/).

How the 240 MB is measured

The node is idle (no in-flight requests) and has completed at least one fresh storage-lease renewal cycle; per-process RssAnon is sampled and summed, and reclaimable file-backed page cache (RssFile) is excluded. Measurements recorded in the spec: lima 159 MB, a cloud host at 162.3 MB, and an iMac at 231.3 MB.

Example: verify the only published port
curl -H "Host: $IWEB_BASE_HOST" http://127.0.0.1:9010/_iweb/health

9010 is any host-side mapped port; inside the container only the kernel's 8080 is exposed.

Ops gathered into one MCP endpoint

The operations entry for an AI agent is this one endpoint. mcp.<base>/mcp is a protected system application: every JSON-RPC request (including initialize and tools/list) must carry an owner key as a Bearer token, and the worker forwards the credential per request and never stores it. Wiring an agent in is one standard MCP config:

{ "mcpServers": { "OpenIWeb": { "url": "https://mcp.<base>/mcp",
  "headers": { "Authorization": "Bearer <owner-key>" } } } }

The tool surface at 0.1.0 carries 19 tools in three groups: application lifecycle (admit, prepare, readiness, activate, start, stop, rollback, versions, …), workspace read/write/delete, and domain listing/registration. Deploying, verifying, rolling back, and cleaning up all happen under the same key's authorization.

Inventory: all 19 MCP tools
application     iweb_application_list      iweb_application_start
lifecycle       iweb_application_admit     iweb_application_stop
                iweb_application_prepare   iweb_application_rollback
                iweb_application_readiness iweb_application_delete_version
                iweb_application_activate  iweb_application_inspect
                                           iweb_application_versions
                                           iweb_application_delete_data
                                           iweb_application_metrics
workspace       iweb_workspace_list        iweb_workspace_read_text
                iweb_workspace_write_text  iweb_workspace_delete
domains         iweb_domain_list           iweb_domain_register

Untrusted code only enters a sandbox

Application code may be copied from the internet or generated by AI, so it is untrusted by default; every application is isolated from the node control plane and from every other application, with no switch to turn that off. The runtime splits along that trust line.

  • The trusted tier: celld v0.3 (Cloudflare Workers API). Applications enter only via the node image you build, one process per app, watchdog soft limits only. The console and the demo apps live here.

  • The untrusted tier: iweb-wasmd (Wasmtime, wasi:http 0.2 components), the only path new code takes into the node. Isolation is enforced inside the engine, with host services only and no socket capability; apps copied from the web or generated by AI enter here.

Object storage is built-in RustFS (S3-compatible, descended from MinIO, single-node friendly), loopback-only with no console.

Layout: the four storage buckets

RustFS keeps four buckets with distinct roles: iweb-workspace, iweb-cells-<app>, iweb-apps, iweb-system.

Every key you hand out can be taken back

Keys follow the GitHub PAT model: one identity, many revocable tokens. The console issues delegated keys in the iwb_<id>_<secret> format with absolute expiry (1/7/30 days or permanent), and its copy button produces a ready-to-paste Chinese deployment prompt with the MCP endpoint and key already templated; paste it straight into any AI agent.

Revocation takes effect immediately: tickets invalidate, monitored WebSockets close, and later requests get 401. The audit trail is append-only and per-key-attributed; every control-plane operation is recorded, rejected 401 attempts included, with rotation capped at 4 MiB.

The bootstrap IWEB_API_TOKEN always authorizes and cannot be revoked: it is the credential for recovering the node directly through api.<base>. Even if every delegated key and the Admin app are compromised, the owner can still reach the Kernel control API directly.

Example: the MCP endpoint accepts exactly these keys
curl -H "Authorization: Bearer <owner-key>" https://mcp.<base>/mcp

Bootstrap and delegated keys both work; a request without a key is rejected.

The rest, and known limitations

  • Static console: a SvelteKit + shadcn-svelte static app served as celld native assets. It is itself an ordinary app, replaceable as a whole, never a secret configuration screen.

  • Three demo applications: hello (a pure static site using only celld's assets file interface), search (D1/SQLite search with parameterized SQL), and collab (two celld instances sharing one Durable Object for cross-instance realtime collaboration over WebSocket).

  • Testing: 489 bun tests; the Rust side runs cargo test and clippy; plus the kernel-recovery black-box contract suite.

Example: demo app domains and how to verify them

hello.<base> (pure static), search.<base> (D1/SQLite search), collab.<base> and collab-b.<base> (cross-instance realtime collaboration). Open collab and collab-b in two browser windows and watch messages jump across instances in real time.

Known limitations: TLS and wildcard certificates are a deployment concern (the kernel does HTTP Host-routing inside the container); monitor metrics follow the Kernel lifecycle and keep no durable history; notes is deployed but not routed; wasm publication stays fail-closed (no celld publication exists; image-only supply).

Install

This release ships as a source-built container image (no registry image published). Build from the tag:

git clone https://github.com/jixoai/openiweb.git && cd openiweb
git checkout v0.1.0
cp .env.example .env   # set CELLD_NODE, IWEB_BASE_HOST, a long random IWEB_API_TOKEN, and the S3 secrets
docker compose up -d --build
curl -H "Host: $IWEB_BASE_HOST" http://127.0.0.1:9010/_iweb/health

Multi-arch: Dockerfile (arm64) and Dockerfile.amd64 (x86_64, with CARGO_BUILD_JOBS and CRATES_MIRROR=rsproxy builder knobs for constrained hosts). Node data lives in the iweb-data volume and RustFS buckets, and configuration lives in .env, so recreating the container keeps state. Owner keys are issued by the Kernel and the console, revocable from the console, and secrets are never stored in the workspace.

Acknowledgements

The trusted tier stands on celld (DenoLand); RustFS descends from MinIO; the untrusted tier's isolation comes from Wasmtime.

Links