MAQPNADocs

Gateway#

The MAQPNA gateway (maqpna-gateway) is the component every governed call passes through. For each Model Context Protocol (MCP) tool call, agent-to-agent (A2A) call, model call and browser egress connection, it verifies identity, checks the kill switch, runs data loss prevention (DLP), evaluates policy, applies pins, taint and budgets, asks for approval when a rule says so, forwards the call with its own credential, and writes an audit record. It also serves the admin API that the CLI, console and MAQPNA Desk use.

The gateway's request path uses only the Go standard library (plus pgx for the optional PostgreSQL backend). Sandboxes can reach nothing else: their NetworkPolicy allows egress only to the gateway and DNS.

Code: cmd/maqpna-gateway, with pkg/policy, pkg/dlp, pkg/approval, pkg/audit, pkg/finops, pkg/revocation, pkg/taint, pkg/toolpin and pkg/state.

Internals#

flowchart LR
    subgraph In["Callers"]
      SB["Sandboxes<br/>(session token)"]
      EXT["External MCP clients<br/>(IdP token)"]
      ADM["CLI, console, Desk<br/>(admin token or OIDC)"]
      OPR["Operator<br/>(usage reports)"]
    end
    subgraph GW["maqpna-gateway"]
      DL["Data listener :8080<br/>/mcp, /llm, /a2a, egress proxy"]
      AL["Admin listener (adminListen)<br/>/v1/*, /metrics, pprof"]
      PIPE["Pipeline:<br/>auth, revocation, DLP, scope,<br/>guards, pins, policy, budgets,<br/>approvals"]
      ST["Stores: approvals, revocations,<br/>taint, pins, outbox, FinOps,<br/>vault, memory"]
      LED["Audit ledger + checkpoints"]
      CS["Config sync: rendered ConfigMaps"]
    end
    SB & EXT --> DL --> PIPE
    ADM --> AL
    OPR --> DL
    PIPE --> ST
    PIPE --> LED
    PIPE -- "residency-checked transport" --> UP["MCP servers, model endpoints,<br/>A2A peers, the web"]
    LED --> SINK["SIEM sinks, WORM storage"]
    CS --> PIPE

Responsibilities#

Area What the gateway does
MCP POST/GET/DELETE /mcp/{server}: governs tools/call, filters tools/list per caller, scans every response, passes other methods through; MCP revisions 2025-03-26, 2025-06-18, 2025-11-25 and 2026-07-28
Models /llm/{route}/v1/chat/completions, /completions, /embeddings, /models: OpenAI-compatible proxy with token metering, DLP, routing and fallback
A2A /a2a/{ns}/{agent} JSON-RPC and HTTP+JSON bindings, signed agent cards, delegation checks
Egress A forward proxy for browser-profile sandboxes (egress.enabled), with domain policy
Built-in tools maqpna-web (fetch, search), memory, maqpna-exec (code interpreter proxy), evidence and live view
Approvals Holds calls, notifies, collects votes, enforces separation of duties and quorum, CIBA user approval
Kill switch Revocation list from the operator plus break-glass entries
Evidence Hash-chained ledger, HMAC and Ed25519 checkpoints, SIEM streaming, WORM shipping, evidence export, third-party register
Admin API Approvals, audit, sessions and timeline, costs and budgets, policies, servers, revocations, taint, posture, licence

Inputs and outputs#

Input Source
gateway.json (-config, env MAQPNA_GATEWAY_CONFIG, default /etc/maqpna/gateway.json) Helm ConfigMap; decoded strictly (unknown fields rejected)
policies.json, upstreams.json, models.json, a2a.json, revocations.json, budgets.json, memory.json, tenants.json ConfigMaps rendered by the operator, polled every policyReloadSeconds (2 s); with configSync read from the API every second
Credential files Secrets maqpna-upstream-credentials and maqpna-model-credentials
Broker public keys identity.jwksURL (refreshed every 300 s and on an unknown kid) or identity.publicKeyFiles
Licence licenseFile, re-read every 30 s
Output Destination
Forwarded calls Upstreams, with X-Maqpna-Namespace, -Agent, -Session, -User, -Subject, -Groups (and optionally a Txn-Token); the agent's Authorization is never forwarded
Audit records auditPath (JSONL) or the shared audit_chain, SIEM sinks, WORM bucket
Annotations on AgentSession maqpna.com/last-activity, maqpna.com/taints, maqpna.com/result
MCPServer annotation maqpna.com/tool-pins
AgentRevocation objects breakglass-<id> mirrors of break-glass entries
Notifications Webhook, Slack, Microsoft Teams, Matrix

How a tool call is handled#

  1. Authenticate the bearer token (broker Ed25519 token or a trusted external IdP token), then the optional X.509-SVID binding and the tenant key binding.
  2. Resolve {server} for the caller's namespace and agent (unknown_server, ambiguous_server, cross_tenant_server).
  3. Check the kill switch (revoked, or revocation_list_unavailable when the list cannot be read).
  4. Check MCP protocol headers and version.
  5. Run request DLP over the arguments (redact or deny).
  6. Check the scope tools:<server>, the injection guards and the tool pin.
  7. Evaluate policy, then the session budget and hierarchical budgets.
  8. On require_approval, hold the call (synchronously, or return -32002 asynchronously); re-check the kill switch after the wait.
  9. In auditFailurePolicy: closed, write a durable intent record first.
  10. Forward with the gateway's own upstream credential; scan the response with DLP and the guards.
  11. Update taint, meter the cost, and append the audit record before releasing the result.

The full pipeline, with every function and reason, is in gateway request pipeline and a governed tool call.

Configuration#

