Mission

Nullspace exists because the way cloud compute is being used is changing.

For a long time, cloud compute was mostly consumed by human developers: someone provisions a server, opens a terminal, runs a service, watches a dashboard, and debugs when things break.

Now we have a new class of users: AI agents. They use computers differently. They may need to spin up a machine for a few minutes, run generated code, operate a browser, test a mobile app, use Excel, process a dataset, or explore ten possible solutions in parallel. They need machines that are fast to start, easy to shape, safe to run, and simple to understand when something goes wrong.

Our goal with Nullspace is to build that layer of infrastructure.

What We're Building Toward

01Composability

Different tasks need different computers. A coding agent may need Linux with a repo and test environment. A desktop automation agent may need Windows with Excel. A research workflow may need GPUs and mounted datasets. Nullspace should make it easy to choose the hardware, operating system, files, tools, and policies an agent needs—without stitching everything together from scratch each time. The goal is to make the right machine easy to create.

Linux
from nullspace import Machine

machine = Machine.create(
    hardware={
        "vcpus": 4,
        "memory": "8GiB",
        "disk": "40GiB",
    },
    runtime={
        "os": "linux",
        "image": "ubuntu-24.04",
    },
    environment={
        "repo": "github.com/acme/api",
        "packages": ["python3", "nodejs", "docker"],
        "secrets": ["GITHUB_TOKEN"],
    },
)

machine.commands.run("pytest", ["tests/"])

02Scalability

Agents can use more computers than humans do. An agentic workflow may want many machines at once—one to run tests, one to browse the web, several more to try different approaches in parallel. It should be easy to scale out when a task benefits from parallelism, and scale up when a machine needs more resources. Starting, resizing, and multiplying machines should be ordinary operations.

Horizontal
from nullspace import Machine

# Agents often need to explore many paths at once.
batch = Machine.batch(
    count=100,
    hardware={
        "vcpus": 2,
        "memory": "4GiB",
    },
    runtime={
        "os": "linux",
        "image": "python-3.12",
    },
    environment={
        "repo": "github.com/acme/evals",
        "secrets": ["OPENAI_API_KEY"],
    },
)

runs = batch.map(
    command=["python", "eval.py"],
    inputs=[
        {"candidate": "patch-001"},
        {"candidate": "patch-002"},
        {"candidate": "patch-003"},
        # ...
    ],
)

results = runs.collect()

03Stateful Lifecycle

Agents build up context while they work: installed dependencies, cloned repos, processed files, hours of setup. Pause should let developers stop work without throwing everything away. Resume should bring back the context quickly. Fork should let agents branch from a shared point and try multiple approaches without repeating setup. State is where a lot of the cost, latency, and usefulness of agentic work lives.

Pause/Resume
from nullspace import Machine

machine = Machine.create(
    hardware={
        "vcpus": 4,
        "memory": "8GiB",
        "disk": "40GiB",
    },
    runtime={
        "os": "linux",
        "image": "python-3.12",
    },
    environment={
        "repo": "github.com/acme/research-agent",
        "volumes": ["experiment-data:/data"],
        "secrets": ["OPENAI_API_KEY"],
    },
)

# The agent spends time setting up useful state.
machine.commands.run("pip", ["install", "-r", "requirements.txt"])
machine.commands.run("python", ["preprocess.py", "--input", "/data/raw"])

# Pause the machine without throwing away the work.
machine.pause()

# Later, resume from the same state.
machine.resume()

machine.commands.run("python", ["continue.py", "--input", "/data/processed"])

04Safety

Agents need room to act, but they also need boundaries. They may run generated code, handle private data, use credentials, or interact with external systems. Some code will be wrong. Some agent actions will be mistaken. Nullspace should treat isolation, permissions, secrets, and auditability as core parts of the platform—letting developers give agents useful capabilities while keeping the blast radius understandable and controlled.

Least Privilege
from nullspace import Machine, Policy

machine = Machine.create(
    hardware={
        "vcpus": 4,
        "memory": "8GiB",
    },
    runtime={
        "os": "linux",
        "image": "python-3.12",
    },
    environment={
        "repo": "github.com/acme/app",
        "secrets": ["GITHUB_TOKEN"],
    },
    policy=Policy(
        network={
            "mode": "allowlist",
            "allow": [
                "api.github.com",
                "pypi.org",
                "files.pythonhosted.org",
            ],
        },
        filesystem={
            "read": ["/workspace/repo"],
            "write": ["/workspace/tmp", "/workspace/output"],
        },
        secrets={
            "allow": ["GITHUB_TOKEN"],
        },
    ),
)

machine.commands.run("python", ["agent.py", "--task", "fix-failing-test"])

05Experience

The default interface should be boring—a developer should be able to ask for a machine without learning the underlying virtualization stack. But the details should not disappear completely. When a workload needs stronger isolation or more control, those choices should be available. And when something fails, the system should explain what happened in a way both the developer and the agent can use.

Simple by Default
from nullspace import Machine

machine = Machine.create(
    purpose="run-agent-code",
    hardware={
        "vcpus": 4,
        "memory": "8GiB",
    },
    runtime={
        "os": "linux",
        "image": "python-3.12",
    },
)

result = machine.commands.run(
    "python",
    ["agent_task.py"],
)

print(result.stdout)