Skip to content

Ikanos — FAQ

Welcome to the Ikanos FAQ! This guide answers common questions from developers who are learning, using, and contributing to Ikanos. For comprehensive technical details, see the Specification - Schema and Specification - Rules.


⛵ Getting Started

Q: What is Ikanos and why would I use it?

A: Ikanos is the first open-source platform for Spec-Driven Integration. Instead of writing boilerplate code to consume HTTP APIs and expose unified interfaces, you declare them in YAML. This enables: - API composability: Combine multiple APIs into a single capability - Format conversion: Convert between JSON, XML, Avro, Protobuf, CSV, TSV, PSV, HTML, Markdown, and YAML - AI-ready integration: Better context engineering for AI systems - Reduced API sprawl: Manage microservices and SaaS complexity

Use it when you need to integrate multiple APIs, standardize data formats, or expose simplified interfaces to AI agents.

Q: What skills do I need to create a capability?

A: You only need to know: - YAML syntax - the configuration language for capabilities - JSONPath - for extracting values from JSON responses - Mustache templates - for injecting parameters (optional, if using advanced features)

You don't need to write Java or other code unless you want to extend the framework itself.

Q: Is Ikanos a code generator or a runtime engine?

A: It's a runtime engine. Ikanos reads your YAML capability file at startup and immediately exposes HTTP or MCP interfaces, either through the Docker image or directly with ikanos serve. There's no compilation step - declare your capability, start the engine, and it works.

Q: Are there other tools that complement Ikanos?

A: Yes. Ikanos is part of Naftiko Fleet (Community Edition), which includes free complementary tools:

  • Crafter — Naftiko VS Code extension with inline structure and rules validation while editing Ikanos capability files
  • Warden — Backstage plugin to scaffold new capabilities and catalog them from CNCF Backstage
  • Skipper — Kubernetes operator to deploy and operate capabilities on Kubernetes

🚣 Installation & Setup

Q: How do I install Ikanos?

A: There are two ways:

  1. Docker (recommended)
    bash docker pull ghcr.io/naftiko/ikanos:{{RELEASE_TAG}} docker run -p 8081:8081 -v /path/to/capability.yaml:/app/capability.yaml ghcr.io/naftiko/ikanos:{{RELEASE_TAG}} serve /app/capability.yaml

If your capability is mounted at /app/ikanos.yaml, you can rely on the image default command and omit serve entirely.

  1. CLI tool (for configuration, validation, and local execution) Download the binary for macOS, Linux, or Windows

See the Installation guide for detailed setup instructions.

Q: How do I run a capability locally without Docker?

A: Use the CLI serve command:

ikanos serve path/to/capability.yaml

# Or, if the file is named ikanos.yaml in the current directory
ikanos serve

This starts the same runtime path as the Docker image, but directly on your machine, and stops cleanly with Ctrl-C.

Q: How do I validate my capability file before running it?

A: Use the CLI validation command:

ikanos validate path/to/capability.yaml
ikanos validate path/to/capability.yaml {{RELEASE_TAG}}  # Specify schema version

This checks your YAML against the Ikanos schema and reports any errors.

Q: Which version of the schema should I use?

A: Use the current framework schema version: {{RELEASE_TAG}}.

Set it in your YAML:

ikanos: "{{RELEASE_TAG}}"


🧭 Core Concepts

Q: What are "exposes" and "consumes"?

A: These are the two essential parts of every capability:

  • Exposes - What your capability provides to callers (REST, MCP or SKILL server)
  • Consumes - What HTTP APIs your capability uses internally

Example: A capability that consumes the Notion API and GitHub API, then exposes them as a single unified REST endpoint or MCP tool.

Q: What's the difference between REST and MCP exposure?

A:

