PP
123ย stars 0 forks ่ดจ้‡ 70 ่ถ‹ๅŠฟ 70

โšก 30-Second Install โ€ข ๐Ÿš€ Quick Start โ€ข ๐Ÿ—๏ธ Architecture โ€ข ๐ŸŽฏ Why Pullrun? โ€ข ๐Ÿ—บ๏ธ Feature Map โ€ข โ˜ธ๏ธ Kubernetes โ€ข ๐Ÿค– AI Agents

ๆฆ‚่งˆ

โšก 30-Second Install โ€ข ๐Ÿš€ Quick Start โ€ข ๐Ÿ—๏ธ Architecture โ€ข ๐ŸŽฏ Why Pullrun? โ€ข ๐Ÿ—บ๏ธ Feature Map โ€ข โ˜ธ๏ธ Kubernetes โ€ข ๐Ÿค– AI Agents

README

โšก 30-Second Install โ€ข ๐Ÿš€ Quick Start โ€ข ๐Ÿ—๏ธ Architecture โ€ข ๐ŸŽฏ Why Pullrun? โ€ข ๐Ÿ—บ๏ธ Feature Map โ€ข โ˜ธ๏ธ Kubernetes โ€ข ๐Ÿค– AI Agents


โšก What is Pullrun?

Pullrun runs the same OCI image as a container or a VM. It stores layers in a content-addressed DAG (no overlayfs), syncs blocks peer-to-peer, and ships as a ~14 MB CLI + ~6 MB runtime daemon.

Why this matters: Modern infrastructure uses too many execution engines โ€” Docker for dev, containerd for production, Firecracker for isolation, CRI for Kubernetes, MCP agents for AI. Each has its own image format, storage, and operational model โ€” even though they all run the same OCI images. Pullrun collapses these layers into one runtime.

Key differentiators:

  • Containers and VMs from the same image โ€” no separate VM build step, no separate VM image format
  • Content-addressed DAG store โ€” zero-copy mmap reads, deduplicated by content hash, byte-identical across every node
  • P2P image distribution โ€” one registry pull per cluster, rest sync peer-to-peer at LAN speed
  • ~14 MB CLI + ~6 MB runtime daemon (stripped) โ€” no daemon required by default (CLI-only pullrun run), optional daemon for background services

Also included: Kubernetes CRI shim (beta), Docker Compose support, MCP server for AI agents, policy engine (Cosign, SBOM, seccomp), P2P sync layer, and AES-256-GCM encrypted secrets โ€” all in the same binary.

# Apple Silicon VM (macOS default) โ€” 3 s
pullrun run alpine:3.18 --cmd "echo" --cmd "hello pullrun" --attach -t

# Firecracker microVM (Linux) โ€” 400 ms
pullrun run alpine:3.18 --backend vm --cmd "echo" --cmd "hello pullrun" --attach -t

# Container (Linux) โ€” 400 ms
pullrun run alpine:3.18 --cmd "echo" --cmd "hello pullrun"

# Windows WSL2 โ€” same image, same command, same store
pullrun.exe run alpine:3.18 --cmd "echo" --cmd "hello pullrun"

๐Ÿ“ฆ Install

# One command, any platform
curl -fsSL https://github.com/pullrun/pullrun/raw/main/install.sh | bash
Platform What you get
macOS brew tap pullrun/tap && brew install pullrun โ†’ native binary, no Xcode
Linux APT package or direct download, systemd service (requires runc for containers, /dev/kvm for VMs)
Windows pullrun.exe + WSL2 auto-provisioning, runc + Firecracker

๐Ÿš€ Quick Start

# โ”€โ”€ Pull any OCI image โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun pull alpine:3.18     # 968 ms โ€” benchmark script at hack/bench.sh

# โ”€โ”€ Run as a container (Linux) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun run alpine:3.18 --cmd "echo" --cmd "hello pullrun"

# โ”€โ”€ Run as a microVM (macOS/Linux) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun run alpine:3.18 --backend vm --cmd "echo" --cmd "hello" --attach -t

# โ”€โ”€ Interactive shell with detach โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun run alpine:3.18 --tty --attach --cmd /bin/sh
#   Ctrl-P Ctrl-Q  โ†’ detach (workload keeps running)
#   pullrun exec  -t /bin/sh โ†’ re-attach

# โ”€โ”€ Background workload โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun run alpine:3.18 --cmd /bin/sleep --cmd 3600

