Kaiten

Deployments

Components & Releases

The component as a versioned brick and the release as an immutable bundle of component versions — creation, versioning, the immutability rule, and the associations the API exposes.

A component is one building block of your product at one version. A release is the set of component versions you ship together. The two are records: Kaiten does not build, store, or run your artifacts — it keeps the authoritative list of what a release contains, so that a deployment zone can point at a release and every instance in that zone is known to run exactly those versions.

Examples on this page use $KAITEN_API and $KAITEN_TOKEN as set up in the quickstart, and extend the seeded Kaiten Sushi Shop catalog — they create new entities next to the seeded ones rather than altering them.

Component — the versioned brick

A component is identified by its name and version together, unique within your organization: Kitchen Display 1.2.0 is one component, Kitchen Display 1.3.0 would be another. Each also has a slug, unique per organization, used in every URL. Versions are linked into a history through previousComponentId: Kaiten Sushi Shop's Delivery Service exists twice — 0.9.0 (driver dispatch, beta) and 1.0.0 (real-time position streaming), the second pointing at the first. Kitchen Display 1.2.0 has no successor yet.

FieldRequiredConstraints
nameyes1–100 characters; (name, version) unique per organization
versionyes1–100 characters, free-form — 2026.8.0, 1.0.0, v1.2.3 are all valid
slugno2–100 characters, ^[a-z0-9][a-z0-9-]*[a-z0-9]$, unique per organization; generated when omitted
descriptionnoup to 500 characters
previousComponentIdnoid of the version this one succeeds

The response adds id, createdBy, and createdAt. Reading and listing require read:components; every write requires write:components.

Create a component version

Kitchen Display moves from 1.2.0 to 1.3.0. The new version is a new component that names its predecessor. Look up the predecessor's id first:

curl "$KAITEN_API/components/kitchen-display-1-2-0" \
  -H "Authorization: Bearer $KAITEN_TOKEN"
{
  "id": "7c1e2d3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
  "name": "Kitchen Display",
  "version": "1.2.0",
  "slug": "kitchen-display-1-2-0",
  "description": "Kitchen display system.",
  "createdBy": { "id": "…", "name": "Ops Team" },
  "createdAt": "2026-07-01T09:00:00Z"
}

Responses on this page are illustrative — ids and timestamps depend on your stack. Every record the seeder writes names the same actor, the Ops Team service account it acts as. Keep the id:

export KDS_120_ID="7c1e2d3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f"   # yours will differ

Then create the successor:

curl -X POST "$KAITEN_API/components" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Kitchen Display",
    "version": "1.3.0",
    "slug": "kitchen-display-1-3-0",
    "description": "Order timers and allergen badges.",
    "previousComponentId": "'"$KDS_120_ID"'"
  }'
{
  "id": "9b8a7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
  "name": "Kitchen Display",
  "version": "1.3.0",
  "slug": "kitchen-display-1-3-0",
  "description": "Order timers and allergen badges.",
  "previousComponentId": "7c1e2d3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
  "createdBy": { "id": "…", "name": "…" },
  "createdAt": "2026-09-08T10:12:00Z"
}

HTTP/1.1 201 Created, and COMPONENT_CREATED is emitted. Posting Kitchen Display 1.2.0 a second time is refused with 409 Conflict — the name and version pair already exists. A previousComponentId that does not exist in your organization is a 404.

Update — in place, or as the next version

PUT /components/{slug} takes the same name, version, slug, and description fields and behaves according to one question: is this component bundled by a release?

  • Not bundled — the component is updated in place. kitchen-display-1-3-0, just created and not yet in any release, can be corrected freely:

    curl -X PUT "$KAITEN_API/components/kitchen-display-1-3-0" \
      -H "Authorization: Bearer $KAITEN_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Kitchen Display",
        "version": "1.3.0",
        "slug": "kitchen-display-1-3-0",
        "description": "Order timers, allergen badges, and station routing."
      }'

    The response is the same component (200 OK, same id) with the new description.

  • Bundled by at least one release — the component is not touched, because the release must keep meaning what it meant when it was cut. Kaiten creates the next version carrying your changes instead, chained to the one you addressed. api-2026-8-0 is bundled by 2026.8.0; a PUT on it with a new version yields a new component, and 2026.8.0 still lists the original. A PUT that keeps the same name and version is refused with 400, code UpdateComponent.LinkedComponentRequiresNewVersion.