Feature REST MCP
Protocol REpresentational State Transfer (JSON-HTTP) Model Context Protocol (JSON-RPC)
Best for General-purpose integrations, web apps AI agent-native integrations
Tool discovery Manual or via OpenAPI Automatic via MCP protocol
Configuration type: "rest" with resources/operations type: "mcp" with tools
Default transport HTTP stdio or HTTP (streamable)

Use REST for traditional REST clients, web applications, or when you want standard HTTP semantics.
Use MCP when exposing capabilities to AI agents or Claude.

Q: What is a "namespace"?

A: A namespace is a unique identifier for a consumed or exposed source, used for routing and references.

  • In consumes: namespace: github means "this is the GitHub API I'm consuming"
  • In exposes: namespace: app means "my exposed API is called app"
  • In steps: call: github.get-user routes to the consumed github namespace

Namespaces must be unique within their scope (all consumed namespaces must differ, all exposed namespaces must differ).

Q: What are "steps" and when do I use them?

A: Steps enable multi-step orchestration - calling multiple APIs in sequence and combining their results.

Simple mode (direct call):

operations:
  - method: GET
    call: github.get-user      # Call one consumed operation directly
    with:
      username: "{{github_username}}"

Orchestrated mode (multi-step):

operations:
  - name: complex-operation
    method: GET
    steps:
      - type: call
        name: step1
        call: github.list-users
      - type: call
        name: step2
        call: github.get-user
        with:
          username: $step1.result  # Use output from step1
    mappings:
      - target: output_field
        value: $.step2.userId

Use steps when your capability needs to combine data from multiple sources or perform lookups.

Q: What's the difference between "call" and "lookup" steps?

A:

  • call steps: Execute a consumed operation (HTTP request)
  • lookup steps: Search through a previous step's output for matching records

Example: Call an API to list all users, then lookup which one matches a given email:

steps:
  - type: call
    name: list-all-users
    call: hr.list-employees
  - type: lookup
    name: find-user-by-email
    index: list-all-users
    match: email
    lookupValue: "{{email_to_find}}"
    outputParameters:
      - fullName
      - department


🧱 Aggregates & Reuse (DDD-inspired)

Q: What are aggregates and why should I use them?

A: Aggregates are domain-centric building blocks inspired by Domain-Driven Design (DDD). An aggregate groups reusable flows under a namespace that represents a coherent domain concept — similar to how a DDD Aggregate Root encapsulates a cluster of related entities.

Use aggregates when: - The same domain operation (e.g., "get forecast") is exposed through multiple adapters (REST and MCP). - You want to maintain a single source of truth for flow definitions (name, description, call chain, parameters). - You want transport-neutral behavioral metadata (semantics) that auto-maps to adapter-specific features.

capability:
  aggregates:                             # alpha3 — keyed map by namespace
    forecast:
      display: Weather Forecast
      flows:                              # alpha3 — keyed map by flow name
        get-forecast:
          description: Retrieve weather forecast for a city
          semantics:
            safe: true
            idempotent: true
          call: weather-api.get-forecast
          inputParameters:                # alpha3 — keyed map
            city:
              type: string
              description: City name
          outputParameters:
            - type: object
              mapping: $.forecast

Q: How does ref work to reference an aggregate flow?

A: MCP tools and REST operations can reference an aggregate flow using ref: {namespace}.{flow-name}. The engine merges inherited fields from the flow — you only specify what's different at the adapter level.

exposes:
  - type: mcp
    tools:                                # alpha3 — keyed map; key IS the tool name
      get-forecast:
        ref: forecast.get-forecast        # inherits description, call, params
      weather-lookup:
        ref: forecast.get-forecast        # override description for MCP context
        description: Look up weather for a city (MCP-optimized)
  - type: rest
    resources:                            # alpha3 — keyed map
      forecast:
        path: /forecast

Merge rules: - Explicit fields on the tool/operation override inherited fields from the flow. - Fields not set on the tool/operation are inherited from the flow. - name and description are optional when using ref — they default to the flow's values.

Q: How do semantics map to MCP tool hints?

