MAQPNADocs

Developer loop#

You can run an agent under MAQPNA on a laptop, with the same gateway, identity broker, policies, data loss prevention (DLP) and audit ledger as in a cluster, and no Kubernetes, Docker or kind. The local MAQPNA is three or four local processes bound to 127.0.0.1, started by maqpna dev up. Your agent then runs under its own session identity with maqpna dev run, and maqpna dev timeline shows each tool and model call it made and the decision on it.

The quickstart at the top of maqpna help is exactly this loop:

Quickstart:
  maqpna dev up                        Start a local MAQPNA: gateway, identity broker, test MCP server
  maqpna dev run -- python agent.py    Run your agent under it, with its own session identity
  maqpna dev timeline --last           See each tool call it made and the decision on it

What dev up starts#

flowchart LR
    subgraph Laptop["127.0.0.1 (state in ./.maqpna)"]
      AG["Your agent<br/>maqpna dev run -- CMD"]
      GW["maqpna-gateway :8080<br/>gateway.json, policies.json,<br/>audit.jsonl"]
      IB["maqpna-identity :8081<br/>generated identity.key"]
      EC["mcp-echo :8090<br/>test MCP server"]
      SL["stub-llm :8000<br/>scripted model (--stub-llm)"]
    end
    AG -- "MAQPNA_TOOL_ECHO_URL<br/>Bearer session token" --> GW
    AG -- "OPENAI_BASE_URL (with --stub-llm)" --> GW
    GW -- "JWKS" --> IB
    GW --> EC
    GW --> SL
    CLI["maqpna dev run"] -- "POST /v1/token" --> IB
    TL["maqpna dev timeline"] -- "GET /v1/sessions/{id}/timeline" --> GW

Step by step#

