MAQPNADocs

Run the stack on your laptop

Install MAQPNA, llamay and kosa8 on one machine, make a governed model call to a local llamay model from code in a kosa8 microVM, find the decision in the audit ledger, then tear it all down.

This quickstart puts all three Kosa8 Labs products on one laptop in about 15 minutes. At the end, a small agent runs inside a kosa8 microVM, calls a llamay model through the MAQPNA gateway, makes one allowed and one denied tool call, and you read each decision in the audit ledger.

You need it when you want to see how the three fit together before you build on them. If you only want MAQPNA, the MAQPNA quickstart is shorter. For what each product does, read the stack overview.

flowchart LR
    subgraph VM["kosa8 microVM"]
        A["agent.py"]
    end
    subgraph DEV["local MAQPNA (maqpna dev up)"]
        IB["Identity broker"]
        GW["Gateway"]
        L[("Audit ledger<br/>.maqpna/audit.jsonl")]
    end
    LL["llamay serve<br/>127.0.0.1:11435"]
    E["mcp-echo<br/>test MCP server"]
    A -->|"session token<br/>/llm/llamay"| GW
    A -->|"/mcp/echo"| GW
    A -.->|"any other host: blocked"| X["internet"]
    IB -->|"mints the token"| A
    GW --> LL
    GW --> E
    GW --> L

The agent never sees llamay's address or key. The microVM can reach only the gateway. The gateway checks every call and writes one audit record per decision.

Before you begin#

You need Why
About 2 GiB of free disk The model (about 400 MiB), the MAQPNA servers and the kosa8 guest image. kosa8 fork can fail with less than 2 GB free.
A network connection for the install Every step after install runs offline.
A machine kosa8 supports for sandboxes See the table below. Without one, you can still do steps 1–4 and run the agent as a local process.

What each product supports:

Platform MAQPNA CLI llamay kosa8 sandboxes MAQPNA with kosa8
macOS, Apple silicon Yes Yes Yes (Virtualization framework) Yes. This is the path Kosa8 Labs tested.
macOS, Intel Yes Yes No. kosa8 needs a remote worker. Not covered here.
Linux, x86-64 with KVM Yes Yes Yes (Cloud Hypervisor, needs /dev/kvm) Same API as macOS. Not yet tested end to end by Kosa8 Labs.
Windows 11, x86-64 Yes Yes Preview, inside WSL2 Not tested. The integration talks to kosa8's Unix socket in your home directory, so run every command inside the same WSL2 Linux shell, with the Linux tab.
Windows 10, Windows on Arm Yes Yes No. kosa8 needs a remote worker. Not covered here.

Step 1: Install the three CLIs#

curl -fsSL https://maqpna.com/install.sh | sh
curl -fsSL https://llamay.com/install.sh | sh
curl -fsSL https://kosa8.com/install.sh | sh

You can also install kosa8 as the Mac app from kosa8.com/download. The app includes the CLI and starts the kosa8 daemon for you.

curl -fsSL https://maqpna.com/install.sh | sh
curl -fsSL https://llamay.com/install.sh | sh
curl -fsSL https://kosa8.com/install.sh | sh

kosa8 on Linux needs cloud-hypervisor, virtiofsd and access to /dev/kvm. The kosa8 installer tells you what to install.

irm https://maqpna.com/install.ps1 | iex
irm https://llamay.com/install.ps1 | iex
irm https://kosa8.com/install.ps1 | iex

Open a new terminal afterwards, so the PATH change applies. On Windows 11, run kosa8 wsl setup once. Then continue in WSL2 with the Linux tabs, as the note above says.

Check it worked. Each command prints a version:

maqpna version
llamay --version
kosa8 doctor

kosa8 doctor checks the parts of kosa8 that commonly go wrong, such as the hypervisor. More install options for each product: MAQPNA, llamay, kosa8.

Step 2: Download a model#

llamay pull qwen2.5:0.5b

This downloads a small instruction model (about 400 MiB) into llamay's model store. It is small enough for a laptop CPU.

Check it worked. The model answers locally, without MAQPNA:

llamay run -m qwen2.5:0.5b -chat -p "Say hello in five words."

Step 3: Start the kosa8 daemon#

kosa8 daemon start

If you installed the Mac app, it has already started the daemon.

