Naftiko · API Governance Rules
Naftiko API Rules
Spectral linting rules defining API design standards and conventions for Naftiko.
33 Rules
error 14
warn 16
info 3
Rule Categories
ikanos
Rules
error
ikanos-namespaces-unique
The same namespace must not be reused across root `consumes`, `capability.consumes`, `capability.exposes`, root `binds`, and `capability.binds`. This prevents ambiguous routing, reference resolution, and variable resolution.
$
warn
ikanos-consumes-baseuri-no-trailing-slash
A trailing slash on the baseUri can cause double-slash issues when the resource path is appended (e.g. https://api.example.com//users).
$.consumes[*].baseUri$.capability.consumes[*].baseUri
warn
ikanos-consumed-resource-no-query-in-path
Query parameters belong in the inputParameters array with in: query, not in the path string.
$.consumes[?(@.type == 'http')].resources[*].path$.capability.consumes[?(@.type == 'http')].resources[*].path
warn
ikanos-rest-resource-path-no-trailing-slash
Trailing slashes on resource paths can cause confusion in routing, documentation, and client code generation.
$.capability.exposes[?(@.type == 'rest')].resources[*].path
warn
ikanos-rest-resource-path-no-query
$.capability.exposes[?(@.type == 'rest')].resources[*].path
warn
ikanos-address-not-example
Placeholder hosts are discouraged in production-bound capability documents.
$.capability.exposes[?(@.type == 'rest')].address$.capability.exposes[?(@.type == 'mcp')].address$.capability.exposes[?(@.type == 'skill')].address
warn
ikanos-aggregate-flow-description
Flow descriptions are inherited by adapter units and improve agent discoverability.
$.capability.aggregates[*].flows[*]
warn
ikanos-aggregate-semantics-consistency
When an MCP tool or REST operation references an aggregate flow via `ref`, any explicit hints or HTTP methods should not contradict the flow's declared semantics. For example, a safe flow should not have destructive=true hints or use a POST/DELETE method.
$
warn
ikanos-mcp-auth-stdio-conflict
Per MCP specification §1.2, stdio transport should not follow the HTTP authorization flow — credentials are retrieved from the environment instead. Authentication is only meaningful for the HTTP transport.
$.capability.exposes[?(@.type == 'mcp' && @.transport == 'stdio')]
error
ikanos-oauth2-https-authserver
The OAuth 2.1 specification requires all authorization server endpoints to be served over HTTPS. A non-HTTPS authorization server URI is a security risk.
$.capability.exposes[*].authentication[?(@.type == 'oauth2')]
warn
ikanos-oauth2-resource-https
The resource URI identifies this server in Protected Resource Metadata and audience validation. Using HTTPS ensures proper security in production.
$.capability.exposes[*].authentication[?(@.type == 'oauth2')]
warn
ikanos-oauth2-scopes-defined
Defining scopes enables the server to include scope information in WWW-Authenticate challenges and Protected Resource Metadata, improving client interoperability.
$.capability.exposes[*].authentication[?(@.type == 'oauth2')]
info
ikanos-info-tags
Tags help categorize the capability for discovery and filtering. At least one tag improves agent cataloging.
$.info
warn
ikanos-consumes-description
A description of the consumed API improves agent understanding of what external service is being used and why.
$.consumes[*]$.capability.consumes[*]
warn
ikanos-rest-resource-description
Resource descriptions are used by agents to understand the purpose of each endpoint. In a world of agents, context is king.
$.capability.exposes[?(@.type == 'rest')].resources[*]
info
ikanos-rest-operation-description
Analogous to operation-description in OAS. Descriptions help agents understand which operations to invoke and when.
$.capability.exposes[?(@.type == 'rest')].resources[*].operations[*]
error
ikanos-no-script-tags-in-markdown
Analogous to no-script-tags-in-markdown (OAS) and arazzo-no-script-tags-in-markdown. Protects against XSS attacks when capability documents from third parties are rendered in HTML documentation or agent UIs.
$.info.description$.info.label$.capability.exposes[*].description$.capability.exposes[?(@.type == 'rest')].resources[*].description$.capability.exposes[?(@.type == 'rest')].resources[*].operations[*].description$.capability.exposes[?(@.type == 'mcp')].tools[*].description$.capability.exposes[?(@.type == 'skill')].skills[*].description$.consumes[*].description$.capability.consumes[*].description
error
ikanos-no-eval-in-markdown
Analogous to no-eval-in-markdown (OAS). Protects against JavaScript injection attacks when description content is rendered in HTML/JS-based documentation or agent tooling.
$.info.description$.info.label$.capability.exposes[*].description$.capability.exposes[?(@.type == 'rest')].resources[*].description$.capability.exposes[?(@.type == 'rest')].resources[*].operations[*].description$.capability.exposes[?(@.type == 'mcp')].tools[*].description$.capability.exposes[?(@.type == 'skill')].skills[*].description$.consumes[*].description$.capability.consumes[*].description
warn
ikanos-baseuri-not-example
Analogous to oas2-host-not-example and oas3-server-not-example.com. Placeholder example.com URIs should never appear in production-bound capability documents — use real service URLs instead.
$.consumes[*].baseUri$.capability.consumes[*].baseUri
error
ikanos-control-port-singleton-and-unique
At most one type: control adapter is allowed per capability, and its port must not conflict with any business adapter port.
$
warn
ikanos-control-address-localhost-warning
Binding the control port to a non-localhost address exposes management endpoints to the network. Use a Kubernetes NetworkPolicy or firewall rule to restrict access in production.
$.capability.exposes[?(@.type == 'control')].address
error
ikanos-script-defaults-required
Every script step must have a resolvable scripts directory and language. When a step omits 'location' or 'language', the engine falls back to 'management.scripting.defaultLocation' or 'management.scripting.defaultLanguage' on the control adapter. If neither is set, the capability will fail at load time. This rule catches the misconfiguration at lint time.
$
error
ikanos-import-from-required
Every import entry in a capability's consumes, exposes, aggregates, or binds section must specify 'from' (the source file path) and 'import' (the namespace to take from that source). An entry with only one of the two is invalid.
$.capability.consumes[?(@.from)]$.capability.exposes[?(@.from)]$.capability.aggregates[?(@.from)]$.capability.binds[?(@.from)]
error
ikanos-import-import-required
Complement of ikanos-import-from-required: catches entries that have 'import' but are missing 'from'.
$.capability.consumes[?(@.import && !@.type)]$.capability.exposes[?(@.import && !@.type)]$.capability.aggregates[?(@.import && !@.type)]$.capability.binds[?(@.import && !@.type)]
error
ikanos-import-unique-alias
Within each section of a capability, the effective namespace of imported entries (the 'as' alias, or 'import' when no 'as') must be unique. Duplicate effective namespaces would cause ambiguous resolution after imports are materialized.
$
warn
ikanos-import-from-not-self
An import 'from' field that is a bare dot ('.') is always an authoring error because it resolves to the importing file's own directory. The engine resolver catches the actual self-reference at runtime.
$.capability.consumes[?(@.from)].from$.capability.exposes[?(@.from)].from$.capability.aggregates[?(@.from)].from$.capability.binds[?(@.from)].from
error
ikanos-standalone-no-imports
Standalone section files (documents without a 'capability' key) are leaves in the import DAG. They must not contain import entries (entries with a 'from' field). Only capability documents may import from other files.
$
error
ikanos-exposes-namespace-required
Without a namespace, an exposed adapter cannot be referenced by an import directive from another file. Applies to standalone source files (root-level 'exposes' array) and to inline entries under 'capability.exposes' that declare a 'type' (i.e. local definitions, not import entries — which have 'from' instead).
$.exposes[*]$.capability.exposes[?(@.type)]
error
ikanos-aggregates-namespace-required
Without a namespace, an aggregate cannot be referenced by an import directive from another file. Applies to standalone source files (root-level 'aggregates' array) and to inline entries under 'capability.aggregates' that declare 'functions' (i.e. local definitions, not import entries — which have 'from' instead).
$.aggregates[*]$.capability.aggregates[?(@.functions)]
error
ikanos-aggregates-unique-function-name
Duplicate function names within a single aggregate would cause ambiguous 'call' and 'ref' resolution. Applies to both standalone and capability documents.
$.aggregates[*].functions$.capability.aggregates[*].functions
error
ikanos-tunnel-identity-must-bind
When a ConsumesHttp 'tunnel' uses Mustache references in 'identity' (for example `{{secrets.ZITI_IDENTITY}}`), every referenced namespace and key must be declared in the document's 'binds' or 'capability.binds' section. Bare filesystem paths are accepted but discouraged.
$
warn
ikanos-tunnel-fallback-direct-warns
A direct fallback means the engine will skip the tunnel and dial the baseUri directly when the tunnel cannot be opened. For public baseUris this defeats the privacy guarantees of the tunnel. Use 'fail' (the default) and let the operator decide how to recover.
$.capability.consumes[?(@.tunnel && @.tunnel.fallback == 'direct')].baseUri$.consumes[?(@.tunnel && @.tunnel.fallback == 'direct')].baseUri
info
ikanos-tunnel-with-public-baseuri-warns
A 'tunnel' is intended for reaching APIs on private networks. When the baseUri points to a public TLD (.com, .net, .io, ...), the configuration may still be correct (the tunnel can override DNS), but it is worth double-checking that the upstream is not unintentionally reached over the public internet.
$.capability.consumes[?(@.tunnel)].baseUri$.consumes[?(@.tunnel)].baseUri