-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
Summary
Define and implement the AgentCapability Custom Resource Definition (CRD) - a Kubernetes-native way to represent services that can be registered on-chain via ERC-8004.
Background
The orchestrator needs a Kubernetes-native mechanism to discover and track services deployed in the stack. Following established patterns (ServiceMonitor for Prometheus, Certificate for cert-manager), we'll use a CRD that:
- Networks emit when deployed (e.g., ethereum creates an RPC capability)
- The orchestrator controller watches and reconciles
- Drives ERC-8004 registration and HTTPRoute generation
See Orchestrator PRD for full context.
Requirements
Must Have
- Define
AgentCapabilityCRD schema (v1alpha1) - Deploy CRD to cluster during
obol stack up - Support capability types:
rpc,inference,fine-tune,validator - Include pricing specification (model, amount, asset, network)
- Include endpoint specification (service, port, path)
- Include
publishflag to control visibility - Status subresource for registration state
Should Have
- Validation webhooks for enum fields
- Default values for common fields
- Short name (
acap) for kubectl
Nice to Have
- Printer columns for
kubectl get agentcapabilities - OpenAPI schema documentation
CRD Specification
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: agentcapabilities.obol.network
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
spec:
group: obol.network
names:
plural: agentcapabilities
singular: agentcapability
kind: AgentCapability
shortNames:
- acap
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: true
subresources:
status: {}
additionalPrinterColumns:
- name: Type
type: string
jsonPath: .spec.type
- name: Published
type: boolean
jsonPath: .spec.publish
- name: Registered
type: boolean
jsonPath: .status.registered
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
schema:
openAPIV3Schema:
type: object
required:
- spec
properties:
spec:
type: object
required:
- type
- endpoint
properties:
type:
type: string
description: "Type of capability being offered"
enum:
- rpc
- inference
- fine-tune
- validator
protocol:
type: string
description: "Protocol identifier (e.g., ethereum-json-rpc, openai-chat)"
chain:
type: object
description: "Blockchain network details (for rpc type)"
properties:
id:
type: integer
description: "Chain ID (e.g., 1 for mainnet)"
name:
type: string
description: "Human-readable network name"
pricing:
type: object
description: "Payment requirements for this capability"
properties:
model:
type: string
description: "Pricing model"
enum:
- per-request
- per-token
- per-epoch
- per-hour
- free
default: per-request
amount:
type: string
description: "Price amount in asset units (e.g., '0.0001')"
default: "0"
asset:
type: string
description: "Payment asset symbol"
default: "USDC"
network:
type: string
description: "Payment network (e.g., base, base-sepolia)"
default: "base-sepolia"
endpoint:
type: object
required:
- service
- port
description: "Kubernetes service endpoint"
properties:
service:
type: string
description: "Kubernetes service name"
port:
type: integer
description: "Service port number"
path:
type: string
description: "URL path prefix"
default: "/"
publish:
type: boolean
description: "Whether to publish this capability to ERC-8004"
default: true
status:
type: object
properties:
registered:
type: boolean
description: "Whether capability is registered on-chain"
agentId:
type: integer
description: "ERC-8004 agent NFT ID"
registryTxHash:
type: string
description: "Transaction hash of registration"
tunnelPath:
type: string
description: "Public URL path via cloudflared"
httpRouteName:
type: string
description: "Generated HTTPRoute resource name"
lastSync:
type: string
format: date-time
description: "Last successful reconciliation"
conditions:
type: array
items:
type: object
properties:
type:
type: string
status:
type: string
lastTransitionTime:
type: string
format: date-time
reason:
type: string
message:
type: stringExample Resources
Ethereum RPC Capability
apiVersion: obol.network/v1alpha1
kind: AgentCapability
metadata:
name: execution-rpc
namespace: ethereum-knowing-wahoo
spec:
type: rpc
protocol: ethereum-json-rpc
chain:
id: 1
name: mainnet
pricing:
model: per-request
amount: "0.0001"
asset: USDC
network: base-sepolia
endpoint:
service: ethereum-execution-reth-mainnet
port: 8545
path: /
publish: trueInference Capability
apiVersion: obol.network/v1alpha1
kind: AgentCapability
metadata:
name: llama-inference
namespace: unsloth
spec:
type: inference
protocol: openai-chat
pricing:
model: per-token
amount: "0.00001"
asset: USDC
network: base-sepolia
endpoint:
service: unsloth-api
port: 8000
path: /v1/chat/completions
publish: trueFine-tuning Capability
apiVersion: obol.network/v1alpha1
kind: AgentCapability
metadata:
name: unsloth-finetune
namespace: unsloth
spec:
type: fine-tune
protocol: unsloth-v1
pricing:
model: per-epoch
amount: "1.00"
asset: USDC
network: base-sepolia
endpoint:
service: unsloth-api
port: 8000
path: /v1/fine-tune
publish: trueImplementation
Option A: Static YAML (MVP)
Add CRD manifest to internal/embed/infrastructure/:
internal/embed/infrastructure/
├── helmfile.yaml
├── crds/
│ └── agentcapability-crd.yaml # NEW
└── ...
Deploy via raw chart in helmfile:
- name: obol-crds
namespace: kube-system
chart: bedag/raw
values:
- resources:
- {{ readFile "crds/agentcapability-crd.yaml" | nindent 10 }}Option B: Controller-gen (Production)
Use kubebuilder/controller-gen for type-safe Go types:
// internal/api/v1alpha1/agentcapability_types.go
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Published",type=boolean,JSONPath=`.spec.publish`
// +kubebuilder:printcolumn:name="Registered",type=boolean,JSONPath=`.status.registered`
type AgentCapability struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec AgentCapabilitySpec `json:"spec,omitempty"`
Status AgentCapabilityStatus `json:"status,omitempty"`
}
type AgentCapabilitySpec struct {
Type CapabilityType `json:"type"`
Protocol string `json:"protocol,omitempty"`
Chain *ChainSpec `json:"chain,omitempty"`
Pricing *PricingSpec `json:"pricing,omitempty"`
Endpoint EndpointSpec `json:"endpoint"`
Publish bool `json:"publish,omitempty"`
}
// +kubebuilder:validation:Enum=rpc;inference;fine-tune;validator
type CapabilityType string
// ... rest of typesRecommendation: Start with Option A for MVP, migrate to Option B when building the controller.
Files to Create/Modify
internal/embed/infrastructure/crds/agentcapability-crd.yaml- CRD manifestinternal/embed/infrastructure/helmfile.yaml- Add CRD deploymentinternal/embed/networks/ethereum/helmfile.yaml.gotmpl- Emit AgentCapability on installinternal/embed/networks/helios/helmfile.yaml.gotmpl- Emit AgentCapability on installinternal/embed/networks/aztec/helmfile.yaml.gotmpl- Emit AgentCapability on install
Testing
# Deploy stack with CRD
obol stack up
# Verify CRD registered
obol kubectl get crd agentcapabilities.obol.network
# Install network (should create AgentCapability)
obol network install ethereum --network=mainnet
# List capabilities
obol kubectl get agentcapabilities -A
# or
obol kubectl get acap -A
# Describe capability
obol kubectl describe acap execution-rpc -n ethereum-<id>Dependencies
- Depends on: None (can be done in parallel with Traefik migration)
- Blocks: Orchestrator controller implementation
Related
- Replace nginx-ingress with Traefik + Gateway API #125 - Replace nginx-ingress with Traefik + Gateway API
- Orchestrator PRD
- Kubernetes CRD Documentation