maqpna dev up#

  1. Checks that no local MAQPNA is already running from the state directory and that the ports are free (gateway 8080, identity broker 8081, mcp-echo 8090, stub-llm 8000 with --stub-llm; each has a --*-port flag).
  2. Generates a random admin token and a random broker token, and writes into ./.maqpna (or --dev-dir): policies.json (the built-in dev policy, or --policy FILE) and gateway.json (file ledger audit.jsonl, policy reload every 2 seconds, approval timeout 300 seconds, identity verification against the broker's JWKS, issuer maqpna-identity, audience maqpna-gateway; with --model or --stub-llm, model routes in scope-only mode).
  3. Finds each server binary: next to maqpna, then on PATH, then (in a source checkout) built with go build, otherwise downloaded for the same release version and verified against the release checksums.txt (and cosign when available), then cached. The test servers mcp-echo and stub-llm run from the maqpna binary itself unless --from-source.
  4. Starts, in order, each with logs in .maqpna/logs/<name>.log: mcp-echo; stub-llm (waits for /healthz); the identity broker with a generated key (waits for its JWKS); the gateway (waits for /readyz).
  5. Saves .maqpna/state.json (URLs, tokens, process IDs, last session) and .maqpna/dev.env, and prints the endpoints.

The built-in dev policy has two policies for every namespace and agent: baseline-guardrails (deny kube-* namespaces in arguments and tools named *secret*, *credential*, *token*) and default-agent-policy (default deny; allow read-only tools such as get_*, list_*, echo; deny prod* namespaces; require approval for delete_*, drop_*, deploy_* and similar; rate-limit writes).

maqpna dev run -- CMD#

sequenceDiagram
    autonumber
    participant D as maqpna dev run
    participant IB as Identity broker :8081
    participant A as Your agent process
    participant GW as Gateway :8080
    D->>IB: POST /v1/token (broker token)<br/>namespace dev, agent coder, user you@localhost,<br/>tier dev, ttl 1h, session dev-xxxxxxxx
    IB-->>D: session token
    D->>A: exec CMD with MAQPNA_GATEWAY_URL, MAQPNA_TOKEN,<br/>MAQPNA_SESSION, MAQPNA_AGENT, MAQPNA_TOOL_ECHO_URL<br/>(+ OPENAI_BASE_URL, OPENAI_API_KEY with --stub-llm)
    A->>GW: tools/call echo, model calls
    GW-->>A: allowed, denied or held for approval
    A-->>D: exit code
    D->>GW: GET /v1/sessions/dev-xxxxxxxx/timeline
    D-->>D: print the summary line, exit with CMD's exit code
  1. Mints a session token at the local broker: namespace dev, agent coder, user you@localhost, tier dev, one hour, session ID dev-<8 hex>, scopes tools:<each MCP server> plus models:* (all overridable by flags).
  2. Runs your command with the environment the SDKs read: MAQPNA_GATEWAY_URL, MAQPNA_TOKEN, MAQPNA_SESSION, MAQPNA_AGENT and MAQPNA_TOOL_<NAME>_URL={gateway}/mcp/<name>. With --stub-llm it also sets OPENAI_BASE_URL, OPENAI_API_KEY (the session token), MAQPNA_MODEL_PROVIDER=openai and MAQPNA_MODEL_NAME=stub, so an unmodified OpenAI SDK goes through the gateway.
  3. Prints maqpna dev: session <id> (agent coder, namespace dev, on behalf of you@localhost) to stderr, passes stdin and stdout through, and when the command ends prints the timeline summary and exits with the command's exit code.

maqpna dev timeline --last#

  1. Reads the last session from state.json (or --session S).
  2. Calls GET /v1/sessions/<id>/timeline?limit=200 with the local admin token. The gateway builds the timeline from the audit ledger, ordered by ledger sequence, plus the approval store and taint state. It never contains raw arguments.
  3. Prints one row per step and the summary line. -o json prints the full structure.

dev status, dev env, dev down#

  • maqpna dev status prints each process with its PID and running or stopped, plus whether the gateway is ready; it exits 3 when something is down.
  • maqpna dev env prints export lines for the local endpoints and tokens, for running an agent from another shell.
  • maqpna dev down sends SIGTERM, then SIGKILL after 10 seconds. State and the ledger are kept, so maqpna audit verify .maqpna/audit.jsonl still works afterwards.

MAQPNA Desk's dev window and maqpna desk drive the same commands: start and stop the local MAQPNA and follow the timeline of the last dev run session live. Held approvals appear in the Desk inbox.

What you see#

Output formats from cmd/maqpna/dev.go; paths, IDs and times are illustrative:

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

$ maqpna dev run -- python agent.py
maqpna dev: session dev-3f9c1a2b (agent coder, namespace dev, on behalf of you@localhost)
...
maqpna dev: dev-3f9c1a2b: 2 allow, 1 deny, cost $0.0003 (maqpna dev timeline --last)

$ maqpna dev timeline --last
TIME      KIND        SERVER/TOOL                DECISION  DETAIL
14:05:09  model_call  llm/stub/chat.completions  allow     usage:prompt=212,completion=38
14:05:09  tool_call   echo/echo                  allow     matched rule read-only-tools
14:05:10  tool_call   echo/delete_resource       deny      system namespaces are off-limits to agents
dev-3f9c1a2b: 2 allow, 1 deny, cost $0.0003 (maqpna dev timeline --last)

The summary counts calls by decision (allow, deny, require_approval) in alphabetical order; with no calls it reads no governed calls. A held call shows the approval ID and status in DETAIL. See maqpna dev up, maqpna dev run, maqpna dev timeline, maqpna dev status and maqpna dev down.

Failure modes#

Failure Effect
A port is in use dev up stops before starting anything and names the port flag to change
A local MAQPNA is already running from this directory dev up refuses: run maqpna dev down first
A server binary cannot be found or its checksum does not match dev up fails; nothing unverified is run
dev run without dev up identity broker (is maqpna dev up running?)
A server crashed maqpna dev status shows it stopped and exits 3; its log is in .maqpna/logs/