MastertheMesh
Solo Enterprise for agentregistry · Reference
Visual reference

agentregistry CRDs — a visual map

TO
Tom O'Rourke
EMEA Field CTO · Solo.io

Every resource agentregistry exposes today — Agent, MCPServer, Skill, Prompt, Runtime, Deployment — what each one models, what the control plane does with it, and copy-paste YAML for each. Plus an honest note on what these actually are, because they're not Kubernetes CRDs in the usual sense.

agentregistry arctl · v0/apply agentgateway kagent AWS AgentCore

What agentregistry is

Solo's enterprise control plane for the agentic assets a platform team needs to govern — agents, MCP tool servers, skills, prompts — plus the runtimes and deployments that put them onto a cluster. One catalog, one set of policies, one observability surface across every runtime you support.

Catalog Central registry of approved agents, tools, skills, prompts.
Discovery Visibility into shadow AI running outside the catalog.
Deployment Lifecycle of approved assets onto supported runtimes.
Observability End-to-end traces over the agentic loop, via OpenTelemetry.
Platform RBAC Fine-grained roles wired to your IdP over OAuth/OIDC.

First enterprise release shipped May 1st 2026 as v2026.05.0 — arctl CLI, Helm chart, container images. Initial runtime support: Kubernetes with kagent and AWS AgentCore. Cross-runtime access policy via agentgateway is the next roadmap item, then approval workflows, drift detection, and remote MCP integration.

This page is the reference for the six resources the catalog is built from — what each kind models, what fields it carries, and copy-paste YAML you can arctl apply -f.

If you've written Istio or kgateway YAML, the agentregistry resources will look familiar — same apiVersion / kind / metadata / spec / status envelope, same kubectl-style apply UX. The thing to know upfront is that these are not Kubernetes CRDs.

The "CRD" in the title is a borrowed shorthand. There is no CustomResourceDefinition registered with the Kubernetes API server for these kinds — running kubectl get crd | grep ar.dev returns nothing.

What agentregistry actually does is reuse the Kubernetes resource envelope as its API contract. You write a manifest, run arctl apply -f (or POST /v0/apply), and the agentregistry server stores it in PostgreSQL. The apiVersion on every kind is ar.dev/v1alpha1 — that's agentregistry's group, not k8s.io's. From a user's point of view, you write the same YAML you would for Kubernetes; the difference is where it lives and who reconciles it.

Heads-up if you also use kagent: the names overlap, the CRDs don't. Every kind on this page is in the ar.dev/v1alpha1 group and is stored in the registry's Postgres. kagent's Agent is a separate, real Kubernetes CRD in kagent.dev/v1alpha2, and kmcp's MCPServer is a separate, real Kubernetes CRD in kmcp.dev/v1alpha1 — both registered on the cluster. Same names, different groups, different objects.

The link is one-way. When a Deployment reconciles onto a Runtime of type: Kubernetes, the runtime adapter translates the registry's Agent / MCPServer rows into kagent.dev / kmcp.dev CRDs and applies those to the cluster. Registry rows are the catalogue you author and version; the kagent / kmcp CRDs are what the cluster actually runs. This page covers the registry side only — the kagent CRD reference is its own page.

Six kinds today. Four of them are tagged content artifacts — Agent, MCPServer, Skill, Prompt — versioned by a metadata.tag that defaults to latest. Two are mutable control-plane objects — Runtime, Deployment — keyed by metadata.name only, no tag. That split is the load-bearing part of the model: artifacts are something you publish multiple immutable revisions of; control objects are something there's exactly one of, that you keep editing in place.

How the pieces fit together