# โ”€โ”€ Build & push (no Docker daemon needed) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun build ./Dockerfile . -t myapp:latest
pullrun build ./Dockerfile . -t myapp:latest --platform linux/amd64,linux/arm64
pullrun push  ghcr.io/myorg/myimg:latest

# โ”€โ”€ Export/import for air-gapped โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun save  -o myimage.tar
pullrun load -i myimage.tar

# โ”€โ”€ Compose (separate binary) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun-compose up -f myapp/compose.yml               # containers (default on Linux)
pullrun-compose up -f myapp/compose.yml --backend vm   # same compose, VM isolation
pullrun-compose logs -f
pullrun-compose down

# โ”€โ”€ Lifecycle โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun list                  # all workloads (pending/running/exited)
pullrun stop 
pullrun exec  /bin/echo hello
# Re-attach to a detached workload (allocates a fresh PTY)
pullrun exec  -t -- /bin/sh
# Or, for bidirectional I/O streaming without a new shell:
pullrun workload run 

# โ”€โ”€ Image management โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun images                 # list pulled images in local DAG store
pullrun rmi alpine:3.18       # remove an image, cascade-delete unreachable layers
pullrun rmi sha256:abc...     # also works by digest

# โ”€โ”€ GC (DAG store garbage collection) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun gc                    # dry-run โ€” report what would be deleted
pullrun gc --apply            # actually delete unreachable nodes
pullrun gc --apply --force    # bypass 90% safety guard

# โ”€โ”€ Events & Stats โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun events --types=WORKLOAD_STARTED,POLICY_DENIED
pullrun stats 

# โ”€โ”€ Network โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun network create my-net --subnet 10.43.1.0/24
pullrun run alpine:3.18 --net bridge

# โ”€โ”€ Secrets โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun secret create db_password -                     # from stdin
pullrun run myapp:latest --secret db_password

# โ”€โ”€ Configs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun config create nginx.conf ./nginx.conf       # from file
pullrun config create nginx.conf -                   # from stdin
pullrun run nginx:latest --config nginx.conf

# โ”€โ”€ Diff & Inspect โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun diff 
pullrun inspect 
pullrun commit  myapp:snapshot

# โ”€โ”€ Login โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun login ghcr.io
pullrun logout

# โ”€โ”€ MCP (AI agents) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun mcp                   # stdio mode (for opencode, Claude Code)
pullrun mcp --sse :8080       # SSE mode (for remote agents)

# โ”€โ”€ P2P sync โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
pullrun-runtime daemon --sync-addr 0.0.0.0:9500

๐ŸŽฏ Why Pullrun?

๐Ÿ—๏ธ Architecture is Everything

Dockerโ€™s overlayfs store is a filesystem overlay โ€” CVEs in overlayfs (CVE-2023-0386, CVE-2023-32629) can let a container escape to the host. Pullrunโ€™s content-addressed DAG store stores layers as-is, verified by content hash. No overlayfs, no escape.

Pullrun also mitigates shared-kernel cross-container risks that arenโ€™t specific to any storage driver โ€” for example, kernel page-cache bugs like CVE-2026-31431 (โ€œCopy Failโ€) affect all containers sharing a host kernel, regardless of the storage backend. Per-VM kernel isolation (Firecracker, Apple Virtualization) fully contains these. The DAG store makes the VM path zero-cost: the same image, no separate VM build step.

Pullrun is also rootless by default โ€” no sudo needed, no daemon listening on a TCP socket, no attack surface from a central dockerd.

๐Ÿ”ฅ The Only Runtime with Containers + VMs from the Same Image

Backend Isolation Pullrun Docker Podman
Linux containers (runc) Process โœ… โœ… โœ…
Firecracker microVMs Per-VM kernel โœ… โŒ โŒ
Apple Silicon VMs Per-VM kernel โœ… โŒ โŒ
Windows WSL2 containers Process โœ… โœ… โŒ
Windows Firecracker VMs Per-VM kernel โœ… โŒ โŒ

Same image, any isolation level. No separate VM image build step. The OCI manifest IS the VM rootfs.

๐Ÿงฌ Content-Addressed DAG Store

Pullrunโ€™s store is built on rkyv + mmap. OCI layers are stored once, deduplicated by content hash, and mmap()'d directly โ€” no tar extraction, no overlayfs, no dockerd process owning the data.

