Public beta — not for production use. Data may be wiped at any time. Questions? Contact us.
Documentation menu

axiom.yaml manifest reference

The complete schema of the axiom.yaml package manifest: every field, its type, default, validation rule, and which CLI commands read or write it.

View as Markdown

axiom.yaml is the package manifest. It sits at the root of every Axiom package and declares the package's identity, its nodes, the message types it imports from other packages, and optional build overrides. The Axiom CLI locates the package root by walking up from the current directory until it finds axiom.yaml.

Three fields are required: name, version, and language. Everything else is optional. axiom validate checks the manifest before publishing (see Validation rules).

Complete example

Every field the parser accepts, in one manifest:

# axiom.yaml — at the package root
name: christian/text-tools
version: 1.2.0
language: python
description: Text processing nodes
author: Christian Lucas
license: MIT
tags:
  - text
  - nlp
nodes:
  - name: SummarizeText
    description: Summarize input text
    input: TextRequest
    output: SummaryResult
    required_secrets:
      - OPENAI_API_KEY
  - name: StreamTokens
    input: TextRequest
    output: TokenFrame
    type: pipeline
imports:
  - package: axiom-official/axiom-conv-ai
    version: 1.0.0
    messages:
      - ChatTurn
env:
  - name: LOG_LEVEL
    description: Verbosity of node logging
    required: false
    secret: false
    default: info
build:
  dockerfile: custom/Dockerfile
  system_deps:
    - ffmpeg
  maven_deps:
    - org.dmfs:lib-recur:0.17.1
  nuget_deps:
    - Cronos:0.8.4

A minimal valid manifest is just the three identity fields — axiom init writes exactly that (plus your --description if given, and a commented example nodes: block you can delete).

Top-level fields

FieldTypeRequiredDefaultPurpose
namestringyesScoped package name, handle/package-name
versionstringyesSemantic version of the package
languagestringyesOne of go, python, rust, java, typescript, csharp
typestringno""proto-only to skip build and deployment; otherwise omit
descriptionstringno""Shown in the registry on publish
authorstringno""Stored with the package record on publish
licensestringno""Stored with the package record on publish
tagslist of stringno[]Discovery tags, stored in the registry's tag index on publish
nodeslist of nodeno[]The package's nodes (see nodes)
importslist of importno[]Message types from other packages (see imports)
envlist of env varno[]Runtime environment variables (see env)
buildobjectnounsetBuild overrides (see build)

description, author, and license are written to the registry when you publish with axiom push. tags are written to the registry's tag index on publish. Marketplace search currently matches on package name, description, and author — not on these tags.

Identity fields: name, version, language

name must be scoped: your-handle/package-name. Both parts may contain only lowercase letters, digits, and hyphens, and each part must start with a letter or digit (pattern: ^[a-z0-9][a-z0-9-]*/[a-z0-9][a-z0-9-]*$). An unscoped or missing name fails validation. axiom init christian/text-tools creates a local directory named after the part after the last / (text-tools/).

version must be semantic versioning: MAJOR.MINOR.PATCH, with an optional leading v and an optional pre-release suffix of letters, digits, and dots after a hyphen (pattern: ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$). Examples: 1.0.0, v2.1.3, 0.4.0-beta.1. axiom init writes 0.1.0 unless you pass --version.

language must be exactly one of go, python, rust, java, typescript, csharp. It selects the node templates, the generated service code, and the Dockerfile the build uses. axiom init defaults to go unless you pass --language (-l).

nodes

Each entry in nodes declares one node in the package. axiom create node appends entries here for you; you can also edit the list by hand.

