Skip to content

Custom resources

Overview

The Capability Custom Resource Definition (CRD) is the core Kubernetes primitive of the Naftiko platform. It lets platform teams declare an integration capability as a Kubernetes object. The Naftiko Skipper operator watches these CRs and reconciles them into running workloads.

Capability CR (YAML spec)
 Naftiko Skipper (Java / JOSDK)
        │  creates / updates
        ├─ ConfigMap       (capability.yaml mounted at /data/capability.yaml)
        ├─ Deployment      (ikanos engine — all named ports + OTEL env vars)
        ├─ Service         (ClusterIP — one port per expose entry)
        ├─ Ingress         (only when exposes[].tags contains "public")
        └─ ServiceMonitor  (only when exposes[] contains type: control)

CRD Registration

The CRD is registered in the cluster via capabilities.naftiko.io. Once applied, the following commands become available:

kubectl get capabilities          # list all capabilities
kubectl get cap                   # short name
kubectl get cap -A                # across all namespaces
kubectl describe cap my-cap

Key CRD properties:

Property Value
Group naftiko.io
Version v1alpha3
Kind Capability
Scope Namespaced
Short name cap
Category naftiko

Resource Structure

apiVersion: naftiko.io/v1alpha3
kind: Capability
metadata:
  name: my-capability
  namespace: default
  labels:
    naftiko.io/tier: standard        # selects CapabilityClass
spec:
  # ── Option A: inline spec ──────────────────────────────
  ikanos: "1.0.0-alpha3"
  info: { ... }
  capability: { ... }
  binds: [ ... ]

  # ── Option B: specRef (recommended) ────────────────────
  specRef:
    configMap: my-capability-spec    # ConfigMap containing the ikanos YAML
    key: capability.yaml             # default key — can be omitted
status:                              # written by the operator, read-only
  phase: Running | Failed
  endpoint: http://my-capability.default.svc.cluster.local:3001
  conditions:
    - type: Ready
      status: "True"

spec Fields

ikanos (string)

Version of the Naftiko specification in use.

spec:
  ikanos: "1.0.0-alpha3"

info (object)

Capability metadata. The display and description fields are required when using an inline spec.

spec:
  info:
    display: "My Capability"
    description: "What this capability does"
    tags:
      - rest
      - internal
    labels:
      naftiko.io/tier: standard        # drives CapabilityClass selection
      naftiko.io/domain: platform
      naftiko.io/cost-center: team-x

The naftiko.io/tier label selects the CapabilityClass resource that sets pod resource requests/limits and resilience defaults. Defaults to standard if absent.

capability (object)

The technical configuration of what the capability exposes and consumes.

spec:
  capability:
    exposes:
      - type: rest           # rest | mcp | skill | control
        address: "0.0.0.0"  # always set in Kubernetes — binds to all interfaces
        port: 3001
        namespace: my-api
        tags: [public]       # "public" triggers Ingress creation
        resources: [ ... ]
      - type: control        # observability port — /metrics, /health/*, /status
        address: "0.0.0.0"
        port: 9090
        observability:
          enabled: true
          metrics:
            local:
              enabled: true
          traces:
            sampling: 1.0
            propagation: w3c
    consumes:
      - namespace: upstream
        type: http
        baseUri: "https://api.example.com"
        resources: [ ... ]

Every entry in exposes[] gets its own named port on both the Service and the Deployment container spec. The control port additionally triggers a ServiceMonitor and Prometheus pod scrape annotations.

binds (array)

Declares external secret sources for variable injection into the spec (e.g. API tokens, credentials). See Secrets & Binds for the full reference.

spec:
  binds:
    - namespace: "registry-env"
      location: "file:///./shared/secrets.yaml"
      keys:
        API_TOKEN: "my-api-token"

The operator resolves file:// binds to a Kubernetes Secret named {capability-name}-bind-{parent-directory} and mounts it into the engine pod.

Note: the Secret is named after the parent directory of the location path, not the bind namespace. Multiple namespaces sharing the same path are backed by one combined Secret. See Secrets & Binds for details.

specRef (object)

Recommended pattern. Points to a ConfigMap containing the full ikanos YAML, decoupling the spec lifecycle from the CR lifecycle.

spec:
  specRef:
    configMap: my-capability-spec    # name of the ConfigMap in the same namespace
    key: capability.yaml             # key inside the ConfigMap (default: capability.yaml)