kosa8 daemon start

Check it worked.

kosa8 daemon status
kosa8 run alpine echo hi

The second command boots a microVM and prints hi.

Step 4: Start a local MAQPNA with llamay and kosa8#

Make a working directory and start MAQPNA in it:

mkdir stack-quickstart && cd stack-quickstart
maqpna dev up --llamay qwen2.5:0.5b --kosa8

This one command does the following:

  1. It starts the identity broker, the gateway and the test MCP server mcp-echo. The first run downloads the server binaries for your maqpna version and checks their checksums.
  2. It starts llamay serve on 127.0.0.1:11435, with a fresh API key in .maqpna/llamay.key and llamay's request audit in .maqpna/llamay-audit.jsonl. The key is passed to llamay in its environment, never on the command line.
  3. It adds the model route llamay. Each session gets its own llamay inference context.
  4. It anchors llamay's audit chain in the MAQPNA ledger every 10 seconds. If the chain does not verify, llamay is taken out of routing.
  5. Because of --kosa8, the gateway listens on your machine's LAN address, so kosa8 guests can reach it. The identity broker checks each kosa8 sandbox with the kosa8 daemon before it issues the sandbox's token. The gateway also mirrors its tool-call decisions into kosa8's audit log and anchors kosa8's audit head in the ledger every 30 seconds.

Expected result. The command ends with a summary like this, with your own addresses:

local MAQPNA is up (/Users/you/stack-quickstart/.maqpna)
  gateway   http://192.168.1.20:8080
  broker    http://127.0.0.1:8081
  mcp       echo -> http://127.0.0.1:8090/mcp
  model     llamay -> http://192.168.1.20:8080/llm/llamay/v1 (OpenAI base URL)
next: maqpna dev run -- <your agent command>

Check it worked.

maqpna dev status

Every component shows running and the gateway shows ready=true. The command exits with code 3 if something is down. The logs of each component are in .maqpna/logs/.

Step 5: Write a small agent#

Save this as agent.py. It uses only the Python standard library, because the microVM has no internet access and cannot pip install anything.

import json, os, urllib.request

def token():
    # Read the token file on every call. After a fork, MAQPNA replaces this file
    # with the child session's token.
    with open(os.environ["MAQPNA_TOKEN_FILE"]) as f:
        return f.read().strip()

def post(url, body, accept="application/json"):
    req = urllib.request.Request(url, data=json.dumps(body).encode(), method="POST", headers={
        "Authorization": "Bearer " + token(),
        "Content-Type": "application/json",
        "Accept": accept,
    })
    with urllib.request.urlopen(req, timeout=120) as resp:
        return resp.read().decode()

# 1. A model call to llamay, through the gateway's model route.
print(post(os.environ["OPENAI_BASE_URL"] + "/chat/completions", {
    "model": os.environ.get("MAQPNA_MODEL_NAME", "llamay"),
    "max_tokens": 32,
    "messages": [{"role": "user", "content": "Say hello in five words."}],
}))

# 2. An allowed tool call: echo is allowed by the built-in dev policy.
mcp = os.environ["MAQPNA_TOOL_ECHO_URL"]
accept = "application/json, text/event-stream"
print(post(mcp, {"jsonrpc": "2.0", "id": 1, "method": "tools/call",
                 "params": {"name": "echo", "arguments": {"text": "hello from kosa8"}}}, accept))

# 3. A denied tool call: the dev policy denies any call aimed at a kube-* namespace.
print(post(mcp, {"jsonrpc": "2.0", "id": 2, "method": "tools/call",
                 "params": {"name": "echo", "arguments": {"text": "x", "namespace": "kube-system"}}}, accept))

# 4. Any other destination is blocked by the sandbox.
try:
    urllib.request.urlopen("http://1.1.1.1", timeout=3)
    print("egress: OPEN (unexpected)")
except Exception:
    print("egress: blocked")

maqpna dev run gives the agent these variables:

Variable Value
MAQPNA_TOKEN_FILE The session token file inside the microVM. It is the only place the token is.
OPENAI_BASE_URL, MAQPNA_MODEL_ENDPOINT {gateway}/llm/llamay/v1
MAQPNA_MODEL_NAME The model name llamay reports
MAQPNA_TOOL_ECHO_URL {gateway}/mcp/echo
MAQPNA_GATEWAY_URL, MAQPNA_SESSION, MAQPNA_AGENT The gateway, the session id and the agent name

