Skip to content

The axis.json Manifest

Open in ChatGPTOpen in Claude

Every project scaffolded by axs new carries an axis.json manifest at its root. It is the stable, machine-readable record of what the project chose — which starter, which layout, where things live — designed to be parsed by axs add, axs feature, axs doctor, future linting, and AI coding assistants. Its prose twin is the generated STRUCTURE.md; together they are a complete placement spec.

Tooling needs to know where a new Lambda belongs. Inferring that from the directory tree is fragile — a freshly scaffolded project has empty directories, and a customised one has misleading ones. A manifest is honest: the values in paths and conventions are sufficient to compute placement without inspecting the tree at all.

{
"$schema": "https://axs.axis-tech.dev/schema/axs.v1.json",
"framework": "@axis-tech-co/axis-framework",
"frameworkVersion": "1.x",
"starter": "rest-v1",
"layout": "module",
"paths": {
"lambdas": "lambdas/modules",
"lambdaConfigs": "config/lambda-configs",
"tables": "config/tables",
"stacks": "cdk/lib/stacks"
},
"conventions": {
"lambdaGrouping": "module-then-kind",
"tableLocation": "central"
}
}

The manifest is written programmatically by axs new at scaffold time, not carried as a committed source file — so this shape is illustrative and the field-by-field contract is the schema. The complete, versioned v1 field reference (and the layout matrix that maps starter + layout to the paths/conventions values above) is the axis.json Reference.

  • starter / layout — the choices made at axs new time (see Create a Project and Layout).
  • paths / conventions — the placement contract axs add executes against. In feature layout these read lambdas/features, feature-then-name, and colocated instead.
  • $schema — a versioned, frozen identifier. The CLI validates every manifest against a bundled copy of the schema at run time, so validation never touches the network. The identifier is not yet served over HTTP, so editors cannot fetch it for completion; once the schema is published at that URL, JSON-schema-aware editors will pick it up with no change to the manifest.

A wiring discriminator records how the app is composed:

  • "manual" (or absent) — the scaffolded cdk/bin/app.ts instantiates stacks directly, gated by config/features.ts. This is what the plain starters produce.
  • "composer" — the project was scaffolded with --composer: bin/app.ts is a thin call to defineAxisApp, there is no config/features.ts, and the manifest additionally carries a typed selections block (the canonical record of which stacks and construct groups are on) plus a tokens block that lets axs feature <name>=on re-emit a pruned feature’s config byte-identically. See The Composer.

Several commands change behaviour based on this discriminator — for example, axs feature <name>=off flips a boolean in features.ts on a manual project but performs a guarded config removal on a composer project.

The v1 schema is frozen and additive: future fields may appear in v1, and v1 readers ignore unknown fields gracefully (additionalProperties: true). A breaking change — renaming a field, removing one, changing a value enum — ships as v2 with a new $schema URL, so old readers fail loudly rather than misread silently.

The framework itself never reads axis.json. Its only contract with your code is the registries (Project Structure); the manifest is purely a contract between tooling and the project. That split is deliberate: your deployed infrastructure can never depend on a tooling metadata file.

© 2026 Axis Tech. Powered by axis-tech.co.