Kitaru
Guides

Deploy and invoke flows

A practical producer-consumer guide to deploying Kitaru flows, moving tags, and invoking stable or canary routes

This guide walks through the deployment workflow you will probably use in a real team: one person publishes a flow, tests a canary version, promotes it, and then other people or agents invoke it by name.

The short mental model is:

  1. Producer deploys from source: kitaru deploy flows/research.py:research_agent.
  2. Kitaru versions the deployment automatically: v1, then v2, then v3.
  3. Producer moves tags like default, canary, or stable to control routes.
  4. Consumer invokes one route by flow name plus a selector: usually a tag, sometimes an exact version. Kitaru resolves that route to the saved snapshot.

The story: a research flow you want to share

Imagine you have this flow in flows/research.py:

from kitaru import checkpoint, flow

@checkpoint
def collect_notes(topic: str) -> str:
    ...

@checkpoint
def write_summary(notes: str) -> str:
    ...

@flow
def research_agent(topic: str) -> str:
    notes = collect_notes(topic)
    return write_summary(notes)

When you are still developing locally, you can run it directly from source:

research_agent.run(topic="durable execution").wait()

A deployment is for the next step: saving a reusable, versioned entrypoint so other processes can invoke the flow without importing flows/research.py.

Step 1: deploy the first version

If you deploy from a source target (path.py:flow_name), initialize the repository first:

kitaru init

Then deploy from the source target:

kitaru deploy flows/research.py:research_agent \
  --input '{"topic": "durable execution"}'

The --input value should contain representative deployment-time input values. Kitaru uses them to prepare the saved deployment snapshot. Consumers can override those values later when they invoke the deployment.

Each kitaru deploy command attaches exactly one routing tag at deploy time. That first deploy gets the reserved default route automatically. Later deploys can choose one other route such as canary, and any extra tags are added or moved afterward with kitaru flow tag.

Because this is the first deployment for research_agent, Kitaru creates:

FlowVersionTags
research_agent1default*

The * means the tag is exclusive. Exclusive tags point to exactly one version at a time.

You can inspect the versions:

kitaru flow deployments list research_agent
kitaru flow deployments show research_agent

show defaults to the default tag when you do not pass --version or --tag.

Step 2: invoke the default route

Now invoke the deployed flow by name:

kitaru invoke research_agent \
  --input '{"topic": "serverless routing"}'

Because you did not specify --version or --tag, Kitaru tries the reserved implicit default route. It resolves research_agent + default to the saved deployment snapshot for that version, starts a new execution from it, and returns an execution ID.

If the flow has no deployments yet, Kitaru says so directly. If deployments exist but none is currently routed as default, invoke with --tag or --version, or move default with kitaru flow tag ... --exclusive.

The same invocation from Python looks like this:

from kitaru import KitaruClient

handle = KitaruClient().deployments.invoke(
    flow="research_agent",
    inputs={"topic": "serverless routing"},
)
print(handle.exec_id)

If you have the flow object imported in the current process, .invoke() is the remote invocation verb:

from flows.research import research_agent

handle = research_agent.invoke(topic="serverless routing")

Step 3: deploy a canary version

Now imagine you improve the flow and want to test the new behavior without moving the default route yet. Deploy it with an exclusive canary tag:

kitaru deploy flows/research.py:research_agent \
  --tag canary \
  --exclusive \
  --input '{"topic": "durable execution"}'

Kitaru creates version 2 and attaches canary*:

FlowVersionTags
research_agent1default*
research_agent2canary*

At deploy time, that is still just one route. If you later want to add another label without redeploying, do it with kitaru flow tag, for example:

kitaru flow tag research_agent benchmark --version 2

That gives version 2 an exclusive canary route plus a shared benchmark label.

Now you can test only the canary route:

kitaru invoke research_agent \
  --tag canary \
  --input '{"topic": "tag routing"}'

This is the safe pattern: deploy a candidate, invoke it explicitly, inspect the execution, and only then move the stable route.

Step 4: promote a version

There are two common promotion styles.

Option A: move default

If your consumers use the implicit default route, move default to version 2:

kitaru flow tag research_agent default --version 2 --exclusive

After that, plain kitaru invoke research_agent ... routes to version 2. The old version no longer has default.

Option B: publish a separate stable route

If you prefer an explicit production route, move stable to version 2:

kitaru flow tag research_agent stable --version 2 --exclusive

Consumers then invoke:

kitaru invoke research_agent \
  --tag stable \
  --input '{"topic": "consumer request"}'

This is nice when you want default for interactive testing, while stable is what other systems use.

Producer vs consumer responsibilities

A useful way to keep the model straight:

RoleNeeds source target?Typical commands
ProducerYes, for deployskitaru deploy, kitaru flow tag, kitaru flow deployments list
ConsumerNokitaru invoke, KitaruClient().deployments.invoke(...), MCP kitaru_deployments_invoke

The producer knows flows/research.py:research_agent. The consumer only needs research_agent plus a selector such as default, stable, canary, or v2.

When selectors matter

Most of the time, consumers should use a tag:

kitaru invoke research_agent --tag stable --input '{"topic": "..."}'

Use an exact version when you need reproducibility:

kitaru invoke research_agent --version 2 --input '{"topic": "..."}'

A version is a hard pin. It will not move when someone promotes stable or default later. Tags are movable route names; versions are the saved deployment snapshots those routes resolve to.

Exclusive tags

Use exclusive tags for routes that should point to one version:

  • default
  • stable
  • prod
  • canary when there is only one current canary

default is always exclusive and reserved. You cannot remove it directly. To move it, attach default to another version with --exclusive.

A deployment that still holds any exclusive tag cannot be deleted. Move the tag first, then delete the old version:

kitaru flow tag research_agent default --version 2 --exclusive
kitaru flow deployments delete research_agent --version 1

Shared tags

Non-exclusive tags can point to more than one version. A common mixed pattern is: deploy one exclusive route at create time (--tag canary --exclusive), then add shared labels later with kitaru flow tag. Shared tags are useful for marking a group of deployments:

kitaru flow tag research_agent benchmark --version 1
kitaru flow tag research_agent benchmark --version 2

But shared tags are not good invocation routes once they point to multiple versions. If benchmark points to both v1 and v2, then this selector is ambiguous:

kitaru invoke research_agent --tag benchmark --input '{"topic": "..."}'

When a tag should be invocable by consumers, make it exclusive.

Generate a curl command

If you need to trigger a deployment from a shell script, CI job, or another system that can make HTTP requests, ask Kitaru to generate the curl command for the active Kitaru server.

Before you do that, make sure Kitaru knows three things:

  1. the Kitaru server URL,
  2. an auth token or API key that can access that server, and
  3. the project to use on that server.

You can provide those by logging in once:

kitaru login https://kitaru.example.com --api-key kat_abc123 --project production

Or, in CI and other headless environments, with environment variables:

export KITARU_SERVER_URL=https://kitaru.example.com
export KITARU_AUTH_TOKEN=kat_abc123
export KITARU_PROJECT=production

Run kitaru info if you want to check which connection Kitaru is using. If one of those three pieces is missing, kitaru auth token exits with a short error that says what to set next.

Then generate the curl command:

kitaru flow deployments curl research_agent \
  --tag stable \
  --input '{"topic": "consumer request"}'

The output is copy-pasteable and uses your active connection's server URL:

# Resolved research_agent tag 'stable' to deployment version v2.
# This command is pinned to v2. Regenerate it if you move the tag.

KITARU_SERVER_ACCESS_TOKEN="$(kitaru auth token)"

curl -sS -X POST \
  'https://kitaru.example.com/api/v1/pipeline_snapshots/7d3176d6-7453-411b-a3f5-91ca5c663d1c/runs' \
  -H "Authorization: Bearer ${KITARU_SERVER_ACCESS_TOKEN}" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{"run_configuration":{"parameters":{"topic":"consumer request"}}}'

Kitaru resolves research_agent plus stable before printing the command. That means the generated URL is pinned to the resolved deployment version. If the producer later moves stable to another version, regenerate the command.

kitaru auth token exchanges your active Kitaru connection for a short-lived server bearer token. The generated curl snippet stores it in KITARU_SERVER_ACCESS_TOKEN for the current shell command only; the curl generator itself does not print or store the real token.

One important practical detail: deployment creation is rejected up front when the selected stack is local or otherwise not executable remotely by the Kitaru server. Use a stack the Kitaru server can execute remotely (for example Kubernetes, Vertex, SageMaker, or AzureML) for deployment routes you plan to invoke remotely.

For older deployments created before this guard existed, kitaru invoke and kitaru flow deployments curl may fail with a stack-compatibility error until you redeploy using a stack the Kitaru server can execute remotely.

The official zenmldocker/kitaru server image already enables workload-manager support for snapshot-backed invocation. If you run a custom image or a plain ZenML server setup, preserve or configure workload-manager support explicitly (e.g. set ZENML_SERVER_WORKLOAD_MANAGER_IMPLEMENTATION_SOURCE) so deployment invoke/curl routes remain executable.

For machine-readable output, add -o json:

kitaru flow deployments curl research_agent --version 2 -o json

Invoke from MCP

MCP clients use the same deployment model with structured tool calls.

Deploy a canary:

{
  "target": "flows/research.py:research_agent",
  "inputs": {"topic": "durable execution"},
  "tag": "canary",
  "exclusive": true
}

Invoke the stable route:

{
  "flow": "research_agent",
  "tag": "stable",
  "inputs": {"topic": "consumer request"}
}

Use kitaru_deployments_list and kitaru_deployments_get before invoking when the assistant needs to discover available versions or confirm where a tag points.

Auth: one workspace context, no deployment tokens

Deployments do not have their own tokens. The CLI, SDK, and MCP server all use the active Kitaru connection context.

For a local server:

kitaru login
kitaru status

For a remote workspace:

kitaru login my-workspace --api-key kat_abc123 --project production
kitaru status

For headless environments:

export KITARU_SERVER_URL=https://kitaru.example.com
export KITARU_AUTH_TOKEN=kat_abc123
export KITARU_PROJECT=production

Once that context is configured, deployment invocation uses it automatically. A consumer invokes one route by flow name plus tag/version; Kitaru resolves that route to the saved snapshot. There is no long-lived per-version service and no extra per-deployment token to pass to kitaru invoke, .invoke(), or kitaru_deployments_invoke.

A practical checklist

Before sharing a route with other people or agents:

  1. kitaru deploy ... --tag canary --exclusive for the candidate.
  2. kitaru invoke FLOW --tag canary ... and inspect the execution.
  3. Move stable or default with kitaru flow tag FLOW TAG --version N --exclusive.
  4. Tell consumers the flow name and tag, not the source file path.
  5. Use exact --version N only for reproducible one-off runs.

If you remember just one thing: deploys create Kitaru-managed saved versions; tags are the movable signposts consumers follow.

On this page