Ikanos — Guide - Linting¶
Table of Contents¶
- Overview
- Quick Start — Polychro CLI
- Polychro Setup
- Prerequisites
- Configuration Files
- Run Locally
- GitHub Actions Workflow
- What Gets Validated
- Extending the Ikanos Ruleset
- Adding Custom Rules
- Overriding Built-in Rules
- Adding Custom Functions
- Full Example
- Troubleshooting
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-schemaandpolychro-rulesetin the same pipeline. Unlike Spectral or v8r, Polychro will pick up# yaml-language-server: $schema=...headers when present — the explicitpathabove 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 found1— warnings only2— 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.,
portmust be an integer) - Invalid enum values (e.g.,
methodmust be GET, POST, PUT, PATCH, or DELETE) - Invalid patterns (e.g.,
namespacemust match^[a-zA-Z0-9-]+$) - Invalid
$schemaversion
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:
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 |
Java (recommended)¶
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.
- 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
- 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;
}
- Configure Polychro to load the functions directory:
- 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:
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:
Or in .polychro.yml:
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 withjava -jarinstead of the native CLI, or contribute the function upstream. - Polyglot functions must live in the
functionsDirreferenced by your config (.polychro.yml→validators.ruleset.functionsDir), resolved relative to that file. When your rulesetextendsthe hosted Ikanos ruleset, Ikanos's built-in functions (likeunique-namespaces) load from the GitHub-hostedfunctions/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: