MAQPNADocs

Session lifecycle#

A session (kind AgentSession) is one run of an agent. The operator reconciles it into a sandbox, a session token and a NetworkPolicy, watches it run, and tears it down at the end. This page follows one session from kubectl apply to its usage report, in the order the code runs (internal/controller/agentsession_controller.go, session_governance.go, podspec.go, bootstrap.go, outcome.go, usage_report.go).

There are two ways to get a sandbox:

  • Direct mode. The trust tier has no sandboxTemplateName. The operator creates an upstream Sandbox with a pod spec it builds itself, and mounts a token it minted.
  • Claim mode (warm pool). The trust tier names a SandboxTemplate. The operator creates a SandboxClaim with no environment; agent-sandbox adopts a pre-started pod, and the pod fetches its session from the identity broker (late binding).

Tiers that require attestation (tier-2, confidential VM) follow the same path, but the operator never sees the token; see attestation-gated secrets.

Phases#

stateDiagram-v2
    [*] --> Pending: created (event Accepted)
    Pending --> Pending: QuotaExceeded<br/>(requeue 5 s)
    Pending --> Provisioning: admitted
    Pending --> Failed: AgentNotFound, TierNotFound,<br/>SovereigntyViolation, ScopeEscalation
    Provisioning --> Provisioning: InputPending, AssertionPending,<br/>WaitingForSnapshot (poll 15 s)
    Provisioning --> Running: sandbox Ready<br/>(claim mode: pod bootstrapped)
    Provisioning --> Failed: InputInvalid, PrincipalUnverified,<br/>AttestationFailed, TemplateNotBootstrapCapable
    Running --> Suspended: spec.suspend, idle, revocation suspend
    Suspended --> Running: resumed or new activity
    Running --> Completed: sandbox Finished or TTL reached
    Suspended --> Completed: TTL reached
    Pending --> Failed: revocation terminate
    Provisioning --> Failed: revocation terminate
    Running --> Failed: revocation terminate
    Completed --> [*]
    Failed --> [*]

Completed and Failed are terminal. When a session ends, status.outcome records a result: Succeeded, Failed, Expired, Revoked or Cancelled.

Sequence (direct mode)#

sequenceDiagram
    autonumber
    actor Dev as Developer or portal
    participant K as Kubernetes API
    participant Op as Operator
    participant IB as Identity broker
    participant AS as agent-sandbox
    participant Pod as Sandbox pod
    participant GW as Gateway
    Dev->>K: create AgentSession (agentRef, user, scopes, ttl)
    K-->>Op: watch event
    Op->>K: add finalizer maqpna.com/cleanup, phase Pending
    Op->>K: read Agent, TrustTier, SovereigntyPolicy default
    Op->>Op: principal, sovereignty, scopes subset, TTL clamp
    Op->>Op: revocations and concurrency quota
    Op->>IB: POST /v1/token (Bearer MAQPNA_BROKER_TOKEN)
    IB-->>Op: token, expiresAt, spiffeId
    Op->>K: Secret session-maqpna-token, input Secret
    Op->>K: Sandbox (runtimeClassName from tier)
    Op->>K: NetworkPolicy session-maqpna
    AS->>Pod: start pod in gVisor or Kata
    AS-->>K: Sandbox Ready
    Op->>K: phase Running, readyAt, readyIn
    Pod->>GW: tool and model calls with the token
    GW->>K: annotate maqpna.com/last-activity
    Note over Op,IB: token re-minted at 80% of its lifetime
    Pod-->>AS: agent exits, Sandbox Finished
    Op->>K: outcome from exit code, phase Completed, teardown
    Op->>GW: POST /v1/usage/sandbox (readyAt, finishedAt)
    GW-->>Op: 2xx, outcome.usageReport Recorded

