Run MAQPNA sessions in kosa8 sandboxes
Install kosa8, connect a local MAQPNA to it, run a governed agent in a kosa8 microVM, fork, snapshot, restore and attest it, and anchor the two audit chains.
kosa8 is the microVM container platform from Kosa8 Labs. Each kosa8 sandbox is a real VM with its own kernel, and kosa8 can fork, snapshot and restore a running sandbox in about a second. This page connects kosa8 to a local MAQPNA (maqpna dev), step by step. When you finish, your agent runs in a kosa8 microVM that can reach only the MAQPNA gateway, holds its own short-lived session token, and can be forked into a governed child session.
What kosa8 adds#
| kosa8 feature | What MAQPNA does with it |
|---|---|
| microVM isolation | Each session runs in its own kosa8 microVM: Apple Virtualization.framework on macOS, Cloud Hypervisor over KVM on Linux. MAQPNA reports this isolation as microvm. |
| Gateway-only egress | MAQPNA creates the sandbox with kosa8 egress allow:<gateway IP>. kosa8 enforces it on the host and in the guest, so the agent can reach the gateway and nothing else. |
| Fork, snapshot, restore | maqpna dev fork live-forks a running sandbox into a child session. maqpna dev snapshot and maqpna dev restore save a session and boot it again as a new session. Each copy gets its own session token. |
| Docker compatibility | kosa8 serves a Docker-compatible API, so the docker CLI and Compose work against it. MAQPNA does not use that API: it calls kosa8's native API on the same daemon. |
| Identity checks | Before it mints a session token, the identity broker asks kosa8 itself whether the sandbox runs that session, with egress pinned to the gateway. The result goes into the token, and policies can match on it. |
| Audit anchors | The gateway posts each tool-call decision to kosa8's hash-chained audit log, and writes kosa8's audit head into the MAQPNA audit ledger every 30 seconds. |
This is the laptop and edge path. kosa8 is not a Kubernetes RuntimeClass, so in a cluster sessions keep running in the gVisor, Kata and confidential VM trust tiers. See MAQPNA on Kubernetes.
How it fits together#
flowchart LR
subgraph host["Your machine"]
subgraph vm["kosa8 microVM · egress: gateway IP only"]
A["Agent<br/>reads MAQPNA_TOKEN_FILE"]
end
G["MAQPNA gateway<br/>LAN IP :8080"]
B["Identity broker<br/>127.0.0.1:8081"]
K["kosa8d<br/>~/.kosa8/run/kosa8.sock"]
L["llamay<br/>127.0.0.1:11435"]
M["MCP servers"]
end
A -->|"tool and model calls"| G
G -->|"/mcp/NAME"| M
G -->|"/llm/llamay"| L
B -->|"verify sandbox before mint"| K
G -->|"mcp.decision records"| K
G -->|"read audit head every 30 s"| K
- The agent runs inside the microVM. Its only network destination is the gateway.
- The gateway decides every tool and model call, writes the audit record, and forwards allowed calls to MCP servers and to llamay.
- The identity broker and the gateway talk to kosa8d on its unix socket, as you.
Before you begin#
- A machine that runs kosa8 sandboxes: an Apple silicon Mac, or x86-64 Linux with KVM. Windows 11 runs kosa8 in WSL2 as a preview. See kosa8 requirements.
- The
maqpnaCLI, version 2.1.0 or later. See Install. - An agent image that contains
/bin/sh.alpine:3.20is enough to try it;python:3.12-alpineruns Python agents. - Optional: llamay, for a local model. See llamay with MAQPNA.
MAQPNA's kosa8 support was run end to end with kosa8 0.5.0 and 0.9.36 on macOS (Apple silicon). Linux uses the same kosa8 API but has not been run with MAQPNA yet. See Compatibility.
Step 1: Install kosa8#
Install kosa8 with the method kosa8.com documents for your system.
curl -fsSL https://kosa8.com/install.sh | sh
Or download the Mac app from kosa8.com/download and drag kosa8.app to Applications. The app includes the CLI and starts the daemon for you.
curl -fsSL https://kosa8.com/install.sh | sh
kosa8 on Linux needs KVM, cloud-hypervisor, virtiofsd and access to /dev/kvm. The installer lists what is missing. .deb, .rpm and .apk packages are on the download page.
irm https://kosa8.com/install.ps1 | iex
kosa8 wsl setup
Windows 11 x86-64 runs kosa8 in WSL2 (preview). Run kosa8 wsl setup once after the install. MAQPNA has not been run against kosa8 on Windows yet.
Step 2: Start the kosa8 daemon#
kosa8 daemon start
kosa8 daemon status
The daemon, kosa8d, is a per-user daemon. It serves its API on the unix socket ~/.kosa8/run/kosa8.sock (mode 0600). MAQPNA uses that socket by default; nothing else needs configuring.
Check it worked. Boot a sandbox with kosa8 alone:
kosa8 run alpine echo hi
It prints hi. The first run downloads and verifies kosa8's guest image. If it fails, run kosa8 doctor.
Step 3: Connect MAQPNA to kosa8#
There are three ways MAQPNA reaches kosa8d. Use the first one on a laptop.
| How | Who uses it | Configure |
|---|---|---|
The default socket ~/.kosa8/run/kosa8.sock |
maqpna dev, the sandbox driver, the identity broker check and the gateway |
Nothing |
KOSA8_HOST and KOSA8_TOKEN environment variables |
The sandbox driver and the broker check (the same variables kosa8's own CLI reads) | Export both before maqpna dev up. A TCP daemon always needs a token. |
The gateway's kosa8 block: socket, or url with tokenFile |
The gateway's audit mirror and anchor | gateway.json. A plain http:// URL is allowed only to a loopback host; put a remote kosa8d behind TLS. |
kosa8 daemon start --listen 127.0.0.1:7440 also serves the API on TCP, protected by a token that kosa8 writes to ~/.kosa8/worker-token. For the gateway, that is:
{
"kosa8": {
"url": "http://127.0.0.1:7440",
"tokenFile": "/Users/you/.kosa8/worker-token",
"mirrorDecisions": true,
"anchorIntervalSeconds": 30
}
}
Every field is in the kosa8 reference.
Step 4: Start a local MAQPNA for kosa8#
maqpna dev up --kosa8
Add --llamay MODEL to serve a local model too, for example maqpna dev up --kosa8 --llamay qwen2.5:0.5b.
--kosa8 changes four things compared with a plain maqpna dev up:
- It checks that kosa8d answers (
GET /kosa8/v1/health) and stops with an error if it does not. - The gateway listens on this machine's LAN IPv4 address, not on 127.0.0.1. kosa8 guests reach the host through host sockets and cannot reach its loopback. Choose the address with
--kosa8-host-ip IP. - The identity broker starts with
-sandbox-check kosa8 -sandbox-check-gateway <gateway URL>. It mints a sandbox's token only after kosa8d confirms the sandbox. .maqpna/gateway.jsongets"kosa8": {"mirrorDecisions": true, "anchorIntervalSeconds": 30}.
Check it worked.
maqpna dev status
grep -A3 '"kosa8"' .maqpna/gateway.json
The gateway URL in the maqpna dev up output shows your LAN address, for example http://192.168.1.20:8080.
Step 5: Run a governed agent in a kosa8 sandbox#
Run any command in a microVM with --kosa8-image. This one prints the token file path and tries the internet:
maqpna dev run --kosa8-image alpine:3.20 -- sh -c '
echo "token file: $MAQPNA_TOKEN_FILE (runtime $MAQPNA_RUNTIME)"
env | grep -c MAQPNA_TOKEN= || true
wget -T 3 -qO- http://1.1.1.1 >/dev/null 2>&1 && echo "egress: open" || echo "egress: blocked"'
What happens:
- MAQPNA creates sandbox
maqpna-<session>with egressallow:<gateway IP>and labels that name the session, agent, namespace, user and scopes. - kosa8 pulls the image on the host and starts the container under a small boot shell. The shell waits for one line on its stdin.
- MAQPNA asks the broker for the token, with a proof that names the sandbox. The broker asks kosa8d about the sandbox: its labels, the egress kosa8 enforces now, and its kernel, rootfs and image digests. Only then does it mint.
- MAQPNA writes the token to the container's stdin. The boot shell saves it to
/tmp/maqpna/token(mode 0600) and starts your command. The token is never in argv or in the container's environment. - MAQPNA checks the live sandbox's egress once more, then streams the output.
You see token file: /tmp/maqpna/token (runtime kosa8), 0 (no MAQPNA_TOKEN in the environment) and egress: blocked. On stderr, maqpna dev prints the session id and the broker verified kosa8 sandbox maqpna-dev-… before minting: microvm, attestation inventory, egress allow:192.168.1.20.
When the command exits, MAQPNA removes the sandbox and wipes its disks. Keep it with --kosa8-keep; size it with --kosa8-cpus and --kosa8-mem-mb.
The agent sees MAQPNA_GATEWAY_URL, MAQPNA_SESSION, MAQPNA_AGENT, MAQPNA_TOOL_<NAME>_URL, and, with a model route, OPENAI_BASE_URL and MAQPNA_MODEL_NAME. It must read the token from MAQPNA_TOKEN_FILE on every call. The MAQPNA SDKs do. See Connect to the gateway.
Step 6: Fork, snapshot, restore and attest#
These commands work on a sandbox you kept with --kosa8-keep. Start a long-running one:
maqpna dev run --kosa8-image alpine:3.20 --kosa8-keep --session demo -- sh -c '
while true; do date; sleep 5; done'
Open a second terminal in the same directory for the commands below.
Fork a running session#
maqpna dev fork --session demo --attach=false
forked session demo -> demo-fork-1 (kosa8 sandbox maqpna-demo-fork-1, 1997 ms); the child's model context forks on its first model call
The time varies. The child session is <session>-fork-1 (change the stem with --name). Its token names demo as maqpna_parent_session and has exactly the parent's namespace, agent, user and scopes. The gateway links the child to the parent: it inherits the parent's taint and budget, and on llamay its model context forks from the parent's.
sequenceDiagram
autonumber
participant D as maqpna dev fork
participant K as kosa8d
participant C as Clone (microVM)
participant B as Identity broker
D->>K: POST /kosa8/v1/sandboxes/maqpna-demo/fork {n:1}
K-->>C: clone resumes at once (parent's token file)
K-->>D: {forks, fork_ms}
D->>B: POST /v1/token (session demo-fork-1, parent demo, proof)
B->>K: list sandboxes, egress, inventory
B-->>D: child session token
D->>K: POST .../maqpna-demo-fork-1/exec (rewrite token file)
alt re-key fails
D->>K: DELETE .../maqpna-demo-fork-1?wipe=1
end
C->>C: next call reads the child token
Snapshot and restore#
maqpna dev snapshot --session demo --id before-refactor
maqpna dev restore --checkpoint before-refactor --attach=false
dev snapshot first asks the gateway to checkpoint the session's llamay contexts (when llamay runs), then snapshots the sandbox, and writes .maqpna/checkpoints/before-refactor.json. dev restore boots that snapshot as a new session, demo-restore-1 by default, with its own token and demo as its parent. It re-keys the copy like a fork, proves its egress, and resumes the llamay contexts. If the checkpointed session was revoked, the gateway refuses and dev restore removes the restored sandbox.
Attest#
maqpna dev attest --session demo
It prints the sandbox's kernel, rootfs and image digests as kosa8 reports them, and the egress kosa8 enforces now. This is attestation level inventory: digests the host reports, not a measured boot. The command exits with status 3 when the egress is not exactly the gateway.
Clean up#
Kept sandboxes stay until you remove them. List and remove them with kosa8:
kosa8 sandbox ls
kosa8 sandbox rm maqpna-demo
Step 7: Use identity checks in a policy#
Because the broker verified the sandbox, every session token from Step 5 carries a maqpna_substrate claim: check: kosa8, isolation: microvm, attestation: inventory, the egress policy and a digest of the measurements. A rule's substrate condition matches on it. A session that no check verified has isolation and attestation none.
This policy lets agent coder run shell commands only from a sandbox kosa8 verified. Everything else falls to the default action, deny:
{
"policies": [
{
"name": "shell-from-microvm",
"namespace": "dev",
"agents": ["coder"],
"defaultAction": "deny",
"rules": [
{
"name": "shell-in-verified-sandbox",
"servers": ["maqpna-client"],
"tools": ["coder__bash_run"],
"substrate": {"checks": ["kosa8"]},
"action": "allow"
}
]
}
]
}
apiVersion: maqpna.com/v1alpha1
kind: ToolPolicy
metadata:
name: shell-from-microvm
namespace: dev
spec:
agents: ["coder"]
defaultAction: deny
rules:
- name: shell-in-verified-sandbox
servers: ["maqpna-client"]
tools: ["coder__bash_run"]
substrate:
checks: ["kosa8"]
action: allow
Load the JSON with maqpna dev up --kosa8 --policy policy.json. The substrate condition also takes isolation, notIsolation, attestation and notAttestation; see the reference.
To accept only known kernels and images, start your own broker with -sandbox-check-allowed FILE, a JSON map such as {"kernel": ["<sha256>"], "image:docker.io/library/alpine:3.20": ["<sha256>"]}. Upgrading kosa8 can change the guest rootfs digest, so update that file before you upgrade.
Step 8: Check the audit anchoring#
With dev up --kosa8, two tamper-evident logs describe each session: the MAQPNA audit ledger (what the agent was allowed to do) and kosa8's audit log (what happened to its sandbox). The gateway ties them together:
- Decision mirror. Each tool-call decision in the ledger is posted to kosa8 as an
mcp.decisionrecord, with the server, tool, rule, allowed flag and a SHA-256 digest of the arguments, never the arguments. The record points back tomaqpna-ledger-<seq>. Posting is asynchronous and bounded: a slow or absent kosa8d never delays a call, and the MAQPNA ledger stays the record of authority. - Audit anchor. Every 30 seconds the gateway asks kosa8d to verify its chain and reads its head. A new verified head is written to the ledger as rule
kosa8/audit-anchor. A broken chain, or a head that went back, is written once askosa8/audit-unverified.
Check it worked.
grep -c '"kosa8/audit-anchor"' .maqpna/audit.jsonl
maqpna audit verify .maqpna/audit.jsonl
eval "$(maqpna dev env)"
curl -s "$MAQPNA_GATEWAY_URL/metrics" | grep maqpna_gateway_kosa8
kosa8 audit verify
- The ledger holds at least one
kosa8/audit-anchorrecord, withkosa8AuditSeq,kosa8AuditHeadandkosa8AuditVerifiedinext. maqpna audit verifyprintsOK.maqpna_gateway_kosa8_mirror_total{outcome="ok"}counts mirrored decisions, andmaqpna_gateway_kosa8_audit_verifiedis1.kosa8 evidence exportnow carries MAQPNA's decisions next to the sandbox events.
What can go wrong#
| Symptom | Cause | Fix |
|---|---|---|
--kosa8: kosa8d unreachable (is `kosa8 daemon start` running?) |
kosa8d is not running, or KOSA8_HOST points somewhere else. |
Run kosa8 daemon start, or unset KOSA8_HOST. |
cannot find this host's LAN address (use --kosa8-host-ip) |
No default route, for example offline. | Pass --kosa8-host-ip with a non-loopback IPv4 address of this machine. |
sandbox: egress is not pinned to the gateway: kosa8 sandboxes reach the gateway by a non-loopback IPv4 literal … |
The local MAQPNA was started without --kosa8, so the gateway is on 127.0.0.1. |
maqpna dev down, then maqpna dev up --kosa8. |
| The sandbox starts, but the agent never runs | The image has no /bin/sh, which the boot shell needs. |
Use an image with /bin/sh (Alpine, Debian, Python images). |
mint: HTTP 403 identity: proof refused: sandbox … |
The broker's check failed: wrong labels, egress not pinned, or a measurement not in -sandbox-check-allowed. |
Read the reason after proof refused:. Run maqpna dev attest to see what kosa8 reports. |
session S has no kosa8 sandbox (start it with dev run --kosa8-image ... --kosa8-keep) |
The sandbox was removed when the command exited. | Run the agent with --kosa8-keep, and fork while it runs. |
sandbox: copy could not be re-keyed: … (removed) |
MAQPNA could not write the child token into the clone, so it removed the clone. | Check that the agent's container still runs and has /bin/sh. |
| A forked agent's calls still show the parent session | The agent caches its token. | Read MAQPNA_TOKEN_FILE on every call. |
dev fork fails inside kosa8 with restore machine state: … permission denied |
Seen with kosa8 0.5.0 on macOS with less than 2 GB of free disk. | Free disk space. dev fork leaves no clone or child session behind. |
maqpna_gateway_kosa8_mirror_total{outcome="error"} grows |
kosa8d refuses the records, for example because its own audit key is missing. | Run kosa8 doctor. The anchor then records kosa8/audit-unverified from kosa8's verdict. |
dev attest exits with status 3 |
The egress kosa8 enforces is not exactly the gateway. | Remove the sandbox and start a new one with maqpna dev run --kosa8-image. |
More fixes for the whole stack are in Troubleshooting.
Next steps#
- Tutorial: private coding agent: a coding agent in a kosa8 microVM, a local llamay model, policies, a fork and the audit trail.
- kosa8 reference: every config field, flag, API call, error and metric.
- llamay with MAQPNA: local models whose context forks with the session.
- Sandbox drivers and Identity checks.
- Developer workflow for the daily loop with the whole stack.