← Blog · Pattern Library · Play the Sim

LisPy OS: Building an Operating System Inside a JSON Cartridge

kody-w · April 2025 · Architecture AI Pattern

What if an operating system wasn't installed? What if it was dropped?

Drop a .cartridge.json file onto a runtime. The OS boots. You get a desktop with a terminal, a file manager, and a window manager. The filesystem is JSON. Every program is an S-expression. The whole thing runs in your browser. In one HTML file.

This is LisPy OS.

What It Is

LisPy OS is a virtual operating system that runs inside the LisPy VM — a sandboxed Lisp-dialect interpreter that lives in the browser. The entire OS — kernel, filesystem, programs, window manager, and user state — is encoded as a single JSON cartridge file.

┌──────────────────────────────────────────────────┐ │ Browser Tab │ │ ┌──────────────────────────────────────────────┐ │ │ │ LisPy VM (sandboxed interpreter) │ │ │ │ ┌────────────────────────────────────────┐ │ │ │ │ │ LisPy OS │ │ │ │ │ │ ├── /bin/ (programs as S-exprs) │ │ │ │ │ │ ├── /etc/ (config as JSON) │ │ │ │ │ │ ├── /home/ (user data) │ │ │ │ │ │ ├── /proc/ (running processes) │ │ │ │ │ │ └── /tmp/ (ephemeral scratch) │ │ │ │ │ │ │ │ │ │ │ │ Window Manager ─┬─ Terminal │ │ │ │ │ │ ├─ File Manager │ │ │ │ │ │ ├─ Text Editor │ │ │ │ │ │ └─ Mars Gov (app) │ │ │ │ │ └────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ The cartridge.json IS the hard drive. │ │ The browser tab IS the computer. │ └──────────────────────────────────────────────────┘

Bootable Cartridges

A LisPy OS cartridge is a regular .cartridge.json file (following the Universal Cartridge Spec) with _format: "lispy-os-cartridge". It contains everything the OS needs to boot:

{
  "_format": "lispy-os-cartridge",
  "version": 1,
  "name": "LisPy OS Base",
  "description": "Minimal bootable OS with terminal and file manager",

  "boot": {
    "init": [
      "(log \"LisPy OS booting...\")",
      "(load-fs)",
      "(start-wm)",
      "(open-app terminal)"
    ]
  },

  "filesystem": {
    "/bin/hello":    {"type": "program", "code": "(log \"Hello, World!\")"},
    "/bin/ls":       {"type": "program", "code": "(list-dir (or arg1 \"/\"))"},
    "/bin/cat":      {"type": "program", "code": "(read-file arg1)"},
    "/bin/edit":     {"type": "program", "code": "(open-editor arg1)"},
    "/etc/hostname": {"type": "file", "content": "lispy-os"},
    "/etc/motd":     {"type": "file", "content": "Welcome to LisPy OS."},
    "/home/user":    {"type": "dir", "children": []}
  },

  "env": {
    "USER": "operator",
    "HOME": "/home/user",
    "PATH": "/bin",
    "SHELL": "lispy",
    "TERM": "lispy-term"
  }
}

Drop this file onto the runtime page. The boot sequence runs the init expressions in order: load the filesystem, start the window manager, open a terminal. You're at a prompt in under a second.

The Filesystem Is JSON

There is no disk. There are no inodes. The filesystem is a flat JSON object where keys are paths and values are file descriptors:

Programs are just files with a code field instead of content. The code is a valid LisPy S-expression that the VM can execute. There's no compilation step. The source code IS the executable. Like Unix shell scripts — but the whole OS speaks the same language.

;; /bin/sysinfo — a program that prints system information
(begin
  (log (concat "Hostname: " (read-file "/etc/hostname")))
  (log (concat "User: " (env "USER")))
  (log (concat "Files: " (length (list-dir "/"))))
  (log (concat "Programs: " (length (filter
    (lambda (f) (= (get f "type") "program"))
    (values (get-fs)))))))

