MAQPNADocs

CRD data model#

MAQPNA's declarative API is the Kubernetes API group maqpna.com, version v1alpha1 (api/v1alpha1/). Every kind has a status subresource. The operator reads these objects and produces two kinds of output: sandboxes, tokens and NetworkPolicies for sessions, and rendered files (ConfigMaps and Secrets in the gateway namespace) that the gateway hot-reloads. The gateway never reads the CRDs for decisions; it reads only the rendered files.

The kinds#

Kind Scope Short name Status highlights
TrustTier Cluster tier warmPoolReady; conditions RuntimeClassAvailable, SandboxTemplateReady, WarmPoolReady, Ready
Agent Namespaced mqagent activeSessions; conditions TierFound, PolicyFound, SovereigntyCompliant, ModelCredentialsReady, Ready
AgentSession Namespaced asess phase (Pending, Provisioning, Running, Suspended, Completed, Failed), outcome.result (Succeeded, Failed, Expired, Revoked, Cancelled), spiffeID, principal, attestation, bootstrap, fork, readyIn
AgentSessionSnapshot Namespaced asnap phase (Pending, Capturing, Ready, Failed), source, forks; condition SnapshotReady
ToolPolicy Namespaced tp condition Ready (Published or InvalidPolicy)
MCPServer Namespaced mcps gatewayKey, observedTools, driftedTools, lastSeen; conditions Ready, SovereigntyCompliant, Pinned
A2APeer Namespaced a2ap gatewayPath; conditions Ready, SovereigntyCompliant
AgentRevocation Namespaced arev affectedSessions, appliedAt; condition Ready (Published, Expired, Invalid)
BudgetPolicy Cluster bp condition Ready (Published)
Tenant Cluster tn keyId, trustDomain, budgetPolicy, isolationViolations; conditions NamespacesOwned, KeyReady, Isolated, Ready
SovereigntyPolicy Cluster sovpol violations, lastEvaluated; conditions Active, Compliant, RetentionBelowAIAct
MemoryStore Namespaced mem gatewayKey; conditions Ready, ResidencyCompliant
ConnectedAccount Namespaced conn phase (Connected, ReauthRequired, Revoked), grant, scopes, token timestamps

How the kinds relate#

classDiagram
    direction LR
    class TrustTier {
      runtimeClassName
      isolation gvisor|microvm|confidential
      sandboxTemplateName
      warmPoolSize
      jurisdiction
      egress gateway-only|deny-all
      attestationRequired
      maxSessionTTL
    }
    class Agent {
      image
      tier
      model
      tools[]
      policyRef
      scopes[]
      budget
      a2a
      profile
    }
    class AgentSession {
      agentRef
      user
      scopes[]
      ttl
      suspend
      input
      fork
      status.phase
    }
    class AgentSessionSnapshot {
      sessionRef
      strategy
      status.forks
    }
    class ToolPolicy {
      agents[]
      defaultAction
      rules[]
      toolPins[]
      toolLabels[]
      cedar
    }
    class MCPServer {
      url
      trust
      dataClass
      auth
      shared
      toolPins
      dlpProfile
    }
    class A2APeer {
      url
      cardKeys
      allowedCallers[]
      skills[]
    }
    class AgentRevocation {
      match
      action block|suspend|terminate
      expiresAt
    }
    class BudgetPolicy {
      namespaces
      users
      onExceed
    }
    class Tenant {
      namespaces[]
      trustDomain
      signingKeySecretRef
      quotas
    }
    class SovereigntyPolicy {
      allowedJurisdictions
      allowedRegistries
      allowedEgressHosts
      requireAttestationFor
      enforcement
    }
    class MemoryStore {
      agents[]
      backend
      jurisdiction
    }
    class ConnectedAccount {
      user
      provider
      status.phase
    }
    class Sandbox {
      upstream agents.x-k8s.io
    }
    class SandboxClaim {
      upstream extensions.agents.x-k8s.io
    }
    class SandboxWarmPool {
      upstream maqpna-tier
    }
    AgentSession --> Agent : spec.agentRef
    Agent --> TrustTier : spec.tier
    Agent --> ToolPolicy : spec.policyRef
    ToolPolicy ..> Agent : spec.agents globs
    Agent --> MCPServer : tools[].serverRef
    AgentSession --> AgentSessionSnapshot : spec.fork.fromSnapshot
    AgentSessionSnapshot --> AgentSession : spec.sessionRef
    AgentSession *-- Sandbox : owns (direct mode)
    AgentSession *-- SandboxClaim : owns (warm pool)
    SandboxClaim --> SandboxWarmPool : warmPoolRef
    TrustTier *-- SandboxWarmPool : creates maqpna-tier
    MemoryStore --> Agent : spec.agents
    Tenant *-- BudgetPolicy : creates tenant-name
    AgentRevocation ..> AgentSession : match sessions, agents, users
    A2APeer ..> Agent : allowedCallers
    SovereigntyPolicy ..> Agent : checked on reconcile