CLIENTS arctl · curl · web UI · IDE RESOURCES · ar.dev/v1alpha1 stored in PostgreSQL via /v0/apply RUNTIMES · reconciled via DeploymentAdapter arctl apply · get · delete · run Web UI localhost:12121 REST · /v0 apply · import · get · resolve IDE generators Claude · Cursor · VS Code agentregistry control plane SCHEME · STORE · RECONCILER · ADAPTER DISPATCH Scheme: routes kind → typed object v1alpha1.Default knows the 6 kinds Store: Postgres rows (+ pgvector) soft-delete · tags · generations Reconciler: NOTIFY → adapter async Apply / Remove / Discover CONTENT ARTIFACTS · tagged ar.dev/v1alpha1 · identity = namespace / name / tag Agent runtime image + model + MCP refs MCPServer bundled (npm / pypi / oci / mcpb) — or remote (SSE / HTTP) Skill SKILL.md bundle from a git repo Prompt inline instruction template, versioned apply with --tag stable, --tag 1.2.0, … · defaults to "latest" CONTROL PLANE · mutable ar.dev/v1alpha1 · identity = namespace / name (no tag) Runtime where things execute · type: Local | Kubernetes | Kagent | BedrockAgentCore Deployment targetRef (Agent | MCPServer) + runtimeRef → reconcile desiredState: deployed | undeployed env, runtimeConfig — per-instance overrides name keys the row — re-applying mutates in place stored as rows stored as rows Local runtime Spec.Type: Local · docker-compose Reads Agent / MCPServer specs, writes agent-gateway.yaml and a docker-compose.yaml, runs compose up agentgateway is in the stack Kubernetes runtime Spec.Type: Kubernetes · kagent Translates targetRef into kagent Agent / kmcp MCPServer CRDs, applies them with the cluster's controller-runtime client deploys via kagent + agentgateway Hosted runtimes Spec.Type: BedrockAgentCore · GeminiAgentRuntime Enterprise-only adapters. Pushes to AWS Bedrock AgentCore / Google AI Runtime via signed cloud-native APIs pluggable adapter — enterprise build DeploymentAdapter.Apply () Adapter dispatch keys on Runtime.spec.type · OSS ships Local + Kubernetes · enterprise registers Kagent, BedrockAgentCore, GeminiAgentRuntime
content artifacts (tagged) control plane (mutable) runtime targets agentregistry server