Top-level keys of gateway.json (cmd/maqpna-gateway/config.go), with defaults:

Key Default Purpose
listen, adminListen :8080, — Data listener; optional separate admin listener (always plain HTTP)
policyFile, policyReloadSeconds —, 2 Policies and reload interval for every rendered file
upstreamsFile, upstreamCredentialsDir, tokenExchangeURL — MCP server registry and credentials
modelsFile, models, modelPolicy —, —, scope-only Model routes; policy also runs ToolPolicy on model calls
identity.{jwksURL, publicKeyFiles, issuer, audience, leewaySeconds, trustedIssuers, resourceBaseURL} audience maqpna-gateway, leeway 30 s Token verification and external MCP clients
auditPath, auditCheckpointEvery, auditFailurePolicy —, 1000, open Ledger, HMAC checkpoint cadence, closed mode
auditCheckpointKey, auditCheckpointIntervalSeconds —, 60 Ed25519 signed checkpoints
auditSinks[] — SIEM streaming
approvalTimeoutSeconds, approvalStorePath, approvalQuorum, approvalPreview 300, —, —, redacted Approvals
adminAuth.{mode, oidc, roles, namespaceRoles} token Admin API authentication and roles
notifiers[], notifyOutboxPath, consoleURL — Notifications
ciba, tokenVault off User approval and per-user tokens
revocationsFile, revocationsJournalPath, revocationMirror — Kill switch
budgetsFile, budgetTimezone, pricingFile, sessionBudgetUSD, budgetCurrency —, UTC Budgets and metering
dlpProfiles, dlp — DLP
taintStorePath, taintRetentionSeconds, pinStorePath, pinCacheSeconds —, 86400, —, 60 Taint and tool pins
stateBackend.{type, dsnFile, schema, maxConns, pollMillis, leader, counters} file Shared state
maxBodyBytes, upstreamTimeoutSeconds 4 MiB, 60 Limits
a2a, txnTokens, tls — A2A, transaction tokens, SVID binding
web, exec, egress, liveView, evidence, guards, modelRouting, captureArgs off Sandbox tools and extensions
otel, sessions, posture, licenseFile, sovereigntyPolicyFile, configSync, activity — Observability and operations

Environment: MAQPNA_ADMIN_TOKEN (static admin and break-glass token), MAQPNA_AUDIT_HMAC_KEY (HMAC checkpoints), MAQPNA_AUDIT_SINK=worm with MAQPNA_AUDIT_WORM_* (WORM shipping), MAQPNA_JURISDICTION. Helm renders gateway.json from gateway.*, adminAuth.*, approvals.*, audit.*, dlp.*, state.*, budgets.*, delegation.* and sovereignty.*. maqpna config validate checks a file offline.

Failure modes#

Failure Behaviour
Policies not loaded at start Every call denied; /readyz 503 policies not loaded
No broker verification keys /readyz 503 no identity verification keys; calls fail authentication
Revocation list missing or invalid Every /mcp and /llm call denied (revocation_list_unavailable); /readyz 503
Invalid rendered file (policies, registry, budgets) Previous content kept; error in GET /v1/policies or /v1/servers
Unknown DLP profile Calls on that binding denied (dlp:profile_unknown)
Upstream unreachable or credential missing upstream_unavailable (502) or upstream_credential_unavailable; nothing sent
Ledger append fails open: logged and counted in maqpna_gateway_audit_errors_total; closed: call refused or result withheld (audit_unavailable), /readyz 503
PostgreSQL unreachable /readyz 503 state backend unreachable; approvals fail closed; reads serve the last folded state
Injection guard unreachable failOpen: false denies (guard_unavailable); the default lets the call through
Licence missing, invalid or expired No effect on traffic; only paid features switch off

Scaling#

The gateway is horizontally scalable. With stateBackend.type: postgres, replicas share approvals, revocations, taint, pins, the outbox, FinOps state, the vault, memory and one audit hash chain, and elect one leader per background job (siem/<sink>, worm/<kind>, tenant-ledgers, memory-sweep, capture-sweep). Still per replica: the policy engine, maxCallsPerMinute windows, guard state and model circuits. With file state, replicas are independent and the chart refuses more than one unless you set guards.allowIndependentGatewayReplicas. See HA and failure handling.

Metrics#

Requests and decisions (maqpna_gateway_requests_total, maqpna_gateway_decisions_total{action,server}, maqpna_gateway_auth_failures_total{reason}, maqpna_gateway_upstream_latency_seconds), approvals (maqpna_gateway_approvals_pending), the kill switch (maqpna_gateway_revoked_total{scope}, maqpna_gateway_revocation_list_ok), DLP, taint, pins, budgets, model routing, audit (maqpna_gateway_audit_head_seq, _audit_errors_total, _audit_commit_seconds, _audit_sink_lag), HA (maqpna_gateway_state_backend_info, maqpna_gateway_leader{job}) and config sync. Full list in observability.

What you see#

/readyz answers 200 when ready, and otherwise 503 with the list of problems:

{"status":"not ready","problems":["revocation list unavailable (failing closed)","state backend unreachable"]}

maqpna doctor evaluates the gateway's posture checks together with cluster checks (format from cmd/internal/cliutil/posture.go; rows illustrative):

STATUS  CHECK                     COMPONENT  DETAIL
PASS    gateway-ready             gateway    HTTP 200
WARN    audit-failure-policy      audit      auditFailurePolicy is open
          fix:                               set gateway.auditFailurePolicy: closed
PASS    state-backend             state      postgres

score 92/100: 0 fail, 1 warn, 12 pass, 2 info, 0 skip, 0 accepted

See maqpna doctor, maqpna config validate and maqpna status.