Skip to content

Ikanos — Guide - Linting

Table of Contents


Overview

Ikanos capabilities are declared in YAML. Two complementary validation layers ensure your capability documents are correct and follow best practices — and both are bundled into a single deterministic linting engine: Polychro.

Layer Polychro validator What it checks
JSON Schema polychro-json-schema Structural validity — required fields, types, allowed values, enum constraints
Ruleset polychro-ruleset (Spectral format) Cross-object consistency, style hygiene, security (XSS/injection)
VS Code Extension Naftiko Crafter Both layers — inline validation as you edit Ikanos capability YAML files (no CLI needed)

Polychro runs both layers in a single pass, with one configuration file, one binary, and unified diagnostics. No Node.js, no Docker, no MegaLinter orchestration required.

JSON Schema catches "is this valid YAML?", the ruleset catches "is this good YAML?"

For a full reference of all Ikanos rules, see the Ruleset page.


Quick Start — Polychro CLI

If you just want to lint a capability file right now:

# Install the Polychro CLI (download the native binary for your OS from
# https://github.com/naftiko/polychro/releases — no JVM required)

# Lint a single file against the Ikanos ruleset
polychro lint my-capability.yml \
  --ruleset https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml \
  --schema https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/schemas/ikanos-schema.json

If you have cloned the Ikanos repository locally:

polychro lint my-capability.yml \
  --ruleset path/to/ikanos/ikanos-spec/src/main/resources/rules/ikanos-rules.yml \
  --schema path/to/ikanos/ikanos-spec/src/main/resources/schemas/ikanos-schema.json

The Ikanos ruleset is written in Spectral format and Polychro executes it natively — no Spectral CLI installation needed.


Polychro Setup

Polychro is a single native binary that runs the JSON Schema validator and the Spectral-format ruleset engine in one pass. It integrates with GitHub Actions, GitLab CI, Azure Pipelines, or any other runner — no Docker required.

Prerequisites

  • The Polychro native binary for Linux, macOS, or Windows — no JVM, no Node.js, no Docker.
  • (Optional) GraalVM 21+ only if you author custom rule functions in JavaScript, Python, or Groovy.

Configuration Files

Create one file at the root of your project:

.polychro.yml

A single configuration file tells Polychro which schema and ruleset to apply to your capabilities. If the Ikanos repository is a sibling directory or submodule:

# .polychro.yml
validators:
  json-schema:
    path: ./path/to/ikanos/ikanos-spec/src/main/resources/schemas/ikanos-schema.json
  ruleset:
    path: ./path/to/ikanos/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

Or reference the schema and ruleset directly from GitHub:

# .polychro.yml
validators:
  json-schema:
    path: https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/schemas/ikanos-schema.json
  ruleset:
    path: https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

One file, both layers. Polychro auto-detects the document format and runs polychro-json-schema and polychro-ruleset in the same pipeline. Unlike Spectral or v8r, Polychro will pick up # yaml-language-server: $schema=... headers when present — the explicit path above is only needed when no header is set or you want to pin to a specific version.

Adapt the file selection to match the directory where you store your capability YAML files. By default Polychro lints any file(s) you pass on the command line — see the next section.

Run Locally

Using the Polychro CLI

# Lint every capability under capabilities/
polychro lint capabilities/*.yml

# Use a custom config (defaults to .polychro.yml if present)
polychro lint --config .polychro.yml capabilities/*.yml

# Output for AI agents (compact, machine-readable)
polychro lint --format agent capabilities/*.yml

# SARIF output for GitHub Code Scanning
polychro lint --format sarif capabilities/*.yml > polychro-results.sarif

Polychro's exit codes are CI-friendly:

  • 0 — no issues found
  • 1 — warnings only
  • 2 — at least one error

Without a config file

You can pass the schema and ruleset directly on the command line:

polychro lint capabilities/*.yml \
  --schema path/to/ikanos-schema.json \
  --ruleset path/to/ikanos-rules.yml

Embedded (Java API)

If you are building a JVM tool that needs to lint Ikanos files in-process — no subprocess, no Node.js — embed Polychro as a library:

<dependency>
    <groupId>io.polychro</groupId>
    <artifactId>polychro-core</artifactId>
    <version>0.1.0</version>
</dependency>
Linter linter = Linter.builder()
    .config(Path.of(".polychro.yml"))
    .build();

Document doc = Document.fromString(yamlContent, "yaml");
List<Diagnostic> issues = linter.lint(doc);

GitHub Actions Workflow

Polychro ships an official GitHub Action that downloads the native binary, runs the lint, and uploads SARIF results to GitHub Code Scanning. Add this workflow to .github/workflows/lint-capabilities.yml:

name: Lint Capabilities

on:
  push:
    paths:
      - 'capabilities/**'
      - '.polychro.yml'
  pull_request:
    paths:
      - 'capabilities/**'
      - '.polychro.yml'

permissions:
  contents: read
  security-events: write   # needed to upload SARIF to Code Scanning

jobs:
  lint:
    name: Polychro
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: naftiko/polychro@v1
        with:
          files: 'capabilities/**/*.yml'
          config: .polychro.yml
          format: sarif
          fail-on: error