Both outcomes emit COMPONENT_UPDATED — the catalog describes it as "updated or auto-versioned".

Delete

DELETE /components/{slug} returns 204 No Content for a component no release bundles, and 409 Conflict for one that a release lists. A release's component list is part of the release; removing a member would rewrite history, so the component stays as long as the release does. COMPONENT_DELETED is emitted on success.

Release — the immutable bundle

A release is a version — unique within your organization — with an optional slug and description, and the list of components it bundles. The list is given at creation as componentIds and returned on every read as components, fully expanded. A release is immutable: created once, read many times, never edited — the rule is spelled out below.

FieldRequiredConstraints
versionyes1–100 characters, unique per organization
slugnosame rules as components; generated when omitted
descriptionnoup to 500 characters
componentIdsnoids of the components to bundle — fixed once created

Kaiten Sushi Shop has two: 2026.7.0 ("July platform release"), which bundles api-2026-7-0, web-app-2026-7-0, kitchen-display-1-2-0, and delivery-service-0-9-0, and 2026.8.0 ("Introduces real-time delivery tracking"), which bundles api-2026-8-0, web-app-2026-8-0, kitchen-display-1-2-0, and delivery-service-1-0-0. The seeder gives the components and the releases explicit slugs, the same on every stack: the two releases are r-2026-7-0 and r-2026-8-0.

The Releases page of Kaiten Sushi Shop: 2026.8.0, Introduces real-time delivery tracking, deployed in two zones for five instances, and 2026.7.0, July platform release, deployed in one zone for one instance, each with four components.The Releases page of Kaiten Sushi Shop: 2026.8.0, Introduces real-time delivery tracking, deployed in two zones for five instances, and 2026.7.0, July platform release, deployed in one zone for one instance, each with four components.

Create a release

Cut 2026.8.1: the 2026.8.0 bundle with Kitchen Display moved to 1.3.0. Collect the four component ids — GET /components/{slug} for api-2026-8-0, web-app-2026-8-0, delivery-service-1-0-0, and the kitchen-display-1-3-0 created above:

export API_280_ID="<id of api-2026-8-0>"
export WEB_280_ID="<id of web-app-2026-8-0>"
export KDS_130_ID="9b8a7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"   # from the 201 above — yours will differ
export DELIVERY_100_ID="<id of delivery-service-1-0-0>"

Then post them together:

curl -X POST "$KAITEN_API/releases" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "version": "2026.8.1",
    "slug": "2026-8-1",
    "description": "Kitchen Display 1.3.0: order timers and allergen badges.",
    "componentIds": [
      "'"$API_280_ID"'",
      "'"$WEB_280_ID"'",
      "'"$KDS_130_ID"'",
      "'"$DELIVERY_100_ID"'"
    ]
  }'
{
  "id": "4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a",
  "version": "2026.8.1",
  "slug": "2026-8-1",
  "description": "Kitchen Display 1.3.0: order timers and allergen badges.",
  "components": [
    { "id": "…", "name": "API", "version": "2026.8.0", "slug": "api-2026-8-0", "…": "…" },
    { "id": "…", "name": "Web App", "version": "2026.8.0", "slug": "web-app-2026-8-0", "…": "…" },
    { "id": "…", "name": "Kitchen Display", "version": "1.3.0", "slug": "kitchen-display-1-3-0", "…": "…" },
    { "id": "…", "name": "Delivery Service", "version": "1.0.0", "slug": "delivery-service-1-0-0", "…": "…" }
  ],
  "createdBy": { "id": "…", "name": "…" },
  "createdAt": "2026-09-08T10:20:00Z"
}

201 Created; RELEASE_CREATED is emitted. The request names its slug, so the release is addressable as /releases/2026-8-1; without slug, the API generates one from the version with a random suffix — 2026-8-1- followed by six hexadecimal characters — to be read from the response. A second release with version: "2026.8.1" is a 409; an unknown component id is a 404. From this moment kitchen-display-1-3-0 is bundled: a PUT on it now creates a successor carrying your changes rather than editing it, and it can no longer be deleted.