FieldTypeRequiredDefaultPurpose
namestringyesPascalCase node name, unique within the package
descriptionstringno""Shown in the registry
inputstringyesInput message name
outputstringyesOutput message name
typestringnounaryOmit or unary for one-in/one-out; pipeline for a streaming generator
required_secretslist of stringno[]Secret names the node reads at runtime
mutation_capableboolnofalseDeclares the node emits state mutations on its response
kindstringno"""" / "concrete" (default) — fixed, specific types decided at authoring time; "generic" — a marketplace connector whose port(s) an Instance later binds to a real type (see Generic and Instance nodes)
generic_portslist of stringno[]Which port(s) are generic — "input", "output", or both; required (non-empty) when kind: generic, forbidden otherwise
retryobjectnounsetPackage-author invocation-retry override (see retry)

nameaxiom create node enforces PascalCase: the first character must be uppercase, and only letters and digits are allowed. Duplicate node names in one manifest are rejected. The node's source file is the snake_case form of the name (SummarizeTextnodes/summarize_text.py).

input / output — each must name a message defined in messages/messages.proto or available from an imported package. axiom validate fails when a referenced message cannot be found.

type — a node is unary unless type: pipeline is set. axiom create node --type accepts only unary or pipeline and writes type: pipeline to the manifest only for pipeline nodes (the key is omitted for unary). See execution model.

required_secrets — names of tenant secrets the node reads at runtime. The declaration is informational: the platform does not enforce it at invocation time. A missing secret does not block a run — secrets.Get("NAME") simply yields a not-found result (found = false) at read time, which the node handles. The marketplace displays the list so users know which secrets to register. axiom validate emits a non-blocking warning when node source calls secrets.Get("NAME") with a name missing from this list. See manage secrets.

mutation_capable — declares that the node emits state mutations on its response. The flow compiler reads this across a flow's nodes to decide whether to enable the mutation path; the default false keeps a node out of that path entirely.

Generic and Instance nodes

kind: generic marks a node as a Generic node: a real, published, deployed connector (a database query, an HTTP call) whose input and/or output port is declared broad enough to serve every use of the node, but not yet the specific shape any one flow wants — the author can't know a particular table's columns or an API's exact response shape at publish time. A Generic node cannot be placed in a flow by itself.

nodes:
  - name: Query
    input: QueryParams        # a real, concrete, GENERALIZED message — not a placeholder
    output: Row                # declare real fields broad enough for every use of this node
    kind: generic               # "" / "concrete" (default) | "generic"
    generic_ports: [input, output]   # which port(s): "input", "output", or both

generic_ports names which port(s) of a kind: generic node are generic. It is required (non-empty) whenever kind: generic, and forbidden (must be empty) on any other node. Each entry must be input or output, with no duplicates. Genericness is signaled entirely by kind + generic_portsnever by the message's name; there is no required naming convention (such as a Placeholder suffix) for a generic port's message. axiom validate does warn (non-blocking) if a generic port's message declares zero fields, since a field-less message forces the node's own handler code to hand-decode raw protobuf wire bytes instead of reading ordinary named fields — declare real, concrete, generalized fields instead.

An Instance binds a Generic node's port(s) to a real, specific type via a field mapping — created with axiom instance create (or the marketplace's Generic Nodes tab), not authored in axiom.yaml. An Instance carries no code of its own: it shares the Generic node's already-built, already-deployed image at runtime, so creating one is pure server-side type-binding — no git commit, no build, no Knative deploy. kind: instance can never be hand-authored in a manifest — only the registry stamps that value when an Instance is created; axiom validate rejects a manifest that sets it. See Configure an Instance from a Generic node for the full declare→create→reference loop (including a field whose type is another message, defined recursively, and previewing a mapping before you save), and the HTTP API reference for the underlying POST /v1/instances endpoint.

retry

Axiom guarantees at-least-once invocation of every node by default — this requires no configuration. retry is the optional package-author layer of a three-layer model (platform defaults → package author, here → flow author, flow.yaml's retry: block): set only the fields you want to override for this node; anything you omit inherits from the platform default, and a flow author can still override this node's effective retry again per-flow. See the axiom-package-authoring skill for the full model and the platform defaults, and the axiom-flow-authoring skill for the flow-author layer.

nodes:
  - name: Query
    retry:
      max_attempts: 5
      initial_interval_ms: 500
      backoff_coefficient: 2.0
      max_interval_ms: 30000
      per_attempt_timeout_ms: 5000
      overall_timeout_ms: 60000
      non_retryable_codes: [TIMEOUT]
FieldTypeRequiredPurpose
max_attemptsintnoRetry attempts before the node fails; rejected by axiom validate/axiom push with a blocking error if it exceeds the platform ceiling (10)
initial_interval_msintnoBackoff before the 2nd attempt
backoff_coefficientfloatnoMultiplier applied to the interval after each attempt
max_interval_msintnoCap on the backoff interval
per_attempt_timeout_msintnoBudget for a single attempt
overall_timeout_msintnoTotal budget across all attempts
non_retryable_codeslist of stringnoNodeErrorCode short names to exclude from retry — TIMEOUT/TRANSPORT only here

This layer applies only to NODE_ERROR_TRANSPORT / NODE_ERROR_TIMEOUT (platform invocation failures) — never to NODE_ERROR_USER (the node's own code returning/throwing an error), which is hardcoded non-retryable at every layer and is never a valid non_retryable_codes entry here (axiom validate rejects USER, PANIC, or CANCELLED in this field with an error). Because invocation retry is at-least-once, not exactly-once, write node handlers to be idempotent — see the idempotency guidance in the axiom-package-authoring skill and the SDK reference pages for Go, Python, and TypeScript.

imports

Each entry in imports declares a dependency on another package's message types. Do not write these entries by hand — run axiom import <package>[@version], which downloads the package's .proto files into imports/<flattened-name>/<version>/ (scope / becomes -, e.g. christian/text-opsimports/christian-text-ops/1.0.0/), adds or updates the manifest entry, and runs axiom generate.

FieldTypeRequiredPurpose
packagestringyesScoped name of the imported package
versionstringyesExact version of the imported package
messageslist of stringyesMessage names imported from that package

Validation rules for imports:

  • An import entry with an empty messages list is a hard error and blocks publishing. Fix it by re-running axiom import <package>@<version>, which populates the list.
  • A message name listed here but not found in the downloaded proto files is a warning — it usually means a stale entry after the imported package changed.
  • The downloaded proto files must exist locally under imports/<flattened-name>/<version>/; if they are missing, re-run axiom import <package>@<version>.

See import package types for the full workflow.

env

Each entry in env describes a runtime environment variable for the package. All fields except name are optional.

FieldTypeRequiredDefaultPurpose
namestringyesVariable name
descriptionstringno""What the variable controls
requiredboolnofalseWhether the variable must be set
secretboolnofalseWhether the value is sensitive
defaultstringno""Default value

The env section is declarative metadata about what the package expects; axiom validate does not check it. For credentials read by node code at runtime, use secrets and required_secrets instead — secrets are tenant-scoped and reach node code through AxiomContext.

build

The optional build section overrides how the package's container image is built. Most packages never need it — axiom build and axiom push generate a Dockerfile automatically from the package language.

FieldTypeRequiredPurpose
dockerfilestringnoPath (relative to the package root) to a custom Dockerfile used instead of the generated one
system_depslist of stringnoExtra OS-level packages installed into the generated image
maven_depslist of stringno(Java only) Third-party Maven dependencies, each an exact-pinned groupId:artifactId:version coordinate
nuget_depslist of stringno(C# only) Third-party NuGet dependencies, each an exact-pinned PackageID:Version coordinate

maven_deps

maven_deps lets a Java package depend on third-party libraries from Maven Central. Each entry is a coordinate groupId:artifactId:version, for example org.dmfs:lib-recur:0.17.1. The coordinates are rendered into the <dependencies> block of the generated pom.xml — which is regenerated on every build, so you declare dependencies here rather than editing pom.xml directly (hand edits are overwritten). The same declaration is honored by the local build (axiom build), by axiom push, and by the server-side registry build, so a Java node can call the library at runtime after publish.

The version must be an exact pin for reproducibility: axiom validate rejects Maven version ranges ([1.0,2.0)), Gradle-style dynamic versions (0.17.+), the LATEST/RELEASE keywords, and -SNAPSHOT versions, as well as any coordinate that is not exactly three colon-separated parts. maven_deps declared on a non-Java package is a validation error. Classifiers, exclusions, BOM imports, and custom <repositories> are not supported today — only plain Maven-Central coordinates.

nuget_deps

nuget_deps is the C# equivalent: it lets a C# package depend on third-party libraries from NuGet.org. Each entry is a coordinate PackageID:Version, for example Cronos:0.8.4. The coordinates are rendered as <PackageReference> items into the generated service.csproj (and the tests/Tests.csproj that axiom test runs) — both are regenerated on every build, so you declare dependencies here rather than editing them directly (hand edits are overwritten). The same declaration is honored by the local build (axiom build), by axiom push, and by the server-side registry build, so a C# node can call the library at runtime after publish.

The version must be an exact pin for reproducibility: axiom validate rejects NuGet floating versions (6.0.*), version ranges ([1.0,2.0)), and any coordinate that is not exactly two colon-separated parts. nuget_deps declared on a non-C# package is a validation error. Custom NuGet feeds (<RestoreSources>), private sources, and per-reference asset controls are not supported today — only plain NuGet.org coordinates.

system_deps

system_deps entries are installed by the generated Dockerfile as OS-level packages. For Python packages, the special value axiom installs the Axiom CLI binary into the image instead of an OS package.

For Python packages, axiom validate scans node source for subprocess calls and emits a non-blocking warning when a node invokes a common tool that is absent from the slim base image and not listed in build.system_deps — without the entry, the tool would be missing from the runtime image. The scan covers a fixed list of tools: git, curl, wget, ssh, rsync, zip, unzip, make, gcc, g++, svn, jq. A tool outside that list (such as ffmpeg) still needs a system_deps entry to be present in the image — it just is not flagged by validation.

Proto-only packages

A proto-only package publishes message types for other packages to import, with no nodes and no running service. A package is treated as proto-only when either:

  • type: proto-only is set explicitly, or
  • the nodes list is empty (proto-only is inferred).

For a proto-only package, the publish pipeline skips the Docker build and the service deployment, and stores only the proto definitions and package metadata. Other packages then pull its messages with axiom import. See the type system.

Which commands read and write axiom.yaml

  • axiom init <name> creates the manifest with name, version (default 0.1.0), language (default go), and your --description. Unless --no-example-comment is passed, it appends a commented example nodes: block showing the required_secrets field.
  • axiom create node <Name> appends a node entry (name, description, input, output, and type: pipeline for pipeline nodes) after scaffolding the source files.
  • axiom import <package>[@version] adds or updates an imports entry, merging newly downloaded message names into the messages list.
  • axiom validate, axiom build, axiom push, axiom dev, and axiom test read the manifest; they must be run inside a package directory (any directory below the one containing axiom.yaml).

When a CLI command rewrites the manifest, it re-serializes the whole file — inline comments you wrote in axiom.yaml are not preserved by these operations. Keep notes elsewhere if you need them to survive.

Validation rules

axiom validate (also run as a gate by axiom push) checks the manifest as its first layer. Failures block publishing unless marked as warnings:

CheckSeverity
name present and scoped (handle/package-name, lowercase/digits/hyphens)error
version present and valid semvererror
language one of the six supported valueserror
Every node's input and output resolves to a known messageerror
Every import entry has a non-empty messages listerror
Imported message names exist in the downloaded proto fileswarning
secrets.Get("NAME") calls covered by required_secretswarning
Python subprocess tools covered by build.system_depswarning
retry.max_attempts within the platform ceiling; retry.per_attempt_timeout_ms not exceeding retry.overall_timeout_ms; retry.non_retryable_codes entries are TIMEOUT/TRANSPORT onlyerror
kind is one of "", "concrete", "generic""instance" can never be hand-authorederror
generic_ports is non-empty (and each entry is input/output, no duplicates) when kind: generic; empty otherwiseerror
A generic_ports-listed port's message declares at least one fieldwarning

Later validation layers check the proto files, node function signatures, and node tests — see the axiom validate reference.