The action reads .polychro.yml automatically, runs the schema and ruleset validators, and fails the build when any diagnostic at or above the fail-on threshold is reported.


What Gets Validated

JSON Schema

The polychro-json-schema validator (JSON Schema Draft 2020-12) catches structural issues such as:

  • Missing required fields (Ikanos, info, capability)
  • Invalid types (e.g., port must be an integer)
  • Invalid enum values (e.g., method must be GET, POST, PUT, PATCH, or DELETE)
  • Invalid patterns (e.g., namespace must match ^[a-zA-Z0-9-]+$)
  • Invalid $schema version

Ruleset

The Ikanos ruleset (Spectral format) catches semantic and style issues such as:

Category Example
Consistency Duplicate namespaces across adapters and binds
URL hygiene Trailing slashes on baseUri, query strings in path fields
Quality Missing description on consumes entries, REST resources, or operations
Security <script> tags or eval( in description fields
Scripting Script steps missing language/location without Control Port defaults (ikanos-script-defaults-required)
Imports Import directive completeness (from+import), alias uniqueness, standalone leaf enforcement

For the full list, see the Specification - Rules page.


Extending the Ikanos Ruleset

The Ikanos ruleset covers the specification's common pitfalls. You can extend it with your own rules to enforce organization-specific conventions — without forking the original ruleset.

Adding Custom Rules

Create your own ruleset file that extends the Ikanos ruleset and adds new rules:

# my-rules.yml
extends:
  - https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

rules:
  # Enforce that all capabilities have at least two tags
  my-org-min-tags:
    message: "Capabilities must have at least 2 tags for discoverability."
    severity: warn
    given: "$.info.tags"
    then:
      function: length
      functionOptions:
        min: 2

  # Enforce a naming convention on exposed resource paths
  my-org-path-prefix:
    message: "All REST resource paths must start with /api/"
    severity: warn
    given: "$.capability.exposes[?(@.type == 'rest')].resources[*].path"
    then:
      function: pattern
      functionOptions:
        match: "^/api/"

  # Require a stakeholder with role "owner"
  my-org-owner-required:
    message: "At least one stakeholder with role 'owner' is recommended."
    severity: info
    given: "$.info.stakeholders[*].role"
    then:
      function: enumeration
      functionOptions:
        values:
          - owner
          - editor
          - viewer

Point Polychro at it in .polychro.yml:

validators:
  ruleset:
    path: ./my-rules.yml

Overriding Built-in Rules

You can change the severity of any Ikanos rule, or disable it entirely:

extends:
  - https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

rules:
  # Promote trailing slash from warning to error
  ikanos-consumes-baseuri-no-trailing-slash: error

  # Disable the info-tags rule (your org doesn't use tags)
  Ikanos-info-tags: off

  # Downgrade missing REST operation description from info to hint
  Ikanos-rest-operation-description: hint

Adding Custom Functions

For advanced validation logic (like the built-in unique-namespaces function), Polychro offers two paths. Java is the recommended option — no extra runtime, full native-image support, and zero per-call overhead. JavaScript, Python, or Groovy are available via the optional polyglot module when you want to port an existing script or your authors are not on the JVM.

Path Module Discovery Startup Per-call cost
Java polychro-ruleset (built-in) ServiceLoader (FunctionProvider) None Native JVM call
Polyglot polychro-ruleset-polyglot (optional) functionsDir resolution GraalVM context init Sandboxed call

Implement RuleFunction, expose it via a FunctionProvider, and drop the JAR on the classpath — no functionsDir configuration required.

// src/main/java/com/example/CheckBindsLocationSchemeFunction.java
package com.example;

import com.fasterxml.jackson.databind.JsonNode;
import io.polychro.ruleset.RuleFunction;

import java.util.List;
import java.util.Map;

public class CheckBindsLocationSchemeFunction implements RuleFunction {

    private static final List<String> ALLOWED = List.of(
        "file://", "vault://", "github-secrets://", "k8s-secret://"
    );

    @Override
    public String name() {
        return "check-binds-location-scheme";
    }

    @Override
    public List<String> evaluate(JsonNode target, Map<String, Object> options) {
        if (target == null || !target.isTextual()) {
            return List.of();
        }
        String value = target.asText();
        boolean allowed = ALLOWED.stream().anyMatch(value::startsWith);
        if (!allowed) {
            return List.of("Bind location \"" + value + "\" must use one of: " + String.join(", ", ALLOWED));
        }
        return List.of();
    }
}
// src/main/java/com/example/MyFunctionProvider.java
package com.example;

import io.polychro.ruleset.FunctionProvider;
import io.polychro.ruleset.RuleFunction;

import java.util.List;

public class MyFunctionProvider implements FunctionProvider {
    @Override
    public List<RuleFunction> functions() {
        return List.of(new CheckBindsLocationSchemeFunction());
    }
}

Register the provider:

# src/main/resources/META-INF/services/io.polychro.ruleset.FunctionProvider
com.example.MyFunctionProvider

Reference the function by name in any ruleset:

extends:
  - https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

rules:
  my-org-binds-location-scheme:
    message: "Bind locations must use an approved URI scheme."
    severity: error
    given:
      - "$.binds[*].location"
      - "$.capability.binds[*].location"
    then:
      function: check-binds-location-scheme

Polyglot (JavaScript, Python, Groovy)

For scripted functions, add the optional polyglot module and use functionsDir.

  1. Create a functions/ directory next to your ruleset:
my-project/
├── .polychro.yml
├── my-rules.yml
├── functions/
│   └── check-binds-location-scheme.js
└── capabilities/
    └── my-capability.yml
  1. Write the function in functions/check-binds-location-scheme.js:
export default function checkBindsLocationScheme(targetVal) {
  const results = [];
  const allowedSchemes = ["file://", "vault://", "github-secrets://", "k8s-secret://"];

  if (typeof targetVal !== "string") return results;

  const hasAllowedScheme = allowedSchemes.some(scheme => targetVal.startsWith(scheme));
  if (!hasAllowedScheme) {
    results.push({
      message: `Bind location "${targetVal}" must use one of: ${allowedSchemes.join(", ")}`,
    });
  }

  return results;
}
  1. Configure Polychro to load the functions directory:
# .polychro.yml
validators:
  ruleset:
    path: ./my-rules.yml
    functionsDir: ./functions/
  1. Reference the function in your ruleset (identical syntax to Java):
extends:
  - https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

rules:
  my-org-binds-location-scheme:
    message: "Bind locations must use an approved URI scheme."
    severity: error
    given:
      - "$.binds[*].location"
      - "$.capability.binds[*].location"
    then:
      function: check-binds-location-scheme

Full Example

Here is a complete .polychro.yml + ruleset pair that extends the Ikanos ruleset with organization-specific rules:

# .polychro.yml
validators:
  json-schema:
    path: https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/schemas/ikanos-schema.json
  ruleset:
    path: ./acme-rules.yml
    functionsDir: ./functions/
# acme-rules.yml — ACME Corp capability linting
extends:
  - https://raw.githubusercontent.com/naftiko/ikanos/main/ikanos-spec/src/main/resources/rules/ikanos-rules.yml

rules:
  # ── Override built-in severities ──
  ikanos-consumes-baseuri-no-trailing-slash: error
  Ikanos-info-tags: off

  # ── Organization rules ──
  acme-min-tags:
    message: "Capabilities must have at least 2 tags."
    severity: warn
    given: "$.info.tags"
    then:
      function: length
      functionOptions:
        min: 2

  acme-binds-location-scheme:
    message: "Bind locations must use an approved URI scheme."
    severity: error
    given:
      - "$.binds[*].location"
      - "$.capability.binds[*].location"
    then:
      function: check-binds-location-scheme

Run it:

polychro lint capabilities/*.yml

Troubleshooting

Polychro: "No issues found"

This is a success message — your file has no errors. Warnings, info, and hint diagnostics are still printed above this line when present.

Polychro: "No schema configured" / schema not auto-detected

Polychro picks up the schema from a # yaml-language-server: $schema=... header when present. If your capability file does not declare one, pass the schema explicitly:

polychro lint my-capability.yml \
  --schema path/to/ikanos-schema.json

Or in .polychro.yml:

validators:
  json-schema:
    path: path/to/ikanos-schema.json

Polychro: "Unable to load custom function"

  • Java functions must be on the classpath of the Polychro process and registered via META-INF/services/io.polychro.ruleset.FunctionProvider. Native binary users who need Java custom functions should build a fat JAR and run it with java -jar instead of the native CLI, or contribute the function upstream.
  • Polyglot functions must live in the functionsDir referenced by your config (.polychro.ymlvalidators.ruleset.functionsDir), resolved relative to that file. When your ruleset extends the hosted Ikanos ruleset, Ikanos's built-in functions (like unique-namespaces) load from the GitHub-hosted functions/ directory automatically.

Polychro: SARIF upload fails in GitHub Actions

The official naftiko/polychro action requires security-events: write permission to publish SARIF results to GitHub Code Scanning. Ensure your workflow grants it:

permissions:
  contents: read
  security-events: write