MAQPNADocs

Package and deploy

Build a container image that runs in a MAQPNA sandbox, apply the Agent and its policy to a cluster, promote it from dev to staging to production, and run it with GitOps.

In a cluster, your agent runs as a container image inside a sandbox the operator creates for each session. The code does not change: the operator gives the sandbox the same variables maqpna dev run gave you on your laptop. What changes is how you ship it: an image in a registry, an Agent and its ToolPolicy applied to the cluster, and a way to promote both from one environment to the next.

flowchart LR
    C["Code + Dockerfile"] --> I["Image<br/>registry.example.eu/agents/triage@sha256:..."]
    M["manifests/ + policies/"] --> V["maqpna validate<br/>maqpna diff"]
    I --> A["maqpna apply<br/>or GitOps"]
    V --> A
    A --> OP["Operator: Agent Ready"]
    OP --> S["maqpna session start<br/>or AgentSession"]
    S --> SB["Sandbox: env, token,<br/>default-deny egress"]

Prerequisites#

  • A cluster with MAQPNA installed (maqpna install, see Install), or a local one (see Install on k3s or kind).
  • A trust tier in the cluster: maqpna tier list.
  • A context for the cluster: maqpna context set dev-cluster --kube-context kind-maqpna --namespace team-a --use.

Step 1: Build an image that fits the sandbox#

The sandbox runs your container as a non-root user (UID 65532 unless the platform assigns one), with a read-only root filesystem, all capabilities dropped and the runtime's default seccomp profile. /tmp is writable. Build for that:

FROM python:3.12-slim AS build
ENV PIP_NO_CACHE_DIR=1 PIP_DISABLE_PIP_VERSION_CHECK=1
RUN python -m venv /opt/venv
COPY sdk/python /src/sdk-python            # a copy of the MAQPNA Python SDK
RUN /opt/venv/bin/pip install /src/sdk-python
COPY requirements.txt /app/requirements.txt
RUN /opt/venv/bin/pip install -r /app/requirements.txt
COPY agent.py /app/agent.py
RUN /opt/venv/bin/python -m compileall -q /opt/venv /app   # nothing is written at runtime

FROM python:3.12-slim
ENV PATH=/opt/venv/bin:$PATH PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 HOME=/tmp TMPDIR=/tmp
COPY --from=build /opt/venv /opt/venv
COPY --from=build /app /app
USER 65532:65532
WORKDIR /app
ENTRYPOINT ["python", "/app/agent.py"]

This follows examples-agents/code-reviewer/Dockerfile in the source tree. Rules for any language:

  • Put no credentials in the image. The session token is mounted at run time; tool and model credentials stay with the gateway.
  • Read the task from MAQPNA_TASK or the file in MAQPNA_TASK_FILE, and report the result with the SDK's report_result before you exit.
  • Write only to /tmp (or the workspace in MAQPNA_WORKSPACE, when the agent has one).
  • The sandbox has default-deny egress. Calls that do not go through the gateway fail.

Build, push and record the digest:

docker build -t registry.example.eu/agents/triage:0.1.0 .
docker push registry.example.eu/agents/triage:0.1.0
docker inspect --format '{{index .RepoDigests 0}}' registry.example.eu/agents/triage:0.1.0

Reference the image by digest (registry.example.eu/agents/triage@sha256:...) in production. A SovereigntyPolicy can require a digest and an allowed registry; check with maqpna sovereignty check -f manifests/ --policy sovereignty.yaml.

Step 2: Create the secrets the gateway needs#

Credentials for MCP servers and models are Secrets in the agent's namespace that only the gateway reads:

kubectl create secret generic github-mcp -n team-a --from-literal=token='<your-github-token>'

The MCPServer references it in spec.auth.secretRef; a model API key goes in Agent.spec.model.apiKeySecretRef ({name, key}). Never reference these Secrets from Agent.spec.env.

Step 3: Apply#

Check, preview, then apply:

maqpna validate -f manifests/ -f policies/ --strict
maqpna diff -f manifests/ -R -n team-a          # exit 3 when there are differences
maqpna apply -f policies/ -n team-a
maqpna apply -f manifests/ -R -n team-a

maqpna apply runs the same offline checks as maqpna validate first, then server-side applies (field manager maqpna-cli), so the API server also runs the CRD schema, CEL rules and admission policies. kubectl apply -f manifests/ works too; you lose only the offline pre-check. Apply the policy before the agent, so its first session is never in a state with no applicable policy (every call denied with no_applicable_policy).

