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-alpha4"
  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 ikanos specification in use.

spec:
  ikanos: "1.0.0-alpha4"

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
      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"
        port: 3001
        namespace: my-api
        tags: [public]       # "public" triggers Ingress creation
        resources:
          hello:
            path: /hello
            operations:
              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
          traces:
            sampling: 1.0
            propagation: w3c
    consumes:
      - namespace: upstream
        type: http
        baseUri: "https://api.example.com"

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 — specRef + @JsonAnySetter catch-all
│   └── SpecRef                   .spec.specRef
├── CapabilityStatus.java         .status — phase, endpoint, conditions
├── CapabilityClassResource.java  Fabric8 CR type for CapabilityClass
└── CapabilityClassSpec.java      CapabilityClass spec — resources, hpa

CapabilitySpec uses @JsonAnySetter / @JsonAnyGetter as a catch-all for all ikanos spec fields (info, capability, binds, tools, aggregates, etc.). The reconciler parses the raw YAML as Map<String, Object> and extracts only the eight structural fields that have Kubernetes infrastructure implications (tier, exposes.type/port/tags, consumes.import/from, binds.location).

This makes the operator immune to ikanos schema evolution — changing resources from List to Map, adding new tool formats, restructuring aggregates — none of these require any operator change.


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 / 10 Production SLA workloads
dev 128Mi / 256Mi 100m / 250m 1 / 1 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-alpha4"
info:
  display: My Capability
  description: "Simple hello world REST capability"
  labels:
    naftiko.io/tier: standard
capability:
  exposes:
    - type: rest
      address: "0.0.0.0"
      port: 3001
      namespace: my-api
      resources:
        hello:
          path: /hello
          operations:
            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