Step 6: Run the agent in a kosa8 microVM#

maqpna dev run --kosa8-image python:3.12-alpine -- python -c "$(cat agent.py)"

--kosa8-image boots a microVM, pulls the image on the host, and starts your command in it. The microVM's only allowed destination is the gateway. MAQPNA checks that on the running sandbox before the agent starts. The token goes in through the container's standard input into a file. It is never in the agent's environment or command line.

Expected result. You see, in order:

  • a chat completion from llamay (JSON with choices and usage);
  • the echo result, with "text":"hello from kosa8";
  • a JSON-RPC error for the second echo call, which names the rule never-touch-system-namespaces;
  • egress: blocked;
  • a summary line from maqpna dev, such as dev-1a2b3c4d: 2 allow, 1 deny, cost $0.0000 (maqpna dev timeline --last).

When the command exits, MAQPNA removes the sandbox and wipes its disks. Add --kosa8-keep to keep it, for example to fork it.

Step 7: Find the decisions in the audit ledger#

List what the session did:

maqpna dev timeline --last

You see one row per governed call: the model call on llm/llamay/chat.completions and the two echo tool calls, each with its decision (allow or deny) and the rule or reason.

The full records are in the audit ledger, .maqpna/audit.jsonl. The model call's record names the llamay context the session ran in:

grep -c llamayContext .maqpna/audit.jsonl          # model calls bound to a llamay context
grep -c 'llamay/audit-anchor' .maqpna/audit.jsonl  # llamay's audit chain, anchored
grep -c 'kosa8/audit-anchor' .maqpna/audit.jsonl   # kosa8's audit head, anchored

Each count is 1 or more. The anchors are written on a timer, so wait 30 seconds if one is still 0.

Verify both chains:

maqpna audit verify .maqpna/audit.jsonl
maqpna llamay verify-audit .maqpna/llamay-audit.jsonl

Both exit with code 0 when the chain is intact, and 3 when it is broken. llamay's audit records counts and the context id, never prompts or completions. The same context id appears in both logs, so you can match one call across them.

Step 8: Tear down#

maqpna dev down
kosa8 sandbox ls           # sandboxes you kept with --kosa8-keep
kosa8 daemon stop          # optional

maqpna dev down stops the broker, the gateway, mcp-echo and llamay. It keeps the state and the ledger in .maqpna/, so you can read them later. To start from scratch, delete the stack-quickstart directory. Remove a kept sandbox with kosa8 sandbox rm <name>.

What can go wrong#

Symptom Cause Fix
--llamay needs the llamay binary on PATH or --llamay-bin llamay is not on your PATH. Open a new terminal after installing, or pass --llamay-bin /path/to/llamay.
llamay: ... (see .maqpna/logs/llamay.log) llamay did not become healthy within 2 minutes, often because the model is not in the store. Run llamay pull qwen2.5:0.5b, then read the log.
dev up fails on a port Something already listens on 8080, 8081, 8090 or 11435, such as your own llamay serve. Stop it, or use --gateway-port, --identity-port, --echo-port or --llamay-port.
--kosa8: ... from dev up The kosa8 daemon is not running. kosa8 daemon start, then kosa8 doctor.
cannot find this host's LAN address (use --kosa8-host-ip) No LAN interface, for example on an offline laptop. Pass --kosa8-host-ip with a non-loopback IPv4 address of this machine.
dev run --kosa8-image cannot start the agent or reach the gateway dev up ran without --kosa8 (the gateway listens on loopback, which guests cannot reach), or the image has no /bin/sh. Run maqpna dev down, then dev up again with --kosa8. Use an image with /bin/sh.
The model call answers 503 with llamay_audit_unverified llamay's audit chain did not verify, so the gateway took llamay out of routing. Run maqpna llamay verify-audit .maqpna/llamay-audit.jsonl to see where it breaks.
kosa8/audit-unverified in the ledger kosa8 reported a broken audit chain, or refused the gateway's records (for example a missing kosa8 audit.key). Run kosa8 doctor. See stack troubleshooting.

Next steps#