Step 4: Check the rollout#

maqpna rollout status ag/triage -n team-a
maqpna agent describe triage -n team-a

rollout status waits until the operator has observed the Agent's current generation and reports it Ready. agent describe shows the trust tier, the policies that apply, sovereignty compliance, sessions by phase and the budget. The same condition works in scripts: maqpna wait --for condition=Ready ag/triage -n team-a --timeout 2m.

Start a session and follow it:

maqpna session start --agent triage -n team-a --user [email protected] \
  --task "Triage issue acme/api#4821" --wait
maqpna session describe <session-name> -n team-a
maqpna logs <session-name> -n team-a -f

If the agent is not Ready, maqpna describe ag triage -n team-a lists its conditions (TierFound, PolicyFound, SovereigntyCompliant, ModelCredentialsReady, ...) and events.

Step 5: Promote from dev to staging to production#

Keep one set of manifests and change only what differs per environment: namespace, image digest, model endpoint, budget. Kustomize overlays are one way:

deploy/
  base/              # agent.yaml, mcpserver.yaml, triage-policy.yaml, kustomization.yaml
  overlays/
    dev/             # namespace team-a-dev, :latest tag allowed, small budget
    staging/         # namespace team-a-staging, image digest
    prod/            # namespace team-a, image digest, production budget and tier
# render, check and apply one environment
kubectl kustomize deploy/overlays/staging > /tmp/staging.yaml
maqpna validate -f /tmp/staging.yaml --strict
maqpna --context staging apply -f /tmp/staging.yaml
maqpna --context staging rollout status ag/triage -n team-a-staging

Gate each promotion on the checks from Test agents and policies, and on a smoke test of the target installation (maqpna smoke --gateway URL). Before you tighten a policy in production, replay it against the production ledger with maqpna replay.

Changes to an Agent apply to new sessions. Running sessions keep their sandbox; maqpna rollout restart ag/triage -n team-a restarts their pods (in-memory agent state is lost, so it asks first). Agents keep no revision history: roll back by applying the previous manifest from Git.

GitOps with Argo CD or Flux#

MAQPNA resources are ordinary custom resources, so Argo CD and Flux sync them like anything else. A few notes:

  • Install the CRDs first. The chart installs them. Put agents, policies and MCP servers in an application or Kustomization that depends on the MAQPNA installation.
  • Validate in the pull request, not in the cluster. Run maqpna validate --strict and maqpna policy test --require-tests in CI (see the CI pipeline).
  • Health. The Agent's readiness is its Ready condition. In Argo CD, a custom health check for maqpna.com/Agent can read it:

    # argocd-cm
    data:
      resource.customizations.health.maqpna.com_Agent: |
        hs = {status = "Progressing", message = "waiting for Ready"}
        if obj.status ~= nil and obj.status.conditions ~= nil then
          for _, c in ipairs(obj.status.conditions) do
            if c.type == "Ready" and c.status == "True" then hs.status = "Healthy"; hs.message = c.message end
            if c.type == "Ready" and c.status == "False" then hs.status = "Degraded"; hs.message = c.message end
          end
        end
        return hs
    
  • Revocations as code. maqpna kill ... --emit-yaml prints an AgentRevocation instead of calling the gateway, so an incident response can go through Git when needed. Revoking through the gateway is faster (about a second); see Kill switch and revocations.

  • Do not sync AgentSession objects you did not mean to run: each one starts a sandbox.

What can go wrong#

Symptom Cause Fix
Agent Ready=False, TierFound=False spec.tier names a tier that does not exist. maqpna tier list; fix spec.tier.
Agent not admitted, SovereigntyCompliant=False Registry, digest or endpoint breaks the sovereignty policy. maqpna sovereignty check -f manifests/ --policy sovereignty.yaml
Session fails with ScopeEscalation The session asks for scopes the agent does not have. Add the tool to Agent.spec.tools (or spec.scopes).
Container exits with a read-only filesystem error The image writes outside /tmp. Precompile at build time; set HOME=/tmp.
Every call no_applicable_policy The policy is missing, in another namespace, or spec.agents does not match. maqpna policy list -n team-a; maqpna agent describe triage -n team-a.
ModelCredentialsReady=False The model API key Secret is missing. Create the Secret named in spec.model.apiKeySecretRef.

Next steps#