[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-83937":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":10,"trendingCount":16,"starSnapshotCount":16,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},83937,"kyushu","peterpeterparker\u002Fkyushu","peterpeterparker","A self-hostable Wasm sandbox for JavaScript workers","https:\u002F\u002Fkyushu.dev",null,"JavaScript",151,3,70,1,0,23,53,99,84.11,false,"main",[],"2026-06-12 04:01:42","# Kyushu\n\nEver wanted to run a Cloudflare Workers-style JavaScript handler in a sandbox, on a VPS or anywhere, without Node.js, Bun, or even Docker? Kyushu lets you do exactly that.\n\nWrite a simple `fetch` handler, build it into a self-contained WebAssembly binary, and run it anywhere with a single CLI binary - `kyu`.\n\n> [!IMPORTANT]\n> Kyushu is an early-stage experiment. Expect breaking changes, missing features, and rough edges. Not recommended for production use.\n\n## Motivation\n\nKyushu grew out of my experience building [Juno](https:\u002F\u002Fgithub.com\u002Fjunobuild\u002Fjuno), a platform where apps run in some sort of containers. I liked the concept, and when I tried Cloudflare Workers it clicked: a single function, sandboxed, handling HTTP, kind of what I implemented in the past but, for VPS or anywhere.\n\nWhen you think about it, in an era where AI agents need safe environments to execute untrusted code, having a lightweight, self-hostable Wasm sandbox could be relevant, whether for running user-defined logic, isolating third-party code, or deploying edge-like handlers on your own infrastructure.\n\nPlus, find it fun to try to avoid using Node or Bun. Long story short, felt like it was worth experimenting.\n\n## How it works\n\nKyushu has two moving parts:\n\n**The worker** is a `wasm32-wasip2` component that embeds a QuickJS JavaScript runtime. When you run `kyu build`, your TypeScript or JavaScript entry point is bundled (via [Rolldown](https:\u002F\u002Frolldown.rs)) and pre-initialized into the worker using [Wizer](https:\u002F\u002Fgithub.com\u002Fbytecodealliance\u002Fwizer). The resulting `.wasm` file contains your code, frozen in memory, ready to handle requests.\n\n**The runner** (`kyu run`) is a Rust binary powered by [Wasmtime](https:\u002F\u002Fwasmtime.io). It loads your built worker, spins up an HTTP server, and dispatches incoming requests into the Wasm sandbox. Your JavaScript runs inside the sandbox - isolated from the host filesystem and environment, except for what you explicitly allow via config.\n\n```\n┌─────────────────────────────────────────┐\n│                kyu run                  │\n│                                         │\n│  ┌──────────────────────────────────┐   │\n│  │         Wasmtime (host)          │   │\n│  │                                  │   │\n│  │  ┌────────────────────────────┐  │   │\n│  │  │   worker.wasm (sandbox)    │  │   │\n│  │  │                            │  │   │\n│  │  │  QuickJS + your JS code    │  │   │\n│  │  └────────────────────────────┘  │   │\n│  └──────────────────────────────────┘   │\n└─────────────────────────────────────────┘\n         ▲                    │\n    HTTP request         HTTP response\n```\n\nWorkers are stateless by design. Each request runs in isolation and module-level variables do not persist between requests.\n\n## Install\n\n```bash\ncurl -fsSL https:\u002F\u002Fkyushu.dev\u002Finstall | bash\n```\n\nOr download a pre-built binary from the [releases page](https:\u002F\u002Fgithub.com\u002Fpeterpeterparker\u002Fkyushu\u002Freleases).\n\n## Quick start\n\n**1. Write a worker**\n\n```typescript\n\u002F\u002F src\u002Findex.ts\nimport type { ExportedHandler } from \"kyushu-types\";\n\nexport default {\n  async fetch(request) {\n    return {\n      status: 200,\n      headers: { \"content-type\": \"application\u002Fjson\" },\n      body: JSON.stringify({ hello: \"world\" }),\n    };\n  },\n} satisfies ExportedHandler;\n```\n\n**2. Build**\n\n```bash\nkyu build\n```\n\nThis produces `worker\u002F__kyushu_worker.wasm`.\n\n**3. Run**\n\n```bash\nkyu run\n# Listening on http:\u002F\u002F0.0.0.0:5987\n```\n\n## TypeScript types\n\nInstall the types package for autocompletion:\n\n```bash\nnpm install --save-dev kyushu-types\n```\n\n## API\n\nWorkers export a default object with a `fetch` handler:\n\n```typescript\nexport default {\n  async fetch(request: WorkerRequest): Promise\u003CWorkerResponse> {\n    \u002F\u002F ...\n  },\n};\n```\n\n### `WorkerRequest`\n\n| Field     | Type                                                     | Description                       |\n| --------- | -------------------------------------------------------- | --------------------------------- |\n| `method`  | `WorkerMethod`                                           | HTTP method (`GET`, `POST`, etc.) |\n| `url`     | `string`                                                 | Full request URL                  |\n| `headers` | `Record\u003Cstring, string>` \\| `undefined`                  | Request headers                   |\n| `body`    | `string` \\| `ArrayBuffer` \\| `Uint8Array` \\| `undefined` | Request body                      |\n\n### `WorkerResponse`\n\n| Field     | Type                                                     | Description                       |\n| --------- | -------------------------------------------------------- | --------------------------------- |\n| `status`  | `number` \\| `undefined`                                  | HTTP status code (default: `200`) |\n| `body`    | `string` \\| `ArrayBuffer` \\| `Uint8Array` \\| `undefined` | Response body                     |\n| `headers` | `Record\u003Cstring, string>` \\| `undefined`                  | Response headers                  |\n\n## CLI reference\n\n```\nkyu build [config]   Bundle and pre-initialize a worker\nkyu run [config]     Run a built worker\nkyu dev [config]     Start a local development server with hot-reload\nkyu --version        Print the CLI version\n```\n\nAll commands accept an optional config file. Pass a path to override the defaults.\n\n## Config reference\n\nThe available options and their defaults:\n\n### Build\n\nConfigure the build step.\n\n```toml\n[input]\nsrc = \"src\u002Findex.ts\"           # default\n\n[output]\ndir = \"worker\"                 # default\nfile = \"__kyushu_worker.wasm\"  # default\n```\n\n| Field         | Type   | Default                | Description                                       |\n| ------------- | ------ | ---------------------- | ------------------------------------------------- |\n| `input.src`   | string | `src\u002Findex.ts`         | Path to your TypeScript or JavaScript entry point |\n| `output.dir`  | string | `worker`               | Output directory for the built worker             |\n| `output.file` | string | `__kyushu_worker.wasm` | Output filename for the built worker              |\n\n### Run\n\nConfigure the runner.\n\n```toml\n[run]\nwasm = \"worker\u002F__kyushu_worker.wasm\"  # default\nport = 5987                           # default\n\n[[worker.mounts]]\nhost = \".\"\nguest = \"\u002F\"\nwritable = true\n\n[[worker.env]]\nkey = \"API_KEY\"\nvalue = \"secret\"\n```\n\n| Field                      | Type   | Default                       | Description                                   |\n| -------------------------- | ------ | ----------------------------- | --------------------------------------------- |\n| `run.wasm`                 | string | `worker\u002F__kyushu_worker.wasm` | Path to the built worker `.wasm` file         |\n| `run.port`                 | number | `5987`                        | Port to listen on                             |\n| `worker.mounts`            | array  | —                             | Filesystem mounts to expose to the worker     |\n| `worker.mounts[].host`     | string | —                             | Path on the host filesystem                   |\n| `worker.mounts[].guest`    | string | —                             | Path inside the worker sandbox                |\n| `worker.mounts[].writable` | bool   | `false`                       | Whether the mount is writable                 |\n| `worker.env`               | array  | —                             | Environment variables to expose to the worker |\n| `worker.env[].key`         | string | —                             | Environment variable name                     |\n| `worker.env[].value`       | string | —                             | Environment variable value                    |\n\n### Dev\n\nConfigure the development server.\n\n```toml\n[dev]\nport = 5987  # default\n```\n\n| Field       | Type   | Default | Description                                     |\n| ----------- | ------ | ------- | ----------------------------------------------- |\n| `dev.port`  | number | `5987`  | Port to listen on                               |\n| `dev.watch` | bool   | `true`  | Watch for file changes and reload automatically |\n\nA custom `[input]` or `[worker]` configuration can also be applied; the details are omitted for brevity but follow the same options documented above.\n\n## Security\n\n> [!CAUTION]\n> Kyushu's sandbox is only as strong as its dependencies. Please read this before deploying anything sensitive.\n\nYour JavaScript runs inside a [Wasmtime](https:\u002F\u002Fwasmtime.io) WebAssembly sandbox, which provides strong isolation from the host system. Access to the filesystem and environment variables is gated by explicit configuration in the run config.\n\nHowever, there are important caveats:\n\n- **JavaScript polyfills**: The QuickJS runtime is extended with Node.js-compatible polyfills from the [wasm-rquickjs](https:\u002F\u002Fgithub.com\u002Fnicolo-ribaudo\u002Fwasm-rquickjs) project, which implement Node.js APIs (`fs`, `crypto`, `http`, etc.) inside the Wasm sandbox. Their security properties have not been reviewed by this project, and it is unknown whether they have been independently audited. They are a third-party dependency and are used as-is.\n- **Experimental status**: Kyushu itself has not been audited. The sandboxing boundaries, configuration parsing, and request handling are all early-stage code.\n\nUse Kyushu for experimentation, local development, and learning. Do not expose it to untrusted input in production without a thorough review.\n\n## Known Limitations\n\n### Top-level `console.log`\n\nCalls to `console.log` and other console methods at the top level of your worker module are silently swallowed. This is a side effect of Wizer pre-initialization: writing to stdout during snapshotting corrupts internal stdio state for the runtime. Only log starting from the `fetch` handler, not at module scope (which is ignored).\n\n### Dynamic `import()` at Runtime\n\nSome npm packages use dynamic `import()` internally as an escape hatch to avoid bundling certain dependencies:\n\n```js\nfunction importAtRuntime(specifier) {\n  return import(specifier);\n}\n```\n\nBundlers intentionally leave these calls untouched, and Kyushu's Wasm sandbox has no Node.js module resolution at runtime — so they'll throw a `ReferenceError` when executed.\n\n**Example:** `file-type`'s `fromFile` dynamically imports `strtok3` at runtime. Use `fromBuffer` instead:\n\n```ts\n\u002F\u002F ❌\nconst fileType = await fileTypeFromFile(filepath);\n\n\u002F\u002F ✅\nconst file = await readFile(filepath);\nconst fileType = await fileTypeFromBuffer(file);\n```\n\n**Rule of thumb:** when a package offers separate Node.js vs. browser\u002Fedge APIs, prefer the browser\u002Fedge variant.\n\n## License\n\nMIT\n",2,"2026-06-11 04:11:50","CREATED_QUERY"]