Docker
The fast path. One command, a persistent volume, exposes :8443.
docker run -d \
--name orva \
-p 8443:8443 \
-v orva-data:/var/lib/orva \
ghcr.io/harsh-2002/orva:latest Node, Python, and TypeScript. Five kernel-enforced boundaries per call. One binary, one port, zero external dependencies.
docker run -d --name orva -p 8443:8443 ghcr.io/harsh-2002/orva:latest
Open https://localhost:8443 · onboarding takes ~30 seconds.
Everything an operator needs to run untrusted code — observed, reversible, no external dependencies, no add-on services.
Each layer is enforced by the kernel, not by Orva. Orva configures the boundaries; the kernel holds them.
UID 0 inside the sandbox maps to UID 65534 (nobody) on the host. All 64 Linux capability bits cleared — verified via /proc/self/status. The function cannot escalate to host root.
/code is a read-only bind mount of the function's versioned directory. /tmp is a private tmpfs wiped after each worker exits. No host filesystem visible.
Hard limits per function: memory.max, cpu.max, pids.max. The host refuses new spawns past 80% total memory reservation.
~150 syscalls blocked: mount, unshare, bpf, kexec_load, init_module, ptrace, and more. Blocked attempts log and kill the process — never silently fail.
Default network_mode: none — loopback only, no inbound possible. Functions opt in to network_mode: egress for outbound HTTPS via an nftables allowlist.
| Orva (nsjail) | Firecracker / VMs | Plain Docker | |
|---|---|---|---|
| Kernel | Shared with host | Separate per VM | Shared with host |
| Isolation primitive | Linux namespaces + seccomp + cgroup v2 | Hardware virtualisation (KVM) | Linux namespaces + cgroup |
| Syscall surface | ~150 syscalls blocked via Kafel policy | Near-zero (hardware boundary) | Unfiltered by default |
| Capability drop | All 64 Linux caps cleared | N/A (separate kernel) | Partial (Docker defaults) |
| Cold start | ~50–200 ms | ~125 ms (Firecracker MicroVM) | N/A |
| Memory / worker | ~30 MB | ~5 MB per MicroVM | Varies |
| Suited for | Homelabs, internal tools, trusted code | Multi-tenant cloud, untrusted code | General app containers |
Full threat model and self-verification recipe: docs/SECURITY.md . Verified end-to-end under Kata Containers 3.30 (QEMU + Cloud Hypervisor); the nsjail boundary holds unchanged with a real Linux kernel underneath.
Click any screenshot for full size.
The fast path. One command, a persistent volume, exposes :8443.
docker run -d \
--name orva \
-p 8443:8443 \
-v orva-data:/var/lib/orva \
ghcr.io/harsh-2002/orva:latest Pin a tag, declare the volume, keep your compose.yml in version control.
services:
orva:
image: ghcr.io/harsh-2002/orva:latest
restart: unless-stopped
ports:
- "8443:8443"
volumes:
- orva-data:/var/lib/orva
volumes:
orva-data: No daemon, no Docker. Single binary, systemd unit, native nsjail.
curl -fsSL https://orva.dev/install.sh | sh
sudo systemctl enable --now orva
Every handler is a default export. kv, invoke, and jobs are pre-installed
— no npm install, no pip install.
Default export an async handler. ctx.kv backed by per-function SQLite; no install needed.
export default async function (req, ctx) {
const seen = (await ctx.kv.get('count')) ?? 0;
await ctx.kv.put('count', seen + 1);
return { ok: true, seen: seen + 1 };
}
Plain async def handler(req, ctx). Async SDK on ctx — no FastAPI, no boilerplate.
async def handler(req, ctx):
seen = await ctx.kv.get('count') or 0
await ctx.kv.put('count', seen + 1)
return {'ok': True, 'seen': seen + 1}
Types ship with the runtime — import type { OrvaHandler } from 'orva' and
you're set.
import type { OrvaHandler } from 'orva';
const handler: OrvaHandler = async (req, ctx) => {
await ctx.jobs.enqueue('thumbnail', { url: req.body.url });
return { queued: true };
};
export default handler;
Full SDK surface on ctx: kv, invoke, jobs,
logger, trace, secrets. Reference docs ship in-product at /web/docs.
— Last word
No control plane. No telemetry. No vendor account. Your hardware, your code, your kernel boundaries.