The cloud became an estate to operate. Kitwork is a disagreement.
Kitwork Engine is cloud infrastructure compiled into a single Go binary. It runs a JavaScript dialect on a custom stack-based bytecode VM — with energy metering, per-tenant sandboxing, hot reload, an integrated router, a fluent query-builder over SQL, a template engine, automatic TLS, built-in rate limiting, and SSE streaming. One process hosts unlimited domains. Deploying a website means dropping a folder.
Every system starts simple — then caching brings Redis, queues bring RabbitMQ, orchestration brings Kubernetes, and the team ends up operating machinery instead of shipping product. Kitwork collapses that estate back into one runtime with one philosophy: from the language, which cannot loop forever, to the cluster, which degrades instead of dying.
Everything in this repository follows five falsifiable rules. If a feature violates one, the feature is wrong.
- What is supported behaves exactly like JavaScript. No almost. No silent nulls.
- What is removed fails at compile time, with an explanation. Absence is a statement, never a surprise.
- Every workload is bounded — by the language and by per-instruction energy metering.
- One binary is the whole platform. If it needs a second service to work, it doesn't ship.
- State outlives machines. Node RAM holds nothing precious; the database is the only memory.
Running untrusted tenant code is the defining problem of cloud infrastructure:
| Approach | Isolation | Cold boot | Footprint | Can tenant code hurt the host? |
|---|---|---|---|---|
| Containers / microVMs | OS-level | 100ms – seconds | an image per tenant | Yes — anything goes inside |
| Embedded V8 / goja | interpreter-level | ~ms | heavy or slow | Yes — while(true) needs watchdogs |
| Kitwork VM | bytecode-level | < 10ms | one Go binary | No — unbounded constructs do not compile |
Kitwork owns the entire pipeline — lexer, parser, compiler, opcodes, VM — so safety is a property of the language definition, not a patch around someone else's runtime. A tenant cannot harm a node; that single guarantee is what later allows any node to absorb any tenant.
go get github.com/kitwork/enginepackage main
import (
"log"
"github.com/kitwork/engine"
)
func main() {
// Starts the engine using server.kitwork.js as the bootstrap config
if err := engine.Run("server.kitwork.js"); err != nil {
log.Fatal(err)
}
}server.kitwork.js — the bootstrap config (run once on startup):
import { server, env } from "kitwork"
server.run({
port: env.PORT || 8080,
root: env.ROOT || "tenants", // multi-tenant root folder
hostname: "kitwork.io",
hot_reload: true,
databases: [
{
alias: "system",
type: "postgres",
host: env.DB_HOST || "localhost",
port: env.DB_PORT || 5432,
user: env.DB_USER || "postgres",
password: env.DB_PASSWORD || "your_password",
name: env.DB_NAME || "postgres",
sslmode: "disable"
}
]
})tenants/029w8decto4uabhpsmfjlxgknzqy7356riv1/kitwork.io/app.kitwork.js — your tenant application:
import { router, database } from "kitwork"
const db = database.connect("system")
router.get("/api/users").handle((req, res) => {
const users = db.table("user").list(10)
return res.json({ success: true, users: users, time: new Date().toISOString() })
})Save the tenant file — the engine recompiles and atomically swaps the bytecode in under 10ms. No build step. No restart. No toolchain.
Rule 1 governs everything supported: it behaves exactly like standard JS. Full operator set (===, ?:, %, +=, ++), real Date and Math, Object.keys/values/entries/assign, Number/String/Boolean conversion, complete String & Array method families, closures at any nesting depth — and Unicode-correct strings where indices count characters, so Vietnamese text never breaks:
orders.filter(o => o.total > 500000)
.map(o => ({ id: o.id, vat: (o.total * 0.1).toFixed(0) }))
.sort((a, b) => b.vat - a.vat)
"Phường Bến Nghé".indexOf("Bến") // 7 — character index, Unicode-safe| Removed | Why | Write instead |
|---|---|---|
while, do |
No unbounded loops on shared compute, ever | .map() / .filter() / .find() |
try / catch / throw |
One visible error path, not invisible jumps | .done(cb) / .fail(cb) |
switch |
Smaller language, fewer ways to disagree | if / else or object lookup |
class |
Data is data; behavior is functions | object literals + arrow functions |
Per Rule 2, a removed keyword produces a compile error that teaches:
assemble error: Kitwork không hỗ trợ vòng lặp 'while' (loại bỏ có chủ đích để
tránh vòng lặp vô tận). Hãy dùng .map() / .filter() / .find() trên mảng dữ liệu.
It is the same trade Starlark, CEL, and eBPF made: on shared infrastructure, provable termination is worth more than expressive power. Kitwork makes the trade in a syntax millions already know.
Imports are native. import { router } from "kitwork" and relative multi-file ESM are lowered inside the engine — no Node.js, no esbuild, no bundler step.
Full reference: ENGINE_CAPABILITIES.md
The platform is the binary. Nothing below is an add-on service — it is all in the same process:
| Built in | What it does |
|---|---|
| Router | Fluent trie router with route groups, lifecycle (handle/then/catch/finally), guards & middleware |
| Database | Query builder over SQL — parameterized, with a mandatory WHERE on update/delete; ACID transactions with automatic rollback on any VM error |
| Templates | views/ pages + layouts + partials with {{ }} bindings, rendered on the zero-VM fast path |
| Caching | .cache() RAM-LRU per route + .static() disk snapshots streamed straight to the socket |
| AutoSSL | Let's Encrypt certificates — and a single-tenant sites/<domain> mode where dropping a folder issues the cert, no config and no DB |
| Rate limiting | Typed rules (ip / user / browser / global), multi-window, scoped per-tenant or server-wide |
| Realtime (SSE) | Server-Sent Events on a Zero-VM path — the broker survives hot reload, with Last-Event-ID replay and 1-to-1 send |
| Static files | Assets served straight from disk; a .txt dropped into views/ opens automatically — no route needed |
| Isolation | Per-tenant .env, path-isolated — no tenant can read the host's or another tenant's secrets |
| Payments (VN) | Built-in NAPAS 247 / VietQR payment-QR generation |
| Outbound | fetch-style HTTP with SSRF protection; go() for bounded background work |
One process serves unlimited domains, routed by hostname. Two layouts:
tenants/<identity>/<domain>/ multi-tenant (identity from the system DB)
├─ app.kitwork.js routes & logic → compiled to bytecode
├─ views/ pages, layouts, partials, {{ bindings }}, auto-served .txt
└─ assets/ served on the zero-VM fast path
tenants/sites/<domain>/ single-tenant — no identity, no DB
Drop a folder in, point DNS at the node, the domain is live — each tenant in its own sandbox with its own energy budget. For a sites/<domain> folder the Let's Encrypt certificate is issued the moment the folder exists, with zero configuration. Deployment is rsync; rollback is git checkout.
graph TD
A[HTTP Request] --> B{Trie Router}
B -- static asset / .txt --> C[Zero-VM fast path]
B -- dynamic --> D{Static cache?}
D -- hit --> E[Stream .static file]
D -- miss --> F[VM from sync.Pool] --> G[Execute bytecode]
G --> H[DB / fetch / render]
H --> I[Snapshot cache → respond → recycle VM]
- Pipeline: hand-written recursive-descent parser → native import bundler (multi-file ESM lowered in-engine, no Node.js) → AST flattens to
uint8opcodes + constants pool → stack-based VM with lexical scope chains and per-opcode energy accounting - Zero-allocation discipline: VMs recycled via
sync.Pooland reset in place; a customvalue.Valuemodel avoidsinterface{}boxing; trie routing is O(path segments) with no regex .cache()/.static(): thread-safe RAM cache per route, and disk snapshots streamed to the socket with sequential reads — noSeek, no RAM staging- Query builder: parameterized SQL compiled in-engine (no reflection-ORM round-trips at request time), with ACID transactions and automatic rollback (QUERY_BUILDER.md)
| Layer | Mechanism |
|---|---|
| Language | Unbounded constructs rejected at compile time |
| Energy budget | Every opcode weighted; execution aborts past max_energy |
| Stack sentinel | Call depth > 64 → controlled VM error, never a Go stack overflow |
| Memory guards | String builders hard-capped; no tenant can balloon node RAM |
| Source mapping | Failures report app.kitwork.js:L53, not hex dumps |
| ACID boundaries | Any VM error → automatic rollback, zero connection leakage |
| SQL safety | Parameterized queries; update/delete without a WHERE is refused |
| Outbound | SSRF guard blocks requests to internal/loopback ranges |
| Rate limiting | Per-IP / user / browser / global, at the server and tenant layers |
Warning
Do NOT load sensitive global host credentials into the host OS environment variables. The global process environment is accessible only to the host setup VM (server.kitwork.js).
To prevent credential leakage in multi-tenant environments, every tenant's VM is strictly isolated: a tenant can only read environment variables loaded from its local .env file located inside its tenant directory (e.g. tenants/<identity>/<domain>/.env).
Measured June 2026 on an i7-11850H (8C/16T) — Go microbenchmarks for the VM core (work/bench_core_test.go), k6 for HTTP against a live multi-tenant node (methodology):
| Metric | Result |
|---|---|
| VM core throughput | ~36,500,000 instructions/s |
| Instruction latency | ~27 ns |
| HTTP throughput | 33,287 req/s · 200 concurrent VUs · real tenant route |
| Response latency under that load | p50 3.5 ms · p95 18.8 ms |
| Success rate | 100.00% (0 / 499,510 failed) |
| Cold boot — full tenant (native bundle + compile + routes) | 9.8 ms |
| Cold boot — script pipeline only (lex → parse → compile → run) | 1.7 ms |
Reproduce: go test ./work/ -bench "VMCoreOps|ColdBoot" -run xxx and k6 run k6_test.js.
No special servers — every node runs this same engine; only responsibility differs (Gateway, Coordinator, Worker):
- State outlives machines — the database is the only memory
- Correctness never rides the bus — elections are database leases, not homemade consensus
- Lose efficiency before availability — when Workers die, Coordinators execute; when Coordinators die, Gateways execute
- Every workload is bounded — the language is the cluster's immune system
Performance degrades. The system continues. The clustering layer is design-complete and being implemented in phases — full design: CLUSTER.MD, transport backbone: backbone.md.
What is Kitwork Engine? A multi-tenant cloud runtime in a single Go binary: a bounded JavaScript dialect compiled to bytecode, executed on a custom VM with energy metering, routing, database access, caching, templating, AutoSSL, rate limiting, and SSE streaming built in.
Is it Node.js-compatible?
No — deliberately. Supported syntax behaves exactly like JavaScript; unbounded constructs (while, try/catch, class) are removed by design and rejected at compile time with instructive errors. Imports are native — there is no Node.js or bundler in the pipeline.
Why not embed V8 or goja? Owning the compiler makes safety a property of the language itself — not a watchdog around someone else's runtime — and keeps cold boots under 10ms in a small binary.
Who is it for? SaaS platforms hosting untrusted tenant logic, serverless workloads needing instant cold starts, programmable API gateways, and teams who want cloud capability without a Kubernetes estate.
Is it production-ready? The engine powers live multi-tenant sites today, including built-in NAPAS 247 / VietQR payment QR generation for the Vietnamese market. The clustering layer is design-complete and being implemented in phases.
| Document | Contents |
|---|---|
| ENGINE_CAPABILITIES.md | Language reference: JS compatibility, removed keywords, cache / static / assets |
| CLUSTER.MD | Distributed architecture: invariants, roles, degradation, roadmap |
| backbone.md | QUIC-centric transport backbone: planes, invariants, phased roadmap |
| QUERY_BUILDER.md | The query-builder database layer |
| BENCHMARK.md | Load-test methodology and raw numbers |
"Logic is the soul of machines. Emotion creates civilization."
Kitwork is written the way one writes an essay — every line argued over, nothing kept that cannot be defended. It is public not because it is finished, but because it is honest: small enough to understand, strange enough to matter, and built to keep running after everything around it fails.
Kitwork Engine is open-source software licensed under the GNU Affero General Public License (AGPL-3.0).
If you wish to use the Kitwork Engine in closed-source proprietary environments or embed it into a commercial product without being bound by the copyleft requirements of the AGPL-3.0, a Commercial License is available. For licensing terms and corporate inquiries, contact: support@kitwork.org.
Huỳnh Nhân Quốc · Kitwork Foundation · AGPL-3.0 & Commercial · Sponsor