When specRef is set, the operator reads the raw YAML from the referenced ConfigMap and writes it verbatim into the generated ConfigMap — no field is lost or set to null.


status Fields

The operator writes these fields after each reconciliation. They are read-only for users.

Field Description Example
phase High-level state Running, Failed
endpoint Internal cluster URL (first non-control port) http://my-cap.default.svc.cluster.local:3001
observedGeneration Last reconciled generation 3
conditions[Ready] Standard K8s condition True / False
kubectl get cap my-capability -o jsonpath='{.status.phase}'
kubectl get cap my-capability -o jsonpath='{.status.endpoint}'

Java Model

The CRD spec is mapped to the following Java classes in the operator:

crd/
├── CapabilityResource.java       Fabric8 CR type (@Group, @Version, @Kind)
├── CapabilitySpec.java           .spec — typed fields + @JsonAnySetter catch-all
│   ├── InfoSpec                  .spec.info
│   ├── CapabilityBlock           .spec.capability
│   ├── ExposeSpec                .spec.capability.exposes[]
│   └── SpecRef                   .spec.specRef
├── CapabilityStatus.java         .status — phase, endpoint, conditions
├── CapabilityClassResource.java  Fabric8 CR type for CapabilityClass
└── CapabilityClassSpec.java      CapabilityClass spec — resources, hpa, resilience

CapabilitySpec uses @JsonAnySetter / @JsonAnyGetter on both the root spec and ExposeSpec to preserve unknown fields (MCP tools, descriptions, prompts, aggregates, etc.) when the spec is serialised back to YAML for the engine ConfigMap. Without this, any field not explicitly declared in the Java model would be silently dropped.


CapabilityClass

Capabilities select a resource tier via info.labels["naftiko.io/tier"]. The operator fetches the matching CapabilityClass cluster resource and applies its values to the generated Deployment.

Three tiers ship by default:

Tier Memory req/limit CPU req/limit HPA min/max Use case
standard 256Mi / 512Mi 250m / 500m 1 / 4 Default — balanced
premium 512Mi / 1Gi 500m / 1000m 2 / 20 Production SLA workloads
dev 64Mi / 128Mi 50m / 100m 1 / 2 Development and staging

If no matching CapabilityClass is found, the operator falls back to the standard defaults.

CapabilityClasses also configure Resilience4j defaults — circuit breaker, retry, bulkhead, and rate limiter settings are applied per consumed namespace.


Printer Columns

kubectl get cap displays:

NAME            LABEL                 PHASE     ENDPOINT                                              AGE
my-capability   My Capability         Running   http://my-capability.default.svc.cluster.local:3001   5m

Quickstart

# 1. Save your ikanos spec
cat > my-spec.yaml << 'EOF'
ikanos: "1.0.0-alpha3"
info:
  display: My Capability
  labels:
    naftiko.io/tier: standard
capability:
  exposes:
    - type: rest
      address: "0.0.0.0"
      port: 3001
      namespace: my-api
      resources:                          # alpha3 — keyed map
        hello:
          path: /hello
          operations:                     # alpha3 — keyed map
            get-hello:
              method: GET
              outputParameters:
                - name: message
                  type: string
                  value: "Hello, World!"
    - type: control
      address: "0.0.0.0"
      port: 9090
      observability:
        enabled: true
        metrics:
          local:
            enabled: true
EOF

# 2. Upload to Kubernetes
kubectl create configmap my-spec --from-file=capability.yaml=my-spec.yaml -n default

# 3. Apply the Capability CR
cat << 'EOF' | kubectl apply -f -
apiVersion: naftiko.io/v1alpha3
kind: Capability
metadata:
  name: my-capability
  namespace: default
  labels:
    naftiko.io/tier: standard
spec:
  specRef:
    configMap: my-spec
EOF

# 4. Wait and test
kubectl wait pod -l naftiko.io/capability=my-capability \
  --for=condition=Ready --timeout=60s -n default

kubectl port-forward svc/my-capability 3001:3001 9090:9090 -n default &
sleep 2

curl http://localhost:3001/hello
curl http://localhost:9090/metrics | grep ikanos_request_total

Reconciliation Triggers

The operator reconciles a Capability CR when:

  • The CR is created or updated
  • A child resource (ConfigMap, Deployment, Service) is modified or deleted
  • The CR is annotated with reconcile-at:
kubectl annotate capability my-capability \
  reconcile-at=$(date +%s) --overwrite -n default