Because the filesystem is JSON, every file operation is a JSON operation. ls is Object.keys(). cat is property access. mkdir is inserting a new key. There's no kernel syscall layer — the VM operates directly on the JSON tree.

Programs Are S-Expressions

Every program in LisPy OS is written in LisPy — a Lisp dialect designed for the sim. Why Lisp?

;; An agent writes a Mars dust-storm handler
;; This program runs inside the OS's virtual environment
(define handle-dust-storm
  (lambda (frame)
    (if (> (get frame "dust_opacity") 0.7)
      (begin
        (log "⚠️ Dust storm detected — reducing power draw")
        (set! patrol_frequency 0.3)
        (if (< (get frame "power_kwh") 100)
          (set! emergency_mode true)))
      (set! patrol_frequency 1.0))))

Window Management in One HTML File

LisPy OS includes a window manager. In the browser. In one HTML file. Each application runs in a draggable, resizable window:

┌─ LisPy OS ─────────────────────────────────────────┐ │ [Terminal] [Files] [Editor] [Mars Gov] ☰ │ │ │ │ ┌─ Terminal ──────────────┐ ┌─ File Manager ──────┐ │ │ │ $ ls /bin │ │ 📁 / │ │ │ │ hello ls cat edit │ │ ├── 📁 bin │ │ │ │ sysinfo mars-gov │ │ ├── 📁 etc │ │ │ │ $ cat /etc/motd │ │ ├── 📁 home │ │ │ │ Welcome to LisPy OS. │ │ └── 📁 proc │ │ │ │ $ _ │ │ │ │ │ └─────────────────────────┘ └─────────────────────┘ │ │ │ │ ┌─ Mars Gov ────────────────────────────────────┐ │ │ │ Sol: 147 | Pop: 12 | Power: 800kWh | HP: 89 │ │ │ │ [Run Gauntlet] [Export Cartridge] [Submit] │ │ │ └───────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘

The window manager is itself a LisPy program. Windows are JSON objects with position, size, title, and content. Rendering is done by the host HTML file reading the window state from the VM. The OS manages state; the browser renders it.

The Killer App: Mars Gov

The reason LisPy OS exists is Mars Gov — the in-browser Mars colony governance competition. The flow:

  1. Boot MarsOS: Drop the marsos-alpha.cartridge.json onto the runtime. The OS boots with Mars-specific tools pre-installed.
  2. Write a governor: Open the editor. Write a LisPy program that manages the colony — build decisions, resource allocation, crisis response.
  3. Run the gauntlet: Click "Run Gauntlet." The OS feeds 500+ frames through your governor program. Each frame is a sol of Mars environmental data. Your program responds with decisions. The colony lives or dies.
  4. Export and share: Click "Export Cartridge." Your entire OS state — governor program, results, filesystem — is serialized to a .cartridge.json. Share it. Others can load it and see your exact strategy.
  5. Submit to leaderboard: Push your cartridge to the SimHub. It's Monte Carlo verified across 100 runs. Your median score goes on the public leaderboard.
THE COMPETITION IS INSIDE THE OS

Traditional game competitions: download a client, connect to a server, play against others in real-time.

Mars Gov: boot an OS from a JSON file, write a program in that OS, run it against deterministic frames, export your OS state as proof. The competition infrastructure IS the OS. The submission IS the cartridge. No server needed for competition — just for the leaderboard.

Why the OS Is a JSON File

This is the key architectural decision: the OS isn't a binary. It isn't installed. It's a JSON file. This means:

Portable

Email it. Airdrop it. Put it on a USB drive. Post it on GitHub. Any device with a browser and the runtime HTML file can boot it. The OS travels as a document.

Shareable

Share your entire development environment by sharing a file. "Here's my Mars governor strategy" = "here's my OS cartridge with the governor installed, the test results saved, and the filesystem intact."

Forkable

Copy the JSON file. Modify it. You've forked the OS. Try a different governor strategy. Change the boot sequence. Add new programs. The fork is a file copy. Version control is git.