A: Aggregate flows can declare transport-neutral semantics (safe, idempotent, cacheable). When exposed as MCP tools, the engine automatically derives MCP tool hints:

Semantics MCP Hint Rule
safe: true readOnly: true, destructive: false Safe operations don't modify state
safe: false readOnly: false, destructive: true Unsafe operations may modify state
idempotent idempotent Passed through directly
cacheable (not mapped) No MCP equivalent
(not derived) openWorld Must be set explicitly on the MCP tool

Explicit hints on the MCP tool override derived values, so you can fine-tune behavior per-tool.


📦 Importing & Modularization

Q: Why was location renamed to from in consumes imports?

A: The binds section already uses location to mean "runtime variable source" — where to fetch secrets at runtime (e.g., file://./secrets.env, vault://app/prod). Reusing location as an import keyword on any section would create semantic ambiguity: does location mean "where to get this entry's definition" or "where to fetch this entry's runtime data"?

from is unambiguous, short, and idiomatic (inspired by ES module syntax: import x from './foo'). It reads naturally in YAML: "this entry comes from another file." The rename was a hard break (no alias) because the project is in alpha.

Q: Can I import bindings from another file?

A: Yes. The binds section supports the same from/import/as directive as consumes, exposes, and aggregates:

binds:
  - from: "./shared/secrets.binds.yml"
    import: api-secrets

  - from: "./shared/secrets.binds.yml"
    import: db-secrets
    as: database-secrets

The imported binding keeps its own location (runtime variable source) — from is consumed by the resolver at parse time and does not appear in the materialized entry.

Q: Why are standalone files leaves — can I import consumes from inside an aggregates file?

A: Not in 1.0. Every standalone file (.consumes.yml, .exposes.yml, .aggregates.yml, .binds.yml) is a leaf in the import graph. Only capability documents may import. This is the strictest possible design, chosen for three reasons:

  1. Symmetry. Letting only aggregates import consumes would re-introduce context-dependent rules the unified mechanism was built to remove.
  2. No evidence yet. A capability that uses a shared aggregates file simply writes two import directives — one for aggregates, one for consumes. That's explicit and trivial.
  3. Strictly additive to add later. Transitive imports need cycle detection, layer enforcement, and namespace merging. Carrying that in 1.0 before there is demand is YAGNI. If needed, the change is purely additive.

See the Importing Guide for the full design rationale.


🔩 Configuration & Parameters

Q: How do I inject input parameters into a consumed operation?

A: Use the with injector in your exposed operation:

Simple mode:

operations:
  - method: GET
    call: github.get-user
    with:
      username: "{{github_username}}"   # From binds
      accept: "application/json"        # Static value

Orchestrated mode (steps):

steps:
  - type: call
    name: fetch-user
    call: github.get-user
    with:
      username: "{{github_username}}"

The with object maps consumed operation parameter names to: - Variable references like {{VARIABLE_NAME}} injected from binds - Static strings or numbers - literal values

Q: How do I extract values from API responses (output parameters)?

A: Use JsonPath expressions in the value field of outputParameters:

consumes:
  - resources:
      - operations:
          - outputParameters:
              - name: userId
                type: string
                value: $.id                      # Top-level field
              - name: email
                type: string
                value: $.contact.email           # Nested field
              - name: allNames
                type: array
                value: $.users[*].name           # Array extraction

Common JsonPath patterns: - $.fieldName - access a field - $.users[0].name - access array element - $.users[*].name - extract all .name values from array - $.data.user.email - nested path

Q: What are "mappings" and when do I use them?

A: Mappings connect step outputs to exposed operation outputs in multi-step orchestrations.

steps:
  - type: call
    name: fetch-db
    call: notion.get-database
  - type: call
    name: query-db
    call: notion.query-database

mappings:
  - target: database_name      # Exposed output parameter
    value: $.fetch-db.dbName       # From first step's output
  - target: row_count
    value: $.query-db.resultCount  # From second step

outputParameters:
  database_name:
    type: string
  row_count:
    type: number

Mappings tell Ikanos how to wire step outputs to your final response.


🔄 OpenAPI Interoperability

Q: Can I bootstrap a capability from an existing OpenAPI specification?

A: Yes. Use the CLI import command:

ikanos import openapi petstore.yaml
ikanos import openapi petstore.yaml -o my-capability.yaml
This parses an OAS 3.0 or 3.1 document and generates a Ikanos capability YAML with a pre-filled consumes HTTP adapter — including authentication, resources, operations, input parameters, and output parameters.

Q: Can I export my REST adapter as an OpenAPI document?

A: Yes. Use the CLI export command:

ikanos export openapi capability.yaml
ikanos export openapi capability.yaml --spec-version 3.1 -f json
This reads a Ikanos capability and generates an OpenAPI document from its REST exposes adapter.

Q: Which OpenAPI versions are supported?

A: OAS 3.0 and 3.1 are fully supported for both import and export. OAS 3.2 support is deferred until the upstream Java libraries (swagger-parser, swagger-core) add it.

Q: What if my capability has multiple REST adapters?

A: Use the --adapter option to target a specific namespace:

ikanos export openapi capability.yaml --adapter public-api
When omitted, the first REST adapter found is exported.


🗝️ Authentication & Security

Q: How do I authenticate to external APIs?

A: Add an authentication block to your consumes section:

consumes:
  - type: http
    namespace: github
    baseUri: https://api.github.com
    authentication:
      type: bearer
      token: "{{GITHUB_TOKEN}}"      # Use token from binds

Supported authentication types: - bearer - Bearer token - basic - Username/password - apikey - Header or query parameter API key - digest - HTTP Digest authentication

Q: How do I manage secrets like API tokens?

A: Use binds to declare variables that are injected at runtime:

binds:
  - namespace: secrets
    keys:
      GITHUB_TOKEN: GITHUB_TOKEN      # Maps env var to template variable
      NOTION_TOKEN: NOTION_TOKEN

consumes:
  - namespace: github
    authentication:
      type: bearer
      token: "{{GITHUB_TOKEN}}"       # Use the injected variable
  - namespace: notion
    authentication:
      type: bearer
      token: "{{NOTION_TOKEN}}"

At runtime, provide environment variables:

docker run -e GITHUB_TOKEN=ghp_xxx -e NOTION_TOKEN=secret_xxx ...

⚠️ Security note: Use runtime injection (omit location) in production. Never commit secrets to your repository.

Q: Can I authenticate to exposed endpoints (REST/MCP)?

A: Yes, add authentication to your exposes block:

exposes:
  - type: rest
    port: 8081
    namespace: my-api
    authentication:
      type: apikey
      in: header
      name: X-Api-Key
      value: "{{api_key}}"
    resources:
      - path: /data
        description: Protected data endpoint

Supported authentication types for exposed endpoints: - apikey - API key via header or query parameter - bearer - Bearer token validation - basic - Username/password via HTTP Basic Auth

Q: Can I send complex request bodies (JSON, XML, etc.)?

A: Yes, use the body field for request bodies:

consumes:
  - resources:
      - operations:
          - method: POST
            body:
              type: json
              data:
                filter:
                  status: "active"

Body types: - json - JSON object or string - text, xml, sparql - Plain text payloads - formUrlEncoded - URL-encoded form - multipartForm - Multipart file upload


🗺️ REST API Design

Q: How do I define resource paths with parameters?

A: Use path parameters with curly braces:

exposes:
  - resources:
      - path: /users/{userId}/projects/{projectId}
        description: Get a specific project for a user
        inputParameters:
          - name: userId
            in: path
            type: string
            description: The user ID
          - name: projectId
            in: path
            type: string
            description: The project ID

Callers access it as: GET /users/123/projects/456

Q: How do I support query parameters and headers?

A: Use in field in inputParameters:

inputParameters:
  - name: filter
    in: query
    type: string
    description: Filter results
  - name: Authorization
    in: header
    type: string
    description: Auth header
  - name: X-Custom
    in: header
    type: string
    description: Custom header

Callers send: GET /endpoint?filter=value with custom headers.

Q: How do forward proxies work?

A: Use forward to pass requests through to a consumed API without transformation:

exposes:
  - resources:
      - path: /github/{path}
        description: Pass-through proxy to GitHub API
        forward:
          targetNamespace: github
          trustedHeaders:
            - Authorization
            - Accept

This forwards GET /github/repos/owner/name to GitHub's /repos/owner/name.

Trusted headers must be explicitly listed for security.


📡 MCP-Specific

Q: How do I expose a capability as an MCP tool?

A: Use type: mcp in exposes instead of type: rest:

exposes:
  - type: mcp
    address: localhost
    port: 9091
    namespace: my-mcp
    description: My MCP server
    tools:
      - name: query-database
        description: Query the database
        call: notion.query-db
        with:
          db_id: "fixed-db-id"
        outputParameters:
          - type: array
            mapping: $.results

Q: What's the difference between HTTP and Stdio MCP transports?

A:

Transport Use Case Setup
HTTP Streamable HTTP transport, integrates with existing infrastructure Specify address and port
Stdio Direct process communication, native integration with Claude Desktop No address/port needed

For Claude integration, Stdio is typically preferred. HTTP is useful for remote or containerized deployments.

Q: How do I expose MCP resources and prompts?

A: Add resources and prompts sections to your MCP server:

exposes:
  - type: mcp
    resources:
      - uri: file:///docs/guide.md
        name: User Guide
        description: API usage guide
    prompts:
      - name: analyze-code
        description: Analyze code snippet
        template: "Analyze this code:\n{{code}}"

MCP clients can then discover and use these resources dynamically.

Q: Can I create MCP tools that return static mock data?

A: Yes, starting in version 1.0.0 Alpha 2. Define outputParameters with value fields and omit call and steps. The tool serves a fixed JSON response — no consumes block is needed. Values can be static strings or Mustache templates resolved against input parameters:

exposes:
  - type: mcp
    port: 3001
    namespace: mock-tools
    description: Mock MCP server
    tools:
      - name: say-hello
        description: Returns a greeting
        inputParameters:
          - name: name
            type: string
            required: true
            description: Name to greet
        outputParameters:
          - name: message
            type: string
            value: "Hello, {{name}}!"

This mirrors the REST mock pattern (no-adapter.yml) and is useful for prototyping, demos, and contract-first development.


⚙️ Control Port & Observability

Q: What is the control port?

A: The control port is a built-in management plane (type: "control" in capability.exposes). It provides engine-provided endpoints for health checks, Prometheus metrics, distributed traces, runtime status, configuration reload, log level control, and log streaming — without writing any code.

Q: How do I enable the control port?

A: Add a control adapter to your capability's exposes array:

capability:
  exposes:
    - type: control
      port: 9090
      management:
        health: true
      observability:
        traces:
          local:
            buffer-size: 200

At most one control adapter is allowed per capability, and its port must not collide with any business adapter port.

Q: What endpoints does the control port provide?

A:

Endpoint Default Description
/health/live, /health/ready Enabled Liveness and readiness probes
/metrics Enabled Prometheus scrape endpoint (requires observability)
/traces Enabled Recent trace summaries (requires observability)
/status, /config Disabled Runtime status and loaded configuration
POST /config/reload Disabled Hot-reload the capability
POST /config/validate Disabled Dry-run validation
/logs Disabled Log level control
/logs/stream Disabled SSE log streaming

Q: How do I enable OpenTelemetry observability?

A: Add an observability block on the control adapter. Observability is enabled by default — you only need to set the fields you want to customize:

capability:
  exposes:
    - type: control
      port: 9090
      observability:
        traces:
          sampling: 1.0
          propagation: w3c
        exporters:
          otlp:
            endpoint: "{{otel_endpoint}}"

This enables distributed tracing and RED metrics (Rate, Errors, Duration) for all capability operations. Metrics are exposed in Prometheus format on the control port's /metrics endpoint.

Q: What metrics does Ikanos expose?

A: The engine emits three histogram metrics following the RED method:

  • ikanos.request.duration.seconds — end-to-end request duration by tool/operation name and status
  • ikanos.step.duration.seconds — individual orchestration step duration
  • ikanos.http.client.duration.seconds — outbound HTTP call duration by namespace, method, and status code

A counter ikanos.request.errors tracks failed requests. All metrics are available in Prometheus text format on the control port's /metrics endpoint.

Q: How do I connect Prometheus and Grafana?

A: Point Prometheus at the control port's /metrics endpoint:

# prometheus.yml
scrape_configs:
  - job_name: ikanos
    metrics_path: /metrics
    static_configs:
      - targets: ['localhost:9090']

A sample Grafana dashboard is provided in demo/shared/observability/grafana-ikanos.json.


🔭 Troubleshooting & Debugging

Q: My capability won't start. How do I debug it?

A:

  1. Validate your YAML first:

    ikanos validate capability.yaml
    

  2. Check the Docker logs: bash docker run ... ghcr.io/naftiko/ikanos:{{RELEASE_TAG}} serve /app/capability.yaml # Look for error messages in the output

Or, when running locally:

ikanos serve capability.yaml

  1. Verify your file path - if using Docker, ensure:
  2. The volume mount is correct: -v /absolute/path:/app/capability.yaml
  3. The file exists and is readable
  4. For Docker on Windows/Mac, use proper path translation

  5. Check external services - ensure:

  6. APIs you're consuming are reachable
  7. Network connectivity is available
  8. Authentication credentials are correct

Q: Requests to my exposed endpoint return errors. How do I debug?

A:

  1. Check the request format - ensure headers, parameters, and body match your definition
  2. Verify consumed API availability - test the underlying API directly
  3. Inspect JsonPath mappings - ensure your extraction paths match the API response
  4. Use Docker logs - see server-side error messages

Q: JsonPath expressions aren't extracting the data I expect. How do I fix it?

A:

  1. Test your JsonPath - use an online tool like jsonpath.com
  2. Inspect the actual response - add an operation without filtering to see raw data
  3. Understand array syntax:
  4. $.users[0] - first element
  5. $.users[*] - all elements (creates array output)
  6. $.users[*].name - all names

  7. For nested objects, trace the path step-by-step: $.data.user.profile.email

Q: My parameters aren't being passed to the consumed API. What's wrong?

A:

  1. Check parameter names match - consumed parameter names must match keys in with
  2. Verify parameter location (in: path, in: query, in: header, etc.)
  3. Check variable references - ensure {{VARIABLE_NAME}} variables are defined in binds
  4. Test without transformation - use forward to proxy the request and see if underlying API works

Q: Authentication is failing. How do I debug it?

A:

  1. Test credentials directly - verify your token/key works with the API
  2. Check token format - ensure it's a valid token (not expired, wrong format, etc.)
  3. Verify placement - is the token in the right header/query/body?
  4. Environment variables - ensure the Docker environment variable matches the key name in binds
  5. Quotes - make sure tokens with special characters are properly quoted in YAML

🚣 Contributing

Q: How do I contribute to Ikanos?

A: We welcome all contributions! Here's how:

  1. Report bugs or request features - GitHub Issues
  2. Search for existing issues first to avoid duplicates

  3. Submit code changes - GitHub Pull Requests

  4. Create a local branch
  5. Ensure your code passes all build validation
  6. Rebase on main before submitting

  7. Contribute examples - Add capability examples to the repository

  8. Document your use case in the example
  9. Include comments explaining key features

  10. Improve documentation - Fix typos, clarify docs, add examples

Q: What's the code structure and how do I set up a development environment?

A: Ikanos is a Java project using Maven. To build and develop:

# Clone the repository
git clone https://github.com/naftiko/ikanos.git
cd ikanos

# Build the project
mvn clean install

# Run tests
mvn test

# Build Docker image
docker build -t ikanos:local .

Key directories: - ikanos-engine/src/main/java/io/ikanos/ Core engine code - ikanos-spec/src/main/resources/schemas/ JSON Schema definitions - ikanos-engine/src/test/ and ikanos-cli/src/test/ Unit and integration tests - ikanos-spec/src/main/resources/schemas/examples/ Capability examples - ikanos-docs/tutorial/ Tutorial capability files

Q: What are the design guidelines for creating capabilities?

A:

  1. Keep the Ikanos Specification as a first-class citizen - refer to it often
  2. Don't expose unused input parameters - every parameter should be used in steps
  3. Don't declare consumed outputs you don't use - be precise in mappings
  4. Don't prefix variables unnecessarily - let scope provide clarity

Example:

# Good: expose only used input
inputParameters:
  - name: database_id   # Used in step below
    in: path

# Bad: expose unused input
inputParameters:
  - name: database_id
  - name: unused_param   # Never used anywhere

# Good: output only consumed outputs you map
outputParameters:
  - name: result
    value: $.step1.output   # Clearly mapped

# Bad: declare outputs you don't use
outputParameters:
  - name: unused_result
    value: $.step1.unused

Q: How do I test my capability changes?

A:

  1. Unit tests - Add tests in src/test/java
  2. Integration tests - Test against real or mock APIs
  3. Validation - Use the CLI tool: ikanos validate capability.yaml
  4. Docker testing - Build and run the Docker image with your capability

Q: Which version of Java is required?

A: Ikanos requires Java 21 or later. This is specified in the Maven configuration.


⛴️ Advanced Topics

Q: Can I use templates/variables in my capability definition?

A: Yes, use Mustache-style {{variable}} expressions:

binds:
  - namespace: env
    keys:
      API_KEY: API_KEY
      API_BASE_URL: API_BASE_URL

consumes:
  - baseUri: "{{API_BASE_URL}}"
    authentication:
      type: apikey
      key: X-API-Key
      value: "{{API_KEY}}"

Variables come from binds and are injected at runtime.

Q: Can I compose capabilities (capability calling another capability)?

A: Indirectly - by referencing the exposed URL/port as a consumed API:

# Capability B "consumes" the exposed endpoint from Capability A
consumes:
  - baseUri: http://localhost:8081   # Capability A's port
    namespace: capability-a

This way, Capability B can combine Capability A with other APIs.

Q: How do I handle errors or retries?

A: Ikanos currently doesn't have built-in retry logic in v1.0.0-alpha3. Options:

  1. At the HTTP client level - use an API gateway with retry policies
  2. In future versions - this is on the roadmap

Check the Roadmap for planned features.

Q: Can I expose the same capability on both REST and MCP?

A: Yes! Add multiple entries to exposes:

exposes:
  - type: rest
    port: 8081
    namespace: rest-api
    resources: [...]

  - type: mcp
    port: 9091
    namespace: mcp-server
    tools: [...]

consumes: [...]  # Shared between both

Both adapters consume the same sources but expose different interfaces.


💨 Performance & Deployment

Q: How scalable is Ikanos for high-load scenarios?

A: Ikanos is suitable for moderate to high loads depending on: - Your consumed APIs' performance - Ikanos's overhead is minimal - Docker/Kubernetes scaling - deploy multiple instances behind a load balancer - Orchestration complexity - simpler capabilities (forward, single calls) are faster

For production workloads: - Use Kubernetes for auto-scaling - Monitor consuming/consumed API latencies - Consider caching strategies above Ikanos

Q: How do I deploy Ikanos to production?

A:

  1. Kubernetes (recommended):

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ikanos-engine
    spec:
      replicas: 3
      template:
        spec:
          containers:
          - name: ikanos
            image: ghcr.io/naftiko/ikanos:{{RELEASE_TAG}}
            volumeMounts:
            - name: capability
              mountPath: /app/capability.yaml
              subPath: capability.yaml
            env:
            - name: GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: ikanos-secrets
                  key: github-token
    

  2. Docker Compose - for simpler setups

  3. Environment Variables - inject secrets via binds (omit location for runtime injection)

Q: Can I use Ikanos behind a reverse proxy (nginx, Envoy)?

A: Yes, absolutely. Ikanos exposes standard HTTP endpoints, so it works with any reverse proxy.

Example (nginx):

server {
    listen 80;
    location / {
        proxy_pass http://Ikanos:8081;
    }
}


📜 Specifications & Standards

Q: How does Ikanos compare to OpenAPI, AsyncAPI, or Arazzo?

A: Ikanos is complementary to these specifications and combines their strengths into a single runtime model: - Consume/expose duality - like OpenAPI's interface description, but bidirectional - Orchestration - like Arazzo's workflow sequencing - AI-driven discovery - beyond what all three cover natively - Namespace-based routing - unique to Ikanos's runtime approach

See the Spec-Driven Integration overview and the Specification - Schema for the formal model.

Q: Is the Ikanos Specification stable?

A: The current public version is {{RELEASE_TAG}}. Because this is an alpha release, minor schema adjustments can still happen before stable 1.0.0. The specification follows semantic versioning: - Major versions (1.x.x) - breaking changes - Minor versions (x.1.0) - new features, backward-compatible - Patch versions (x.x.1) - bug fixes

Check the Ikanos field in your YAML to specify the version.


📣 Community & Support

Q: Where can I ask questions or discuss ideas?

A: Join the community at: - GitHub Discussions - Ask questions and share ideas - GitHub Issues - Report bugs or request features - Pull Requests - Review and discuss code changes

Q: Are there examples I can reference?

A: Yes! Several resources:

Q: How often is Ikanos updated?

A: Check the Releases page for version history. The project follows a regular release cadence with security updates prioritized.


🚤 Common Use Cases

Q: I want to create a unified REST API that combines Notion + GitHub. How do I start?

A:

  1. Read the Tutorial - particularly steps 1-6 for foundations and steps 7-10 for advanced needs
  2. Define consumed sources - GitHub and Notion APIs with auth
  3. Design exposed resources - endpoints that combine their data
  4. Use multi-step orchestration - call both APIs and map results
  5. Test locally - use Docker to run your capability

Q: I want to expose my capability as an MCP tool for Claude. How do I do this?

A:

  1. Use type: mcp in exposes
  2. Define tools - each tool is an MCP tool your capability provides
  3. Add hints (optional) - declare behavioral hints like readOnly, destructive, idempotent, openWorld to help clients understand tool safety
  4. Use stdio transport - for native Claude Desktop integration
  5. Test with Claude - configure Claude Desktop with your MCP server
  6. Publish - share your capability spec with the community

See Tutorial — Track 1: Context Engineering for a full MCP example, then continue with Tutorial — Track 2: API Reusability for Skill and REST exposure.

Q: I want to standardize data from multiple SaaS tools. How do I use Ikanos?

A:

  1. Consume multiple SaaS APIs - define each in consumes
  2. Normalize outputs - use outputParameters to extract and structure data consistently
  3. Expose unified interface - create a single API with harmonized formats
  4. Use orchestration - combine data from multiple sources if needed

This is Ikanos's core strength for managing API sprawl.


🏝️ Additional Resources


🔔 Feedback

Did this FAQ help you? Have questions not covered here? - Add an issue - GitHub Issues - Start a discussion - GitHub Discussions - Submit a PR - Help us improve this FAQ!