Step by step#

  1. Accept. On first sight the operator sets phase Pending, emits the Accepted event and adds the finalizer maqpna.com/cleanup.
  2. Resolve the agent and tier. A missing Agent fails the session with AgentNotFound; a missing TrustTier with TierNotFound.
  3. Bind the principal. The operator records status.principal{user, groups, verifiedBy} once, from the installed admission mode (--principal-binding: off, requester, trustedCreators) or, with spec.userAssertionRef, by verifying the user's ID token (verifiedBy: idToken). A missing assertion Secret keeps the session Provisioning (AssertionPending); a mismatch fails it with PrincipalUnverified.
  4. Check sovereignty. The agent's image, model endpoint, tool URLs and tier jurisdiction are checked against the default SovereigntyPolicy. In enforce mode a violation fails the session (SovereigntyViolation); in audit mode it only sets a condition and a Warning event.
  5. Check scopes. spec.scopes must be a subset of the agent's effective scopes, else ScopeEscalation. models:<route> is added when the agent has a model endpoint.
  6. Clamp the TTL. ttl = min(spec.ttl or --default-session-ttl (1h), tier.maxSessionTTL, agent.maxSessionTTL); expiresAt = startedAt + ttl. A reduction sets TTLClamped.
  7. Expire if due. When now ≥ expiresAt, the sandbox, claim and Secrets are deleted, the result is Expired and the phase Completed.
  8. Govern. For an attested session still pending, the operator asks the attestation service for the release outcome (AttestationFailed on failure). It applies the strictest matching AgentRevocation: terminate fails the session (Revoked), suspend sets spec.suspend=true, block mints nothing and re-checks within 30 seconds. A session that would exceed maxConcurrentSessions stays Pending with QuotaExceeded; the oldest queued session is admitted first.
  9. Validate input. spec.input (task up to 64 KiB, or a ConfigMap reference, plus env) is checked. A missing ConfigMap means InputPending; bad input fails with InputInvalid; in claim mode a task over 4 KiB fails with TaskTooLargeForClaim.
  10. Resolve a fork. With spec.fork, the operator waits for the snapshot (WaitingForSnapshot) and checks agent, user and scopes match.
  11. Get credentials.
    • Direct, non-attested: POST {--broker-url}/v1/token and the Secret <session>-maqpna-token; re-minted at 80% of the token lifetime (default token TTL 15 minutes).
    • Claim mode: nothing is minted; TokenReady reason Bootstrap.
    • Attested: POST {--attest-url}/v1/releases with a 32-byte nonce; TokenReady reason AttestationGated.
  12. Create the sandbox. The input Secret, the workspace PVC (if any), then the Sandbox (or SandboxClaim against maqpna-<tier>), owner-referenced, with shutdownTime = expiresAt and shutdownPolicy: Delete as an upstream backstop. Then the NetworkPolicy <session>-maqpna.
  13. Set the phase. Upstream Finished=True → Completed; suspended or idle → Suspended; ready → Running (first time: readyAt, readyIn and the metric maqpna_session_ready_seconds{tier,mode}); otherwise Provisioning, polled every 15 seconds.
  14. Requeue at the earliest of expiry, token refresh, idle deadline or the provisioning poll.
  15. Idle and suspend. The gateway stamps maqpna.com/last-activity on every tool or model call (at most once a minute per session). After agent.idleTimeout (default 15 minutes) without activity, the operator suspends the sandbox (operatingMode: Suspended in v1beta1) until new activity arrives.
  16. End. The result is set once, by precedence: revocation (Revoked) > expiry (Expired) > agent-reported result (maqpna.com/result, written by the gateway when the agent calls POST /v1/sessions/self/result) > exit code of the agent container > the upstream Finished reason.
  17. Finalise. Deleting the session runs the finalizer: the result becomes Cancelled (reason Deleted) if nothing else ended it, usage is reported, the sandbox, claim, Secrets and NetworkPolicy are deleted, then the finalizer is removed.
  18. Report usage. With --usage-report-url set (the chart points it at the gateway's /v1/usage/sandbox), the operator posts {namespace, session, agent, user, tier, readyAt, finishedAt} once per session, authenticated with a projected ServiceAccount token. The gateway checks it with a TokenReview, meters the sandbox time and appends a ledger record with decision usage. Failures retry every 30 seconds; after one hour the report is marked Lost (event UsageReportFailed). See usage metering.

Claim mode: late binding#

sequenceDiagram
    autonumber
    participant Op as Operator
    participant AS as agent-sandbox
    participant Pod as Warm pod
    participant IB as Identity broker (port 8083)
    participant K as Kubernetes API
    Op->>K: status.bootstrap.env (no token)
    Op->>K: SandboxClaim (warmPoolRef maqpna-tier, no env)
    Pod->>IB: POST /v1/bootstrap?wait=20 (projected SA token, aud maqpna-bootstrap)
    AS->>K: adopt warm Sandbox for the claim, label pod claim-uid
    IB->>K: TokenReview, pod, Sandbox, SandboxClaim, AgentSession
    IB->>K: patch status.bootstrap.pod, podUID, bootstrappedAt
    IB-->>Pod: env, token, expiresAt, spiffeId
    Op->>K: phase Running once bootstrap.podUID is set
  1. The operator writes the session environment to status.bootstrap.env and creates a claim without spec.env, because agent-sandbox only adopts warm pods for claims without one.
  2. The warm pod (its SDK, or maqpna-attest-agent --mode=bootstrap on attested tiers) long-polls the broker's bootstrap port with its projected ServiceAccount token.
  3. After adoption the broker follows pod → Sandbox → SandboxClaim → AgentSession, binds the first pod (409 already_bootstrapped for any other) and returns the environment and a fresh token. Attested sessions get the release ID and nonce instead of a token.
  4. The session is Running once bootstrapped; readyAt is the bootstrap time. Repeated bootstrap calls from the bound pod refresh the token.

What you see#

kubectl get agentsessions shows the result and start latency as extra columns (OUTCOME, READY-IN), and maqpna session list prints:

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

Values are illustrative. Events on the session follow the steps: Accepted, TokenMinted, SandboxCreated, Running, TokenRefreshed, Suspending, Resumed, Expired, Completed, and the warnings ScopeEscalation, SovereigntyViolation, Revoked, QuotaExceeded. Read them with maqpna events or maqpna session describe.

Failure modes#

Situation Behaviour
Identity broker unreachable MintFailed event; nothing is created; the reconcile retries
Operator down Running sessions keep running; tokens are not re-minted, so they expire at their TTL
Attestation fails The release is burned; the session fails with AttestationFailed and its sandbox is deleted
Usage report endpoint down Retries every 30 s for one hour, then outcome.usageReport: Lost
Warm pool empty The claim waits for a sandbox; alert MaqpnaWarmPoolEmpty