Reading the diagram:

  1. A session names its agent (spec.agentRef, same namespace). Its scopes must be a subset of the agent's.
  2. An agent names a cluster-scoped trust tier (spec.tier) and a ToolPolicy in its namespace (spec.policyRef). A policy separately selects agents by spec.agents globs; both must line up for the policy to apply at the gateway.
  3. An agent's tools[].serverRef points to an MCPServer in its own namespace, or in another namespace that shares it (shared, allowedNamespaces).
  4. A session owns exactly one upstream Sandbox (direct mode) or SandboxClaim (when the tier sets sandboxTemplateName). The trust tier creates the SandboxWarmPool named maqpna-<tier>.
  5. A fork names a snapshot (spec.fork.fromSnapshot) or a session (spec.fork.fromSession, which makes the operator create a snapshot <session>-fork).
  6. A Tenant labels its namespaces maqpna.com/tenant=<name> and owns a generated BudgetPolicy tenant-<name> built from its quotas.
  7. AgentRevocation objects match sessions by name, agent, user or token ID; in the gateway namespace (maqpna-system by default) they are cluster-wide.
  8. ConnectedAccount objects are written by the gateway's token vault (status only); no operator reconciler manages them.
  9. The SovereigntyPolicy default is consulted by the Agent, AgentSession, MCPServer, A2A, model-route and MemoryStore reconcilers.

From CRDs to gateway files#

flowchart LR
    TP[ToolPolicy] --> P["ConfigMap maqpna-policies<br/>policies.json"]
    AG[Agent] --> M["ConfigMap maqpna-models<br/>models.json"]
    AG --> MC["Secret maqpna-model-credentials<br/>route.key"]
    MS[MCPServer] --> U["ConfigMap maqpna-upstreams<br/>upstreams.json"]
    AG --> U
    MS --> UC["Secret maqpna-upstream-credentials<br/>ns.name.token and others"]
    AG --> A2["ConfigMap maqpna-a2a<br/>a2a.json"]
    PEER[A2APeer] --> A2
    REV[AgentRevocation] --> R["ConfigMap maqpna-revocations<br/>revocations.json"]
    AG --> B["ConfigMap maqpna-budgets<br/>budgets.json"]
    BP[BudgetPolicy] --> B
    MEM[MemoryStore] --> ME["ConfigMap maqpna-memory<br/>memory.json"]
    TN[Tenant] --> T["ConfigMap maqpna-tenants<br/>tenants.json"]
    TN --> TK["Secret maqpna-tenant-keys<br/>tenant.pem"]
    TN --> BP
    P & M & MC & U & UC & A2 & R & B & ME & T --> GW(["maqpna-gateway"])
    T & TK --> IB(["maqpna-identity"])

Every rendered ConfigMap is compact JSON with sorted keys, written by one reconciler that re-renders the whole document on any change. Secrets are written before the ConfigMap that references them. Invalid objects are left out of the document and get Ready=False, so one bad object never takes the others down. The operator exports the size of each rendered ConfigMap as maqpna_rendered_configmap_bytes{name} (ConfigMaps are limited to 1 MiB).

What you see#

kubectl get columns come from the CRD printer columns (api/v1alpha1/agentsession_types.go; values illustrative); maqpna explain documents any kind or field:

$ kubectl get asess -n team-a
NAME       PHASE       AGENT   USER            OUTCOME     READY-IN   AGE
fix-4790   Completed   coder   bob@acme.eu     Succeeded   2.43s      118m
fix-4821   Running     coder   alice@acme.eu               1.71s      3m

See maqpna explain, maqpna init and maqpna validate.