The immutability rule

A release, once created, is a fact:

  • No update endpoint. There is no PUT or PATCH on /releases/{slug} — the resource exposes GET, POST on the collection, and DELETE, nothing else; not even the description can change. Nothing writes the record after its creation, and the record carries no updatedAt or updatedBy: the columns were removed rather than left unused, which is why a release reads with createdBy and createdAt only, where a zone or an instance also reads with its last update.
  • The component list is fixed. components is exactly the set given at creation.
  • Correcting a release means creating another one. Cut 2026.8.2 with the right components; the wrong one can be deleted only if it was never deployed.
  • Deletion is refused once a release has been deployed. DELETE /releases/{slug} answers 409 Conflict, code DeleteRelease.InUseConflict, as soon as the journal holds an entry for the release — whether or not a zone still runs it — and while another release names it as its previous_release_id. Deleting a release never removes a journal entry: while an entry names the release, the release stays. One that was created and never deployed can be deleted, and RELEASE_DELETED is emitted.

The rule is what makes the rest of the pillar trustworthy: when the journal says 2026.8.0 landed in shared-eu, the four component versions that statement refers to are the four it referred to on the day it landed.

Associations as the API exposes them

Every link between the entities of this pillar is a field on one of them:

LinkWhere it livesDirection
Component version historyComponent.previousComponentIdnewer → older
Release contentsRelease.components[] (expanded Component objects)release → components
Zone's current releaseDeploymentZone.releaseIdzone → release
Journal entryDeployment.releaseId, Deployment.deploymentZoneIdentry → release, entry → zone
Instance placementInstance.deploymentZoneId and deploymentZoneSluginstance → zone

Two things a reader might expect are not there. A component does not list the releases that bundle it — walk GET /releases and read components instead. And releases do not chain to one another in the API: the data model shows a previous_release_id column, but no request accepts it and no response returns it. The succession 2026.7.0 → 2026.8.0 is visible in the version numbers; GET /releases/{slug} carries no link to a predecessor.

Reading a seeded release shows the shape:

curl "$KAITEN_API/releases/r-2026-8-0" \
  -H "Authorization: Bearer $KAITEN_TOKEN"
{
  "id": "2b6f9c1e-8d3a-4f7b-9e0c-6a5d4b3c2f1e",
  "version": "2026.8.0",
  "slug": "r-2026-8-0",
  "description": "Introduces real-time delivery tracking.",
  "components": [
    { "id": "…", "name": "API", "version": "2026.8.0", "slug": "api-2026-8-0", "previousComponentId": "…", "…": "…" },
    { "id": "…", "name": "Web App", "version": "2026.8.0", "slug": "web-app-2026-8-0", "previousComponentId": "…", "…": "…" },
    { "id": "…", "name": "Kitchen Display", "version": "1.2.0", "slug": "kitchen-display-1-2-0", "…": "…" },
    { "id": "…", "name": "Delivery Service", "version": "1.0.0", "slug": "delivery-service-1-0-0", "previousComponentId": "…", "…": "…" }
  ],
  "createdBy": { "id": "…", "name": "Ops Team" },
  "createdAt": "2026-08-01T09:00:00Z"
}

Lists (GET /components, GET /releases) are cursor-paginated: items, hasMore, and a nextCursor to pass back as ?cursor=; limit defaults to 50 and caps at 200. The shared conventions — pagination, problem responses, slugs — are documented on API conventions.

Events

Event nameEvent typeWhen
COMPONENT_CREATEDcom.kaiten.component.v1.createdA component is created
COMPONENT_UPDATEDcom.kaiten.component.v1.updatedA component is updated in place or auto-versioned
COMPONENT_DELETEDcom.kaiten.component.v1.deletedA component is deleted
RELEASE_CREATEDcom.kaiten.release.v1.createdA release is created
RELEASE_DELETEDcom.kaiten.release.v1.deletedA release is deleted

The payload of each is the full object — Component or Release — as returned by the API.

Where to go next

A release does nothing until a zone runs it: Deployment Zones models the places, and Deployments & Lifecycle records the landings. Exact reference: data model.

On this page