How to read it: clients on top, six resource kinds in the middle (split by mutability — artifacts on the left have tags, control objects on the right don't), runtime targets at the bottom. The agentregistry server stores everything in Postgres; when a Deployment row is upserted the reconciler resolves its runtimeRef, looks the adapter up by Runtime.spec.type, and calls DeploymentAdapter.Apply. Local writes agent-gateway.yaml + docker-compose.yaml and runs compose up. Kubernetes translates the target into kagent / kmcp CRDs and applies them. The dashed enterprise box on the right is what additional adapters look like — same interface, different push target.

What a deploy actually does

Push a Deployment manifest and here's the path it follows. The reconciler runs everything after POST /v0/apply asynchronously — Apply returns Progressing=True immediately, and the adapter's watch loop later flips Ready=True via Store.PatchStatus when the workload converges.

arctl apply -fmulti-doc YAML
POST /v0/applyvalidate · upsert · NOTIFY
Reconcilerresolve refs · lookup adapter
Runtime adapterdocker-compose · kagent · …

The kinds, group by group

One section per family, with copy-paste YAML and field-level explainers underneath. The colour stripe matches the diagram. The four content artifact kinds first, then the two control-plane kinds.

🤖 Agent ar.dev/v1alpha1 · tagged

An Agent bundles an identity with the things it needs at runtime: a container image (source.image), the LLM it talks to (modelProvider + modelName), and references to the MCPServers it can call. The agent's code lives in the image; the agent's capabilities are everything in spec.mcpServers.

Agent · minimal definitionone image + one model + one MCP ref
Register it from the CLI
# scaffold a starter project (agent.yaml + arctl.yaml + .env)
arctl init agent summarizer \
  --framework adk --language python \
  --model-provider gemini --model-name gemini-2.5-flash

# build the container image and push it
arctl build ./summarizer --push

# register the manifest with the registry
arctl apply -f ./summarizer/agent.yaml

# list every revision you've published
arctl get agent summarizer --all-tags
apiVersion: ar.dev/v1alpha1
kind: Agent
metadata:
  name: summarizer
  tag: stable                # default: "latest"
spec:
  description: "Summarizes long documents into bullet points."
  source:
    image: ghcr.io/acme/summarizer:v1.0.0
    repository:                                # optional — link to source
      url: https://github.com/acme/summarizer
  modelProvider: gemini       # gemini | openai | anthropic | azureopenai | agentgateway
  modelName: gemini-2.5-flash
  mcpServers:
    - kind: MCPServer
      name: acme/fetch
      tag: stable             # pin — omit for "latest at deploy time"

What each field controls

metadata.tag
The version label for this revision of the agent. The Agent / MCPServer / Skill / Prompt kinds are tagged artifacts — re-applying the same name with a different tag produces a new row, not a mutation. arctl get agent summarizer --all-tags lists every tag you've published. Omit the field and it lands as latest.
source.image
The OCI image the runtime will pull. This is the agent's executable container — the kagent ADK runtime image, your own Go binary in a Distroless base, etc. The registry doesn't validate that the image runs; it validates the reference and stores it.
source.repository
Optional pointer at the source-code origin. Useful for arctl pull agent summarizer (which re-fetches the source so a teammate can build it locally) and for audit — "where did this image come from?". Not used at runtime.
modelProvider + modelName
What model the agent talks to. modelProvider is the SDK-level provider name; modelName is the model ID. Set modelProvider: agentgateway to route LLM calls through your Solo agentgateway instead of straight to the provider — that's where you get prompt caching, token rate-limiting and cross-provider failover.
mcpServers[]
Pure ResourceRefs — kind + name (+ optional tag) — pointing at other rows already in the registry. No inline config; if you need to override an env var per deployment that goes on the Deployment, not here. Per the v1alpha1 audit, Skills and Prompts used to be referenceable too but were removed for being ADK-Python-specific.

How to publish one: arctl init agent summarizer --framework adk --language python --model-provider gemini --model-name gemini-2.5-flash scaffolds a project; arctl build summarizer/ --push builds + pushes the image; arctl apply -f summarizer/agent.yaml registers it. The full-stack.yaml example shows the agent + every dependency in one file.

🔌 MCPServer ar.dev/v1alpha1 · tagged

An MCPServer describes a tool surface — usually one or more MCP tools the agent can call. Two flavours: a bundled server (the registry pulls/runs it from a package — npm, pypi, oci, mcpb) and a remote server (the registry doesn't run it, it just references an already-running endpoint, e.g. a SaaS MCP).

MCPServer · bundled (OCI image) · stdio transportthe registry runs the container
Register it from the CLI
# scaffold a starter MCP project (mcp.yaml + arctl.yaml + .env)
arctl init mcp acme/fetch --framework fastmcp --language python

# build the container image and push it
arctl build ./fetch --push

# register the manifest with the registry
arctl apply -f ./fetch/mcp.yaml

# inspect what was stored
arctl get mcp acme/fetch -o yaml
apiVersion: ar.dev/v1alpha1
kind: MCPServer
metadata:
  name: acme/fetch              # namespace/name is conventional for MCP names
  tag: stable
spec:
  title: Fetch Server
  description: "Fetches and extracts content from URLs."
  source:
    package:
      registryType: oci         # oci | npm | pypi | mcpb | nuget
      identifier: ghcr.io/acme/fetch:1.0.0
      version: "1.0.0"          # optional — derived from identifier if blank
      runtimeHint: docker       # hint for the local runtime
      transport:
        type: stdio             # stdio | sse | streamable-http
      environmentVariables:
        - name: FETCH_TIMEOUT_MS
          value: "8000"
        - name: FETCH_API_KEY
          isSecret: true
          isRequired: true

What gets validated, what gets stored

registryType
Where the package lives. Each value has its own validator — oci requires the image to carry a LABEL io.modelcontextprotocol.server.name ownership annotation if you're publishing to the public MCP catalogue; private deployments can turn that check off. npm calls the npm registry, pypi calls PyPI, mcpb looks for an MCP bundle archive.
transport.type
stdio for containers and local binaries (most common — kagent's MCP runtime image talks stdio), sse for HTTP Server-Sent-Events MCP servers, streamable-http for the newer chunked-HTTP MCP transport. agentgateway speaks all three and bridges between them, which is the point of going through it.
environmentVariables[]
The env vars the runtime injects when starting the container. isSecret: true means the registry surfaces it as a secret in the UI; isRequired: true means the deploy fails if no value is supplied. Values can be set per-Deployment via spec.env — see the Deployment section.

For npm / pypi packages, identifier is the package name and version is the version — at deploy time the runtime hint decides whether the local runtime invokes npx or uvx. runtimeArguments and packageArguments (omitted above) let you wire positional / named arguments through to the package's entry point — the schema is the same as the upstream MCP registry's.

MCPServer · remote (pre-deployed SaaS / cloud endpoint)registry doesn't run anything — just references
Register it from the CLI
# remote MCPServers are hand-authored — there's no init template
# (no container to build; the URL is the artifact).
# Write the YAML, then:
arctl apply -f databricks-unity-catalog.yaml

# confirm it's stored
arctl get mcp com.databricks.cloud.workspace/unity-catalog -o yaml
apiVersion: ar.dev/v1alpha1
kind: MCPServer
metadata:
  name: com.databricks.cloud.workspace/unity-catalog
  tag: stable
spec:
  title: Databricks Unity Catalog
  description: "Pre-deployed Unity Catalog MCP server hosted by Databricks."
  remote:
    type: streamable-http        # sse | streamable-http
    url: https://workspace.cloud.databricks.com/mcp
    headers:
      - name: Authorization
        value: "Bearer {{ .Secrets.databricks_token }}"

Use this when the MCP server already exists

spec.remote (not spec.source)
Switches the kind from "registry deploys this" to "registry catalogs this and routes through agentgateway". There is no DeploymentAdapter.Apply step for remote MCPServers — the workload is somebody else's problem. What you get from the registry is governance: a name, a tag, audit trail, and the gateway config that lets approved agents reach the URL.
headers[]
HTTP headers the gateway should set on the upstream call. Templated values ({{ .Secrets.* }}) resolve through agentregistry's secret store at deploy time — never put raw bearer tokens in the YAML.

A bundled MCPServer carries spec.source; a remote MCPServer carries spec.remote. The validator rejects manifests that set both. Agents reference both kinds with the same mcpServers: [{kind: MCPServer, name: …}] ref — they don't need to know which kind it is.

📚 Skill & Prompt ar.dev/v1alpha1 · tagged

Both kinds carry knowledge rather than runnable code. A Skill is a SKILL.md bundle (markdown + code examples + reference docs) that an agent loads to extend what it knows how to do. A Prompt is a reusable instruction template you can version and share across agents. Neither one has a runtime — they just exist to be referenced.

Skill · package agent knowledge from a git repoSKILL.md + supporting files
Register it from the CLI
# scaffold a starter SKILL.md bundle + skill.yaml envelope
arctl init skill summarize

# commit + push the skill folder to a git repo, then fill
# spec.source.repository in skill.yaml — and register it:
arctl apply -f skill.yaml

# fetch a previously-published skill back to disk for inspection
arctl pull skill summarize
apiVersion: ar.dev/v1alpha1
kind: Skill
metadata:
  name: summarize
  tag: stable
spec:
  title: Summarization
  description: "How to write concise, accurate summaries with bullet points."
  source:
    repository:
      url: https://github.com/acme/agent-skills
      branch: main
      subfolder: skills/summarize          # SKILL.md + assets live here
      commit: e1f2a3b…                     # optional — pin to a commit SHA

What's in a skill, and how an agent uses it

source.repository
The skill content is in git, not in the YAML. subfolder is the path inside the repo; the runtime fetches the folder, looks for SKILL.md, and surfaces the bundle to the agent. commit pins to a specific SHA — without it the runtime resolves the branch at deploy time, which is fine for dev and dangerous for prod.
How the agent loads it
The Agent kind doesn't have a skills field today — that ref was removed in the v1alpha1 audit because it was ADK-Python-specific. What you do instead: build a Skill into the agent's container image at arctl build time, or have the agent's runtime pull the skill on startup using a name baked into its config. The registry is the source of truth for "this skill exists at this version"; the runtime decides when to load it.

arctl init skill summarize scaffolds a starter SKILL.md; arctl pull skill summarize fetches the skill folder back to disk for inspection. The same tag-based identity as Agent/MCPServer applies — --tag stable, --tag 1.2.0, --all-tags.

Prompt · reusable system / instruction templateinline content, versioned
Register it from the CLI
# scaffold a prompt.yaml with an empty spec.content block
arctl init prompt summarizer-system-prompt

# fill in spec.content, then register
arctl apply -f prompt.yaml

# publish a second revision under a different tag
arctl apply -f prompt.yaml --tag experiment-shorter
apiVersion: ar.dev/v1alpha1
kind: Prompt
metadata:
  name: summarizer-system-prompt
  tag: stable
spec:
  description: "Default system prompt for summarization agents."
  content: |
    You are a document summarization assistant. Given a document,
    produce a concise summary that captures the key points.

    Guidelines:
    - Keep summaries under 200 words
    - Preserve factual accuracy
    - Use bullet points for lists of 3+ items
    - Highlight any action items or deadlines

When to use it

spec.content
The prompt body is stored inline — that's why Prompt doesn't have a source. For tiny prompts (one paragraph, a few rules) this is the right shape. For prompts with bundled examples, code snippets and reference docs, use a Skill instead so it lives in git.
Versioning + reuse
Prompts are tagged the same way artifacts are, so you can publish summarizer-system-prompt:stable and summarizer-system-prompt:experiment-shorter side by side, and have different agents pin to different revisions. arctl get prompt summarizer-system-prompt --all-tags lists every revision.

Same as Skill — there is no prompts: ref on the Agent kind today. The agent's runtime is responsible for fetching summarizer-system-prompt:stable by name when it starts. Prompts are still useful even without a ref because they're discoverable from the catalogue, which is the actual point of the registry — a prompt review board can find and approve them.

🛠️ Runtime ar.dev/v1alpha1 · mutable

A Runtime is a connection handle to one execution target — "the local docker daemon", "the cluster at api.kagent-dev.internal", "the AWS account where Bedrock AgentCore lives". spec.type picks which DeploymentAdapter reconciles it; spec.config is the adapter-specific config map. OSS ships Local and Kubernetes; enterprise adds Kagent, BedrockAgentCore and GeminiAgentRuntime.

Runtime · Local · docker-compose on this machineOSS · default for arctl run
Register it from the CLI
# Runtimes are control-plane objects — no init scaffolding,
# you author the YAML and apply it.
arctl apply -f local-runtime.yaml

# list registered runtimes
arctl get runtime
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
  name: local
spec:
  type: Local
  # No config — Local picks up the daemon's docker socket.
  telemetryEndpoint: http://otel-collector:4318

What "Local" actually does

spec.type: Local
Dispatches to the local-runtime adapter. On Apply, the adapter renders an agent-gateway.yaml + docker-compose.yaml in the agentregistry runtime directory, then runs docker compose up. Both files together describe the agentgateway service, every deployed MCP backend, and every deployed agent.
No spec.config
Local is the simplest type — it reads the daemon directory it was constructed with, not any field in the manifest. The validator still requires spec.type; nothing else.
telemetryEndpoint
Exported as OTEL_EXPORTER_OTLP_ENDPOINT into every workload this Runtime serves. Telemetry is a property of where things run, not of the individual Deployment — every container started by this Runtime gets the same endpoint, so traces show one consistent picture.
Runtime · Kubernetes · kagent-equipped clusterOSS · in-cluster or external
Register it from the CLI
# author the YAML — spec.config.kubeconfig if registering a remote
# cluster, or spec.config.inCluster: true for in-cluster pods
arctl apply -f cluster-prod-runtime.yaml

arctl get runtime cluster-prod -o yaml
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
  name: cluster-prod
spec:
  type: Kubernetes
  config:
    # All controller-runtime client knobs:
    inCluster: true                      # use the pod's service account
    namespace: kagent                    # default namespace for kagent CRDs
    # kubeconfig: |                      # alternative: explicit kubeconfig
    #   apiVersion: v1
    #   ...
  telemetryEndpoint: http://otel-collector.observability:4318

What "Kubernetes" actually does

spec.type: Kubernetes
Dispatches to the Kubernetes-runtime adapter. On Apply, the adapter builds a controller-runtime client from spec.config, translates the Deployment's Agent / MCPServer target into the corresponding kagent Agent CRD or kmcp MCPServer CRD, and applies them. Removal is by label selector — every kagent resource owned by this Deployment carries a deploymentID label so a single delete-by-label sweep cleans up.
config.inCluster vs config.kubeconfig
inCluster: true means the agentregistry pod uses its own service account — fine when agentregistry runs in the cluster it's deploying to. kubeconfig embeds the full kubeconfig as a string and lets agentregistry deploy into a different cluster, which is the multi-cluster operator pattern.
Note on "Kagent" type
The enterprise build also registers a separate Kagent runtime type that talks to the kagent controller directly via its REST API (kagentUrl: http://kagent-controller.kagent:8083) instead of writing CRDs. Same effect — different mechanism — and it's how Solo Enterprise for kagent's UI sees agents the registry has registered.

Adapter dispatch is exact-match on the canonical CamelCase value (Local, Kubernetes, Kagent, BedrockAgentCore, GeminiAgentRuntime). You can write any casing in YAML; Runtime.Validate() canonicalises in place.

Runtime · BedrockAgentCore · AWS Bedrock runtimeenterprise-only
Register it from the CLI
# enterprise-only adapter — the OSS registry will store the YAML
# but the reconciler rejects Deployments with "unknown runtime type"
# until the enterprise BedrockAgentCore adapter is registered.
arctl apply -f aws-bedrock-runtime.yaml
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
  name: aws-bedrock-prod
spec:
  type: BedrockAgentCore
  config:
    region: us-east-1
    # IAM role agentregistry assumes to create / invoke AgentCore runtimes.
    # SigV4 calls to bedrock-agentcore.us-east-1.amazonaws.com are signed
    # with credentials minted from this role.
    iamRoleArn: arn:aws:iam::253915036081:role/agentregistry-deployer
    # Optional default execution role passed to AgentCore on create.
    executionRoleArn: arn:aws:iam::253915036081:role/AgentCoreRuntimeRole
  telemetryEndpoint: https://otel-gateway.acme.com:4318

What the BedrockAgentCore adapter does on Apply

spec.type: BedrockAgentCore
Enterprise-only. On Apply, the adapter assumes iamRoleArn via STS, builds a CreateAgentRuntime request body from the Deployment's Agent target, SigV4-signs the HTTPS call (service = bedrock-agentcore, region = the configured region), and POSTs to AWS Bedrock AgentCore. AWS returns a runtime ARN like arn:aws:bedrock-agentcore:us-east-1:253915036081:runtime/summarizer-9f — the adapter stamps that into Deployment.status and into the deployment's runtime annotations.
Why this isn't OSS
The Bedrock adapter and the equivalent GeminiAgentRuntime adapter are part of the governance story Solo Enterprise for agentregistry sells — they tie cloud-hosted runtimes to the same audited catalogue the in-cluster ones live in. The OSS registry is happy to store a Runtime with type: BedrockAgentCore, but without the enterprise adapter registered the reconciler will reject the Deployment with "unknown runtime type".

SigV4 in one line: AWS rejects any unsigned API call. SigV4 means the request carries an Authorization: AWS4-HMAC-SHA256 … header whose signature is an HMAC-SHA256 over (request method + URI + sorted canonical headers + payload hash), keyed by a chain derived from your AWS secret + date + region + service. The Bedrock adapter does this signing in-process every Apply call.

🚀 Deployment ar.dev/v1alpha1 · mutable

A Deployment is the join: which artifact, on which runtime, with which per-instance overrides. Identity is namespace + name only — re-applying the same name mutates the existing row, with status conditions tracking convergence. Multiple Deployments of the same Agent on the same Runtime are allowed as long as they have distinct names — that's how you do canary / dev-vs-prod splits without forking the Agent.

Deployment · deploy the summarizer agent on KubernetestargetRef + runtimeRef + overrides
Register it from the CLI
# Deployments are control-plane objects — no init scaffolding.
# Author the YAML pointing at an existing Agent + Runtime, then:
arctl apply -f summarizer-prod-deployment.yaml

# observe convergence (Progressing → Ready or Degraded)
arctl get deployment summarizer-prod -o yaml
arctl watch deployment summarizer-prod

# tear it down (sets desiredState: undeployed and removes the row)
arctl delete deployment summarizer-prod
apiVersion: ar.dev/v1alpha1
kind: Deployment
metadata:
  name: summarizer-prod
spec:
  targetRef:
    kind: Agent                  # must be Agent or MCPServer
    name: summarizer
    tag: stable                  # pin a revision — omit for "latest at apply time"
  runtimeRef:
    kind: Runtime
    name: cluster-prod
  desiredState: deployed         # deployed (default) | undeployed
  env:                           # per-instance env overrides
    LOG_LEVEL: info
    OPENAI_API_KEY: "{{ .Secrets.openai_prod }}"
  runtimeConfig:                 # adapter-specific extras
    replicas: 3
    requests:
      cpu: "500m"
      memory: "512Mi"

What each field controls

targetRef + runtimeRef
Both required. The Reconciler resolves them at Apply time: targetRef must point to an existing Agent or MCPServer (other kinds are rejected by the per-adapter SupportedTargetKinds); runtimeRef must point to an existing Runtime whose spec.type has a registered adapter. Both refs are checked structurally before anything else happens.
desiredState
Lifecycle intent. deployed (or empty) calls DeploymentAdapter.Apply — go ensure the workload runs. undeployed routes straight to Remove — tear down external state, leave the row. Soft-delete (a DELETE on the row) also fires Remove on the way out. Both paths are idempotent.
env
Per-instance environment variables, layered on top of whatever the Agent / MCPServer manifest declares. This is the right place for environment-specific secrets — {{ .Secrets.* }} templating resolves through the secret store at deploy time so the manifest itself stays cleanable.
runtimeConfig
Free-form, adapter-specific. The Kubernetes adapter reads replicas and resource requests; the Local adapter ignores them. Putting something here that the target adapter doesn't understand is silently dropped — there's no schema validation on this map.

Status conditions: Apply returns Progressing=True immediately. The adapter's watch loop later flips Ready=True when the workload converges. Permanent failures surface as Degraded=True with a reason; transient ones bubble back as a returned error and the reconciler retries. arctl get deployment summarizer-prod -o yaml shows the live status block.

Multi-resource apply · agent + MCP server + runtime + deployment in one filerecommended workflow
Register it from the CLI
# one file, every dependency — documents are applied in order so refs resolve
arctl apply -f stack.yaml

# override the registry endpoint (defaults to http://localhost:12121)
arctl apply -f stack.yaml --registry-url https://registry.acme.com

# preview without writing
arctl apply -f stack.yaml --dry-run

# pipe from stdin
cat stack.yaml | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: MCPServer
metadata:
  name: acme/fetch
  tag: stable
spec:
  title: Fetch Server
  source:
    package:
      registryType: oci
      identifier: ghcr.io/acme/fetch:1.0.0
      runtimeHint: docker
      transport: { type: stdio }
---
apiVersion: ar.dev/v1alpha1
kind: Agent
metadata:
  name: summarizer
  tag: stable
spec:
  source: { image: ghcr.io/acme/summarizer:v1.0.0 }
  modelProvider: gemini
  modelName: gemini-2.5-flash
  description: "Summarizes documents using Gemini."
  mcpServers:
    - kind: MCPServer
      name: acme/fetch
      tag: stable
---
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
  name: local
spec:
  type: Local
---
apiVersion: ar.dev/v1alpha1
kind: Deployment
metadata:
  name: summarizer-dev
spec:
  targetRef:  { kind: Agent,   name: summarizer, tag: stable }
  runtimeRef: { kind: Runtime, name: local }

Why one file is the right shape

Document order = apply order
The POST /v0/apply batch endpoint applies documents in order, so the MCPServer lands before the Agent that references it, the Agent lands before the Deployment that targets it, and the Runtime is created before anything tries to reconcile against it. Reverse the order and the agent apply will fail with "MCPServer/acme/fetch not found".
Per-doc results
The response is one status entry per document — created, configured, unchanged, or failed. A failure mid-batch doesn't roll back the successes; everything that succeeded before the failure stays in the database. Re-applying is idempotent, so the recovery path is "fix the broken doc and re-apply the whole file".

Run with arctl apply -f stack.yaml. --registry-url overrides the default http://localhost:12121. Idempotent — re-applying the same file produces the same set of rows, with configured / unchanged status verbs rather than created.

All six kinds, one table

The full reference. Identity is what the registry rows are keyed on. Adapter / consumer is what does something with the row at reconcile time — for content artifacts it's whichever Deployment references them; for control objects it's a DeploymentAdapter.

Kind Group apiVersion Identity What it models Adapter / consumer
Agent artifact ar.dev/v1alpha1 namespace / name / tag An agent: image + model + MCP refs. referenced by Deployment.targetRef
MCPServer artifact ar.dev/v1alpha1 namespace / name / tag An MCP tool surface — bundled (package) or remote (URL). referenced by Agent.mcpServers · Deployment.targetRef
Skill artifact ar.dev/v1alpha1 namespace / name / tag A SKILL.md knowledge bundle from git. consumed by agent runtimes at startup
Prompt artifact ar.dev/v1alpha1 namespace / name / tag A reusable, versioned instruction template (inline content). consumed by agent runtimes at startup
Runtime control ar.dev/v1alpha1 namespace / name An execution target — Local / Kubernetes / Kagent / BedrockAgentCore / GeminiAgentRuntime. DeploymentAdapter dispatch key
Deployment control ar.dev/v1alpha1 namespace / name The join: target × runtime × overrides × lifecycle intent. reconciled by the matching DeploymentAdapter

How this connects to the governance story

Governance is what these six kinds buy you. Every agent and every MCP server an application can reach has to exist as a row first — Agent, MCPServer, target — and the runtime only ever sees what the registry compiled out of those rows. If it isn't registered, it isn't callable. That's the allowlist, and it's the whole point: a single audited list of what's approved to run, with a CLI (arctl) and an API in front of it so platform teams can review and admit things before application teams pick them up.

Today the enforcement is config-time. When arctl deploy runs, the registry generates the agentgateway routing table (Local) or the kagent CRDs (Kubernetes) from exactly the rows the Deployment references. A tool whose ARN or package isn't in the registry doesn't get a route — the gateway has no way to call it, because it was never told it existed.

The enterprise roadmap moves this to request-time. The May 2026 release update lists Access Policy as the next piece — agentgateway will pull from the registry continuously and check each call against it, instead of relying on whatever snapshot was compiled at deploy. Same six kinds, same YAML. The difference is the gateway asks the registry on the hot path instead of reading a frozen routing table, so revoking access to a tool takes effect on the next call rather than the next redeploy.

For implementation depth the agentregistry-dev/agentregistry repo is the source of truth — the same six kinds, plus the adapter interface that Local, Kubernetes and the enterprise hosted runtimes plug into. Once you've got the kinds straight, the Agentic / MCP lab is the next stop for what agentgateway does with them.