{"id":44886242,"url":"https://github.com/x0root/float","last_synced_at":"2026-02-17T17:34:46.627Z","repository":{"id":336982244,"uuid":"1151378409","full_name":"x0root/float","owner":"x0root","description":"Runs Linux containers in your browser via WebAssembly.","archived":false,"fork":false,"pushed_at":"2026-02-14T09:02:48.000Z","size":9928,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T16:42:39.754Z","etag":null,"topics":["container","containerization","debian","docker","linux","vm","wasm","webvm"],"latest_commit_sha":null,"homepage":"https://float-kohl.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/x0root.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-06T11:47:10.000Z","updated_at":"2026-02-14T09:02:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"40c6e62e-f6c1-4e7e-8a30-ac73d846d46e","html_url":"https://github.com/x0root/float","commit_stats":null,"previous_names":["x0root/float"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/x0root/float","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x0root%2Ffloat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x0root%2Ffloat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x0root%2Ffloat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x0root%2Ffloat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/x0root","download_url":"https://codeload.github.com/x0root/float/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/x0root%2Ffloat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29551257,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T14:33:00.708Z","status":"ssl_error","status_checked_at":"2026-02-17T14:32:58.657Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["container","containerization","debian","docker","linux","vm","wasm","webvm"],"created_at":"2026-02-17T17:34:45.211Z","updated_at":"2026-02-17T17:34:46.613Z","avatar_url":"https://github.com/x0root.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.png\" alt=\"Float Logo\" width=\"200\"\u003e\n\u003c/p\u003e\n\n![License](https://img.shields.io/badge/license-MIT-blue)\n![WebAssembly](https://img.shields.io/badge/WebAssembly-654FF0?logo=webassembly\u0026logoColor=white)\n![SvelteKit](https://img.shields.io/badge/SvelteKit-FF3E00?logo=svelte\u0026logoColor=white)\n![Node.js](https://img.shields.io/badge/Node.js-339933?logo=node.js\u0026logoColor=white)\n![Puppeteer](https://img.shields.io/badge/Puppeteer-40B5A4?logo=puppeteer\u0026logoColor=white)\n\n[![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fx0root%2Ffloat)\n[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/x0root/float)\n\nNote: Float runs on Vercel, but you can't create multiple containers/VMs there (the Manager spawns child processes/ports).\n\n# Float - Browser-Based Linux Container Platform\n\nRun Linux containers in your browser via WebAssembly. Debian runs client-side; optional server endpoints provide a command API and an HTTP gateway.\n\nFloat is an open-source, client-side containerization platform that runs full Linux environments directly in the browser through WebAssembly. Built on CheerpX's x86 emulation and JIT engine, it enables developers to spin up multiple isolated Debian containers from a single disk image, all running entirely on the client side without backend infrastructure.\n\nWith its container management API and Docker-inspired orchestration, Float lets developers programmatically control, connect to, and manage containers through a familiar interface, while offloading compute costs from servers to users' browsers. \n\n---\n\n## Features\n\n- **Client-side VM execution**: CheerpX runs in the browser; no backend required to execute the VM.\n- **Debian and Alpine images**: Debian terminal route and Alpine GUI route.\n- **Headless runner**: Optional Puppeteer automation for API-driven execution.\n- **HTTP gateway**: Optional server-side proxy so the VM can fetch external URLs.\n- **Disk streaming**: Root filesystem is an ext2 image streamed on demand.\n\n---\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                    BROWSER (Client)                        │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │\n│  │   Float     │  │   xterm.js  │  │   Service Worker    │ │\n│  │  (Svelte)   │  │  Terminal   │  │ (Lazy Disk Loading) │ │\n│  └──────┬──────┘  └─────────────┘  └─────────────────────┘ │\n│         │                                                    │\n│  ┌──────▼────────────────────────────────────────────────┐ │\n│  │         CheerpX JIT Engine (WebAssembly)               │ │\n│  │  • x86 → Wasm JIT Compilation                        │ │\n│  │  • Linux syscall emulation                           │ │\n│  │  • ext2 filesystem support                           │ │\n│  └──────┬─────────────────────────────────────────────────┘ │\n└─────────┼────────────────────────────────────────────────────┘\n          │\n          ▼\n┌─────────────────────────────────────────────────────────────┐\n│                SERVER (Optional - for API)                   │\n│  ┌─────────────────┐  ┌──────────────────────────────────┐ │\n│  │  Command Queue  │  │      HTTP Gateway (Internet)     │ │\n│  │   API Bridge    │  │  /api/gateway → proxy to web    │ │\n│  └─────────────────┘  └──────────────────────────────────┘ │\n└─────────────────────────────────────────────────────────────┘\n```\n\n### How It Works\n\n1. **CheerpX Virtual Machine**: The core engine JIT-compiles x86 instructions to WebAssembly in real-time. It emulates Linux syscalls and provides virtual block devices and network interfaces.\n\n2. **Disk Images**: Linux filesystems (ext2 format) are streamed on-demand over WebSocket connections. Only the disk sectors needed by running processes are fetched, with local caching via IndexedDB for performance.\n\n3. **HTTP Gateway**: A server-side proxy endpoint (`/api/gateway`) allows the VM to fetch external web resources. Requests are initiated from inside the VM by printing marker strings in the terminal output. The host UI intercepts markers and performs the fetch.\n\n4. **Headless Runner**: A Puppeteer-based automation script runs the VM in a headless Chrome instance, enabling API-driven command execution without a visible browser window.\n\n5. **Container Management API**: REST endpoints allow programmatic control - submit commands, retrieve output, and manage multiple container instances remotely.\n\n---\n\n## Quick Start\n\n### 1. Installation\n```bash\ngit clone https://github.com/x0root/float\ncd float\nnpm install\n```\n\n### 2. Configuration (`.env`)\nCreate a `.env` file to configure your environment:\n\n```ini\n# Security: Key required to authorize API commands\nAPI_KEY=your-secret-api-key-here\n\n# Storage: WebSocket URL for the Debian disk image\n# Default: wss://disks.webvm.io/debian_large_20230522_5044875331.ext2\nDISK_SOURCE=wss://disks.webvm.io/debian_large_20230522_5044875331.ext2\n\n# Manager dashboard login (optional)\nMANAGER_USER=manager\nMANAGER_PASSWORD=change-me\n\n# Optional. If not set, the manager session token signing secret uses API_KEY.\n# MANAGER_SESSION_SECRET=...\n```\n\n### 3. Development Server\n```bash\nnpm run dev\n# Access UI: http://localhost:5173 (Debian Terminal)\n# Alpine GUI: http://localhost:5173/alpine\n```\n\n### 4. Headless Mode (Background Execution)\n```bash\n# Auto-runs with `npm run dev` or separately:\nnpm run headless\n```\n\n---\n\n## Manager Dashboard (WebVM Instance Management)\n\nFloat includes a manager dashboard that can spawn and control multiple independent WebVM instances.\n\n### What the manager does\n\n- **Create VM**: starts a dedicated `vite preview` server on its own port and a dedicated headless runner for background execution.\n- **Terminate**: stops the VM (kills both the per-VM preview server and the headless runner).\n- **Start**: starts a previously terminated VM again.\n- **Rename**: changes the displayed name.\n- **Delete**: removes the VM record from the manager list.\n\n### Access\n\n- **Dashboard**: `http://localhost:5173/manager`\n- **Login**: `http://localhost:5173/manager/login`\n\nThe manager uses an HTTP-only cookie session. All manager API routes require being logged in.\n\n### Per-VM URLs and ports\n\nEach created VM has its own URL (example):\n\n- `http://localhost:48566/`\n\nThe manager will show a clickable **Run at** link that passes `?api_key=...` so the VM UI can automatically configure the API key for gateway features.\n\n### Per-VM headless logs\n\nEach per-VM headless runner writes logs to:\n\n- `.webvm-logs/\u003cvmId\u003e.headless.log`\n\nThis is the first place to check if commands are stuck or the runner fails to start.\n\n### Serverless (Vercel) limitation\n\nThe **Manager dashboard VM spawning feature** is designed for a persistent Node.js server.\n\nOn serverless platforms like **Vercel**, the manager will not be able to create or reliably “detect” VMs because:\n\n- **No long-running child processes**: the manager spawns per-VM processes (a `vite preview` server and a Puppeteer headless runner). Serverless functions cannot keep these running.\n- **No shared in-memory state**: the VM registry is stored in-memory, which does not persist across serverless invocations/cold starts.\n- **No dynamic localhost ports**: per-VM URLs are `http://localhost:\u003cport\u003e` and serverless platforms do not expose arbitrary ports.\n\n#### Recommended deployment options\n\n- **Local / VPS / persistent VM**: run the manager on a machine that can spawn and keep processes alive.\n- **Vercel for UI + external runners**: host the UI on Vercel, but run the headless runners and manager on a persistent host and store registry state in an external DB/Redis.\n\n---\n\n## Usage Guide\n\n### Interactive Terminal\nThe default route (`/`) provides a full Debian terminal running `/bin/bash` with a complete user environment (vim, python, curl, etc.).\n\n### Alpine Linux GUI\nVisit `/alpine` for a graphical Alpine Linux experience with `/sbin/init` and display support.\n\n### Remote API Control\nExecute commands programmatically via the headless runner:\n\n```bash\n# Submit command\ncurl -X POST \"http://localhost:5173/api?api_key=YOUR_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"cmd\":\"python3 -c \\\"print(2+2)\\\"\"}'\n```\n\n#### PowerShell example\n\n```powershell\n$params = @{\n    Uri         = \"http://localhost:48566/api?api_key=YOUR_KEY\"\n    Method      = \"POST\"\n    ContentType = \"application/json\"\n    Body        = (@{ cmd = 'python3 -c \"print(2*15)\"' } | ConvertTo-Json)\n}\n\nInvoke-RestMethod @params\n```\n\n### Command API (`/api`)\n\nThe command API is a queue:\n\n- **POST `/api?api_key=...`**\n  - Send `{ \"cmd\": \"...\" }` to enqueue a command.\n  - By default it waits up to 60 seconds for the headless runner to complete the command.\n  - If it times out waiting, it returns `status: pending` and you can poll.\n\n- **GET `/api?action=pending\u0026api_key=...`**\n  - Used by the headless runner (CommandExecutor) to fetch the next command.\n\n- **GET `/api?commandId=...\u0026api_key=...`**\n  - Poll the result of a command.\n\n### HTTP Gateway (`/api/gateway`)\n\n- **POST `/api/gateway?api_key=...`**\n  - Used by the VM UI to proxy URL fetches from inside the VM.\n  - This enables `curl`, `wget`, and related workflows via the marker protocol described below.\n\n### Manager API (`/api/manager/webvm`)\n\n- **GET `/api/manager/webvm`**\n  - Returns the current VM list (plus the API key for generating \"Open\" links).\n\n- **POST `/api/manager/webvm`**\n  - Body contains `action`:\n    - `create`: `{ action, name, port?, diskSource? }`\n    - `terminate`: `{ action, id }`\n    - `start`: `{ action, id }`\n    - `rename`: `{ action, id, newName }`\n    - `delete`: `{ action, id }`\n\nThe manager API requires being logged into the manager dashboard (cookie session).\n\n### Internet Access from VM\nRun standard networking commands inside the VM:\n\n```bash\ncurl https://example.com\nwget https://example.com/file.txt\n```\n\nThese requests are intercepted via terminal markers (`__FETCH__...__ENDFETCH__`), forwarded through the HTTP Gateway, and responses are injected back into the terminal output.\n\n### Network Gateway: Technical Details\n\n#### What the gateway is\nThe gateway is a normal HTTP endpoint implemented in SvelteKit:\n\n- **Endpoint**: `POST /api/gateway?api_key=...`\n- **Purpose**: fetch a URL from the host environment and return data to the VM.\n\nThe VM itself does not have a full network stack or DNS in the browser sandbox. Instead, the UI proxies web requests.\n\n#### How it works in this project (end-to-end)\n\n- **1) A VM-side helper emits markers**\n  - A small Python script is written into the VM as `/tmp/net` (see `src/lib/net_gateway.js`).\n  - When you run `/tmp/net curl https://example.com`, it does not open sockets.\n  - Instead it prints a marker to stdout like `__FETCH__https://example.com__ENDFETCH__` and waits for a response terminated by `__ENDRESPONSE__`.\n\n- **2) The browser UI intercepts those markers**\n  - The main VM UI component (`src/lib/WebVM.svelte`) scans the terminal output stream for these markers.\n  - When it finds one, it extracts the URL and calls `handleFetch(...)`.\n\n- **3) The UI calls the host gateway endpoint**\n  - `handleFetch(...)` performs a `fetch('/api/gateway?api_key=...')` POST with JSON `{ url, method, responseType }`.\n  - The API key is taken from `localStorage['webvm-api-key']` (or from `?api_key=...` which the UI seeds into localStorage).\n\n- **4) The server performs the real network request**\n  - The SvelteKit endpoint (`src/routes/api/gateway/+server.js`) validates the API key and runs `fetch(targetUrl)` from the host environment (Node).\n  - It returns the response as JSON:\n    - `data` for text\n    - `data_b64` for binary (base64)\n    - `headers/status/statusText` for HEAD/header-only mode\n\n- **5) The UI injects the response back into the VM session**\n  - The UI writes the returned data back into the VM’s stdin/terminal stream and appends `__ENDRESPONSE__`.\n  - The VM-side helper reads until it sees `__ENDRESPONSE__` and then prints/saves the content.\n\nThis is intentionally a **URL-fetch bridge**, not a full TCP/UDP network stack.\n\n#### Marker protocol\nThe WebVM terminal output is scanned for markers:\n\n- **Text fetch**: `__FETCH__\u003curl\u003e__ENDFETCH__`\n- **Header-only (HEAD) fetch**: `__FETCH_HEAD__\u003curl\u003e__ENDFETCH_HEAD__`\n- **Binary fetch (base64-encoded)**: `__FETCH_B64__\u003curl\u003e__ENDFETCH_B64__`\n\nThe UI injects the response back to the VM, terminated by:\n\n- `__ENDRESPONSE__`\n\n#### Limitations\n- This is **not** a general-purpose socket/network layer. It is a URL fetch bridge.\n- Large responses can be slow (especially binary/base64) and may hit timeouts.\n- Only the host can see the real network; the VM cannot directly resolve DNS.\n\n#### Relation to CheerpX networking customization\nCheerpX does have internal networking integration points, but they are not documented publicly yet.\n\nFloat does **not** depend on undocumented CheerpX APIs for networking.\n\nInstead, this repo implements networking as an explicit, auditable proxy layer:\n\n- The VM requests a URL by printing markers\n- The UI forwards the request to a normal HTTP endpoint\n- The endpoint does the real network `fetch` and returns data\n- The UI injects the response back\n\nThis keeps the approach portable across hosts (local Node, VPS) and avoids relying on internal CheerpX hooks.\n\n---\n\n## Security\n\n**WARNING**: Float executes binary code in your browser sandbox.\n\n- **Browser Isolation**: Runs inside WebAssembly sandbox - cannot access host OS files\n- **API Key Protection**: All `/api` endpoints require `API_KEY` authentication\n- **Network Segmentation**: VM \"localhost\" is isolated from host localhost\n- **No Privileged Operations**: All VM operations are unprivileged (uid 1000 in Debian)\n\n---\n\n## Development\n\n### Project Structure\n```\nsrc/\n├── lib/\n│   ├── WebVM.svelte          # Main VM component (CheerpX integration)\n│   ├── rpc_scripts.js        # VM-side curl/wget proxy script\n│   ├── net_gateway.js        # VM-side /tmp/net helper (curl/wget + HEAD)\n│   ├── commandExecutor.js    # API command polling\n│   ├── activities.js         # CPU/disk activity monitoring\n│   └── ...\n├── routes/\n│   ├── api/                  # API endpoints (gateway, command queue)\n│   ├── alpine/               # Alpine Linux GUI route\n│   └── remote/               # Remote console UI\n├── app.html                  # HTML shell with CheerpX loader\nscripts/\n└── headless-webvm.js         # Puppeteer automation\n```\n\n### Customizing the Debian Image with Docker\n\nThe Debian environments used by Float are built from the Dockerfiles in `dockerfiles/`.\n\n- `dockerfiles/debian_mini`\n  - Smaller package set.\n- `dockerfiles/debian_large`\n  - Larger package set.\n\nBoth Dockerfiles:\n\n- Start from an i386 Debian base image (`i386/debian:bookworm`).\n- Install packages via `apt-get install ...`.\n- Create a `user` account and set passwords (`user:password`, `root:password`).\n- Copy `./examples` into `/home/user/examples`.\n\nIf you modify the package list in either Dockerfile, it changes what tools are present inside the VM image (for example adding/removing `vim`, `python3`, `gcc`, etc.).\n\n#### How the Dockerfile affects the VM\nThe VM root filesystem is an ext2 disk image derived from the container filesystem produced by these Dockerfiles. The app points at a specific prebuilt disk image URL (see `config_public_terminal.js` and `.env`). Updating the Dockerfiles does not change the running VM until you rebuild and host a new disk image.\n\n#### High-level rebuild flow\n1. Edit `dockerfiles/debian_mini` or `dockerfiles/debian_large`.\n2. Build the Docker image.\n3. Export the filesystem and convert it into an ext2 disk image.\n4. Host the ext2 image on a server that supports range requests / streaming.\n5. Update `DISK_SOURCE` to point at your new disk image.\n\nExact image build/export steps depend on your environment and hosting choice. The key point is:\n\n- **Dockerfile change** -\u003e **new disk image build** -\u003e **update `DISK_SOURCE`**\n\n---\n\n## Contributing\n\n1. **Fork** the repository\n2. **Create** a feature branch: `git checkout -b feature/cool-thing`\n3. **Commit** your changes\n4. **Push** to the branch\n5. **Open** a Pull Request\n\n---\n\nPowered by [Leaning Technologies](https://leaningtech.com)\n\n---\n\n## Troubleshooting\n\n### VM API returns `pending` and never completes\n\nThis means the per-VM headless runner is not executing queued commands.\n\n- Check the per-VM headless log:\n  - `.webvm-logs/\u003cvmId\u003e.headless.log`\n- Ensure the VM was created from the manager while running on a persistent Node process (not serverless).\n\n### Browser shows `ERR_UNSAFE_PORT`\n\nSome ports are blocked by browsers. If you pick a blocked port, the VM UI will not load.\n\n- Let the manager auto-select a port (leave Port empty)\n- Or choose a safe port (examples: `4173`, `5173`, `5637`, and other non-blocked ports)\n\n### Windows notes\n\n- Per-VM servers are started by invoking the Vite CLI via Node directly to avoid Windows `spawn` issues.\n- If a per-VM runner fails to start, inspect the `.webvm-logs` file for the error.\n\n### Serverless platforms (Vercel)\n\nVM creation/start is disabled on serverless platforms because they cannot spawn persistent child processes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx0root%2Ffloat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fx0root%2Ffloat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx0root%2Ffloat/lists"}