A sha256: digest is globally consistent. Every node that has pulled alpine:3.18 stores byte-identical files on disk. This makes P2P sync trivial: blocks are verified by content hash, transferred delta-only, and deduplicated across the entire cluster.

๐Ÿ“Š By the Numbers

Metric Pullrun Docker
First alpine:3.18 pull 968 ms ~2 s
Container run latency ~400 ms ~800 ms
Apple Virt VM boot ~160 ms N/A
Firecracker VM cold boot ~500 ms N/A
Firecracker VM warm pool ~200 ms N/A
Idle daemon RSS 24.6 MiB ~90 MiB
Binary size (stripped) ~20 MB (CLI + runtime) ~75 MB
Rootless by default โœ… โŒ (dockerd as root)
Central daemon Optional Required

Benchmarks: single-node, cold cache, alpine:3.18 on Apple M3 (macOS 16) for Pullrun vs Docker Desktop 4.27. Container run latency measured from run command exit to workload PID alive. Apple VM boot measured to interactive exec prompt. Firecracker measurements on Linux x86_64 with KVM; warm pool configured with --vm-warm-pool-size 4. Pullrun daemon RSS measured after pullrun pull alpine:3.18 + pullrun run alpine:3.18 --tty --attach --cmd /bin/sh then detached. Docker RSS from docker run -d --name idle alpine:3.18 sleep 3600.

Benchmarked with hyperfine (โ‰ฅ10 iterations, mean ยฑ stddev reported). The benchmark script lives at hack/bench.sh โ€” run it yourself to reproduce the numbers.


โœจ Features

๐Ÿƒ Workload Lifecycle

pull, run, stop, exec, attach, list, logs, stats, events, inspect, prune, rmi, gc plus:

  • commit โ€” create a new image layer from a running containerโ€™s current filesystem. The DAG store computes the delta against the original image, producing a content-addressed layer.
  • diff โ€” show file-level changes between a running workload and its original image. The store compares DAG trees to produce an add/modify/delete listing.
  • update โ€” live resource limit changes (CPU millicores, memory bytes) without restarting the workload.
  • cp โ€” bidirectional file copy between host and workload paths.
  • save / load โ€” OCI-compatible single-file tarball export/import for air-gapped environments. Re-import produces identical content hashes.
  • login / logout โ€” bearer token auth for private OCI registries.

๐Ÿ”ง Build

Native Dockerfile builder (FROM, RUN, COPY, ADD, WORKDIR, ENV, CMD, ENTRYPOINT) with content-addressed layer caching by instruction hash. Uses runc directly for RUN โ€” no Docker daemon. Multi-platform builds via --platform linux/amd64,linux/arm64.

๐Ÿ—๏ธ OCI Kernel Images

Kernel binaries are first-class OCI content. pullrun kernel install downloads a vmlinux from an OCI registry into the DAG store. The --kernel-image flag lets any workload specify a custom kernel by OCI reference โ€” same store-backed, content-addressed pipeline: verified by digest, cached forever, pushable to any registry.

๐Ÿณ Compose

Full Docker Compose-compatible workflow: up, down, logs, ps, build. Supports dependency ordering (topological sort), port mapping, environment variables, volumes (bind mounts), resource limits (CPU/memory), labels, and per-project bridge networks for isolation. Parses standard docker-compose.yml files via the compose-spec/compose-go library.

Each service can run as a container or VM โ€” use --backend vm to boot all services as Firecracker microVMs from the same compose file, no changes required.

โ˜ธ๏ธ Kubernetes CRI

Drop-in CRI shim at cri/pullrun-cri/ โ€” implement RuntimeService and ImageService from the Kubernetes CRI API. Maps pod sandboxes to pullrun workloads, supports RuntimeClass (pullrun-container / pullrun-vm), pod annotations for image/CPU/memory overrides, and streaming (exec, attach, port-forward).

A native control-plane stub lives in control-plane/: pullrun-controller (scheduler) + pullrun-agent (per-node deployer) communicating over gRPC. This is a v1 work-in-progress with etcd, .pullrun.local DNS, and admission control planned โ€” but the wire protocol is stable today.

๐Ÿค– MCP AI Integration