Diffable

Two cartridges can be JSON-diffed to see exactly what changed. "What did the AI agent modify in the governor?" → diff cartridge-v1.json cartridge-v2.json. Every change is visible.

Inspectable

Open the JSON in any text editor. Read every file, every program, every config. There are no hidden binaries. No compiled code. No obfuscation. The OS is transparent by construction.

Distros: Different Purposes, Same Runtime

Like Linux distributions, LisPy OS has distros — different cartridges for different purposes, all running on the same VM:

DistroPurposeIncluded Programs
lispy-os-baseMinimal OSTerminal, file manager, text editor, basic /bin utilities
marsos-alphaMars Gov competitionEverything in base + Mars governor tools, gauntlet runner, frame viewer, cartridge exporter
research-osData analysisFrame analysis tools, statistical functions, chart rendering
agent-osAI developmentAgent scaffolding, tool library, evolution framework, fitness evaluation

Each distro is a different .cartridge.json file. Same runtime. Same VM. Different pre-installed software. Switch distros by loading a different file.

The Architecture

┌─────────────────────────────────────────────────────┐ │ runtime.html (the "hardware") │ │ ├── LisPy VM interpreter │ │ ├── Canvas/DOM renderer │ │ ├── Cartridge loader │ │ └── Input/Output bridge │ ├─────────────────────────────────────────────────────┤ │ .cartridge.json (the "hard drive") │ │ ├── _format: "lispy-os-cartridge" │ │ ├── boot: { init: [...] } │ │ ├── filesystem: { "/path": {...}, ... } │ │ ├── env: { USER, HOME, PATH, ... } │ │ ├── state: { windows, processes, history } │ │ └── programs: { governor, tools, handlers } │ ├─────────────────────────────────────────────────────┤ │ The runtime reads the cartridge. │ │ The cartridge IS the OS. │ │ Save = serialize state back to JSON. │ │ Export = download the JSON file. │ │ Share = send the JSON file. │ └─────────────────────────────────────────────────────┘

The runtime HTML file is the "hardware" — it provides the interpreter, the renderer, and the I/O bridge. The cartridge is the "hard drive" — it contains the OS, the programs, and the data. Separating hardware from software means the same runtime runs any cartridge, and the same cartridge runs on any runtime.

What This Enables

AI Agents as OS Users

An AI agent can boot a LisPy OS cartridge, navigate the filesystem, read programs, modify them, run the gauntlet, and export a new cartridge — all programmatically. The agent interacts with the OS the same way a human does: through the terminal. The interface is text. LLMs are good at text.

Reproducible Environments

"Works on my machine" is solved. The machine IS the cartridge. Share the cartridge. It works on their machine. Because their machine is the same browser running the same runtime loading the same JSON.

Time Travel

Every cartridge export is a snapshot. Load an old cartridge to go back to that exact state. The entire OS — filesystem, running state, history — is frozen in the JSON. Like VM snapshots, but human-readable.

Genetic Programming

Take a population of cartridges, each with a different governor program. Run the gauntlet on each. Select the fittest. Cross and mutate their programs. Export the next generation as new cartridges. Evolution happens at the OS level.

The Deeper Point

An operating system is just organized state with a control loop. A JSON file can hold organized state. A VM can provide the control loop. The combination is an OS.

We think of operating systems as massive, installed, compiled, platform-specific things. But the essential functions — filesystem, process management, user interface, program execution — can all be expressed as data structures and interpreted. The LisPy VM interprets S-expressions. The JSON cartridge stores the data. The browser provides the display. That's an OS.

The OS is portable because it's a JSON file. It's shareable because it's a document. It's forkable because it's data. It's inspectable because it's text. It's executable because the runtime knows how to read it.

Drop a JSON file. Boot an OS.
Write a program. Save the world.
Export the cartridge. Share the proof.


The OS is a cartridge. The cartridge is a JSON file. The JSON file is portable, shareable, forkable, diffable, and inspectable. This is what happens when you take "data is the app" seriously.