Native Model Context Protocol server exposing 15 runtime operations as MCP tools โ€” run, stop, exec, list, get, inspect, logs, stats, pull_image, list_images, build, push, prune, compose_up, compose_down plus MCP resources (pullrun://workload/{id}, pullrun://workload/{id}/logs, pullrun://store/info, pullrun://images). Works in stdio mode (for opencode, Claude Code, Cursor) or SSE mode (for remote agents via HTTP).

๐Ÿ” Policy Engine

Gate workloads before they run โ€” built-in support for:

  • Cosign signature verification (Ed25519 key pairs, key ID matching)
  • SBOM evaluation (CVSS scoring, ban by license)
  • Seccomp profiles (default allowlist of ~50 syscalls, unconfined, or custom JSON)
  • Read-only rootfs โ€” prevents runtime tampering (write only to --volume mounts)
  • no_new_privileges โ€” blocks setuid and capset escalation
  • Policy is declarative: required_signature: true, max_cvss_score: 7.0, deny_licenses: ["GPL-3.0"]

Compose all four: --require-signature --readonly-rootfs --no-new-privileges --seccomp-profile default.

๐ŸŒ P2P Image Distribution

Nodes share image blocks peer-to-peer via gRPC + Bloom filters. One node pulls from the registry, the rest delta-sync from each other. Features: mDNS/discovery for zero-config LAN peer finding, Bloom filter cache to avoid redundant transfers, gossip protocol for peer state, delta computation, registrar service for peer tracking.

๐Ÿ“ก Networking

User-defined bridge networks with IPAM, inbound/outbound port forwarding, DNS resolution, and iptables integration. Four modes per workload via --net: isolated (default for containers, loopback only with host proxy on 10.42.0.1), bridge (shared pullrun-br0 bridge, inter-workload communication), slirp (default for VMs, userspace NAT via slirp4netns โ€” no bridge, no iptables), host (shares host namespace), none (no network). Isolation is enforced by the proxy, not per-workload VLANs.

๐Ÿ—๏ธ Encrypted Secrets

AES-256-GCM encryption at rest, decrypted into workload tmpfs at runtime. pullrun secret create/get/ls/inspect/rm โ€” data stays encrypted on disk, only the runtime process can decrypt.

๐Ÿ”„ Export/Import

Single-file OCI-compatible tarball export for air-gapped environments. Re-import produces identical content hashes.

๐Ÿ“Š Events & Observability

Real-time event stream via pullrun events โ€” IMAGE_PULLED, WORKLOAD_STARTED, POLICY_DENIED, etc. Per-workload stats with CPU/memory. Prometheus metrics exporter built into the daemon. PrometheusRule alerting config in deploy/.

๐Ÿงน DAG Store Garbage Collection & Reference-Counted rmi

pullrun rmi removes an image by tag or digest with immediate cascade deletion of unreachable subtree nodes. Per-node refcounts (node.refcount sidecar files) preserve shared layers โ€” a layer referenced by another image is never deleted until the last referencing image is removed. Crash recovery via recompute_all_refcounts on daemon startup rebuilds refcounts from all tagged image and workload roots.

pullrun gc reclaims unreachable DAG nodes (orphaned layers, manifests, blobs) that are no longer reachable from any tagged image or running workload. Features: dry-run by default (report only), --apply to actually delete, --force to bypass the 90% safety guard, op-lock protection for in-flight operations, VM kernel image pinning via kernel_image_digest. The store no longer grows forever.

๐Ÿ–ฅ๏ธ Interactive Shells

Full TTY support with detach/re-attach via Ctrl-P Ctrl-Q. Works across all backends (container, Firecracker VM, Apple VM). Detached workloads keep running โ€” re-attach with pullrun exec --tty /bin/sh. Even works on exited workloads (daemon starts a fresh sleep container).

โค๏ธ Health Checks & Restart Policies

pullrun run --health-cmd 'curl -f http://localhost:80' โ€” periodic health checks with configurable interval, timeout, and retries. --restart on-failure|always|unless-stopped controls automatic restart on exit. Health state is surfaced via pullrun inspect and pullrun events.

๐Ÿ—„๏ธ VM State Persistence

Stopped VMs behave like hibernated machines โ€” all writes preserved, restart on demand with exec. Backend specifics: Apple Virt rootfs persists via VirtioFS, Firecracker retains the ext4 image, Container (runc) rootfs is ephemeral (only --volume mounts survive). No re-pull needed.

๐Ÿ” Private Registries

pullrun login for bearer token auth. Daemon accepts --insecure-registry for plain-HTTP mirrors and air-gapped LAN caches.


โ˜ธ Kubernetes

Pullrun ships a CRI shim in cri/pullrun-cri/ that implements the Kubernetes Container Runtime Interface. It maps pod sandboxes to pullrun workloads and supports:

  • RuntimeClass โ€” pullrun-container for runc containers, pullrun-vm for Firecracker VMs
  • Pod annotations: pullrun.io/image, pullrun.io/cpu-millicores, pullrun.io/memory-bytes
  • Streaming: exec, attach, port-forward
  • Image management via the DAG store

Deploy as a DaemonSet with manifests in deploy/:

kubectl apply -f deploy/runtime-daemon.yaml
kubectl apply -f deploy/serviceaccount.yaml
kubectl apply -f deploy/servicemonitor.yaml

๐Ÿค– MCP AI Integration

Any MCP-compatible AI agent (opencode, Claude Code, Cursor) can control pullrun through natural language:

# Start the MCP server
pullrun mcp

# In opencode or Claude Code, the agent can now:
#   "pull alpine and run it as a VM"
#   "exec into my-app and check the logs"
#   "show me all running workloads"
#   "run docker-compose up from my project"

The MCP server exposes 15 tools and 4 resource types. SSE mode for remote agents: pullrun mcp --sse :8080.


๐ŸŒ P2P Image Distribution

# Node A โ€” seed
pullrun-runtime daemon --sync-addr 0.0.0.0:9500

# Node B โ€” pulls delta blocks from Node A
pullrun-runtime daemon --sync-addr 0.0.0.0:9501 \
  --sync-peers node-a.example.com:9500

Each block verified by content hash before acceptance โ€” no trust required.


๐Ÿ”ง Backend Comparison

Backend Isolation Platform Boot Time Best For
๐Ÿง Linux Containers (runc) Process (namespace) Linux, macOS, Windows (WSL2) ~400 ms Dev, CI/CD, dense packing
๐Ÿ”ฅ Firecracker microVM Per-VM kernel (KVM) Linux x86_64, Windows (WSL2 x86_64) ~500 ms cold / ~200 ms warm pool Multi-tenant, untrusted workloads, compliance, fast-recycling sandboxes
๐ŸŽ Apple Virtualization Per-VM kernel (Hypervisor.framework) macOS Apple Silicon ~160 ms macOS dev, Apple Silicon CI

๐Ÿ—๏ธ Architecture

                              pullrun (Go CLI)
                   pull ยท run ยท build ยท compose ยท inspect
                   events ยท stats ยท network ยท secret ยท mcp
                              โ”‚
                              โ”‚ gRPC (UDS or TCP)
                              โ–ผ
                   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                   โ”‚         pullrun-runtime             โ”‚
                    โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
                    โ”‚  โ”‚ Store    โ”‚   โ”‚ Executor         โ”‚ โ”‚
                    โ”‚  โ”‚ (DAG)    โ”‚   โ”‚ runc / VM        โ”‚ โ”‚
                    โ”‚  โ”‚ โ”” rkyv   โ”‚   โ”‚ โ”” VmPool (warm)  โ”‚ โ”‚
                    โ”‚  โ”‚ โ”” mmap   โ”‚   โ”‚                  โ”‚ โ”‚
                    โ”‚  โ”‚ โ”” DashMapโ”‚   โ”‚                  โ”‚ โ”‚
                   โ”‚  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
                   โ”‚       โ”‚                โ”‚            โ”‚
                   โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”       โ”‚
                   โ”‚  โ”‚       Network            โ”‚       โ”‚
                   โ”‚  โ”‚  IPAM ยท Proxy ยท DNS      โ”‚       โ”‚
                   โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚
                   โ”‚                                      โ”‚
                   โ”‚  Sync ยท Policy ยท Secrets ยท Metrics   โ”‚
                   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
                   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                   โ”‚      Kubernetes     โ”‚
                   โ”‚  CRI shim ยท Runtime โ”‚
                   โ”‚  Class ยท Prometheus โ”‚
                   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The store uses DashMap> for lock-free concurrent reads โ€” the first reader pays for mmap() + page faults, every subsequent reader is a single atomic load. The Rust workspace is eleven crates (store, oci, exec, vm, net, dns, vsock, sync, policy, runtime, init); the Go side is the CLI, CRI shim, compose, and control-plane stub.


๐Ÿ“ Project Layout

proto/            # Protobuf definitions (single source of truth)
runtime/          # Rust workspace โ€” core data plane
  pullrun-store/   # Zero-copy DAG store (rkyv + mmap + DashMap concurrent cache)
  pullrun-oci/     # OCI client + DAG converter
  pullrun-exec/    # Executor trait + runc wrapper
  pullrun-vm/      # Firecracker + Apple Virt backends, warm VM pool (pool.rs), pullrun-init guest agent
  pullrun-net/     # IPAM, proxy, DNS, iptables, slirp4netns
  pullrun-dns/     # In-process DNS server for workload resolution
  pullrun-vsock/   # Vsock transport layer for VM guest-host communication
  pullrun-sync/    # P2P block sync (Bloom, mDNS, gossip)
  pullrun-policy/  # Cosign, SBOM, seccomp gates
  pullrun-runtime/ # gRPC daemon
cli/pullrun/      # Go CLI (cobra) โ€” 30+ commands
cri/pullrun-cri/  # Kubernetes CRI shim
control-plane/    # Multi-node orchestration stub: pullrun-controller + pullrun-agent (v1 WIP)
cmd/pullrun-compose/ # Docker Compose-compatible CLI
deploy/           # K8s manifests, Prometheus rules, alerts
docs/             # Architecture, operations, policy, MCP
tools/            # Standalone smoke-test workspaces (apple-virt-exec, firecracker-smoke, etc.)

๐Ÿงช Testing

247 tests, 0 failures โ€” every commit is gated by the full suite (cargo fmt --check, cargo test, go test ./..., golangci-lint).

Suite Stack What it covers Tests
pullrun-store ๐Ÿฆ€ Rust DAG store, rkyv round-trips, op-locks 30
pullrun-runtime ๐Ÿฆ€ Rust gRPC service, events, metrics, policy integration 40
pullrun-vm ๐Ÿฆ€ Rust network, ext4, OCI kernels, Apple Virt + Firecracker* 28
pullrun-net ๐Ÿฆ€ Rust IPAM, firewall, proxy, DNS, loopback 18
pullrun-policy ๐Ÿฆ€ Rust cosign, SBOM, gates 17
pullrun-oci ๐Ÿฆ€ Rust dockerfile parser, puller, converter, layouts 16
pullrun-gc ๐Ÿฆ€ Rust DAG garbage collection 16
pullrun-sync ๐Ÿฆ€ Rust P2P block sync, bloom, delta 13
pullrun-vsock ๐Ÿฆ€ Rust vsock transport 9
pullrun-exec ๐Ÿฆ€ Rust seccomp, rootless 8
pullrun-init + build-initramfs ๐Ÿฆ€ Rust guest agent, initramfs 5
pullrun-cri ๐Ÿน Go CRI shim lifecycle, filestore 20
pullrun-compose ๐Ÿน Go compose parsing, up/down flows 15
pullrun CLI ๐Ÿน Go workload run, commands 9
control-plane ๐Ÿน Go controller store 9
Total 247

* 2 Firecracker hardware tests are #[ignore]d (need /dev/kvm) โ€” run manually: cargo test -p pullrun-vm --test firecracker_boot -- --include-ignored --nocapture

cargo test                                   # Rust workspace: 194 tests
go test ./...                                # Go modules (cri, cli, compose, control-plane): 53 tests
cargo fmt --all --check && golangci-lint run ./...   # CI gates

End-to-end CRI behavior (exec stdin streaming, exit-code propagation, pod teardown) is validated against a live crictl sandbox in a VM โ€” see AGENTS.md release notes for the full lifecycle checklist.


๐Ÿ“š Documentation

Document What Youโ€™ll Find
docs/PULLRUN_GUIDE.md Full user guide for all platforms
docs/ARCHITECTURE.md DAG store design, executor trait, network model
docs/OPERATIONS.md Deploying, monitoring, troubleshooting
docs/POLICY.md Policy engine (cosign, SBOM, CVSS, license)
docs/WINDOWS.md Windows/WSL2 setup, Firecracker, known issues
docs/ALL_MCP.md MCP server reference (AI agent integration)
docs/cross-node-dag-sync.md P2P block sync design

๐Ÿ“„ Technical Report

A technical report describing the architecture is available in /paper and archived on Zenodo: https://doi.org/10.5281/zenodo.20679669


๐Ÿ“„ License

Apache 2.0 โ€” see LICENSE. Contributions subject to CLA.md.


View this README on GitHub

ๅฎ‰่ฃ…

This server does not publish a one-line install command.

Open the repository installation guide