Skip to content

Latest commit

 

History

History
2095 lines (1540 loc) · 66.7 KB

File metadata and controls

2095 lines (1540 loc) · 66.7 KB

Manta HTTP API Reference

Documentation version: this reference describes the HTTP API shipped with manta 2.0.0. The v1 series did not have a server binary, so there is no v1 equivalent of this document; for older v2 betas, browse the repository at the matching git tag.

The manta HTTP server (manta-server binary) exposes a REST + WebSocket API. The default port and TLS material come from ~/.config/manta/server.toml (see README.md); the canonical port is 8443 and TLS is required by default. The server fails closed without cert + key — pass --allow-http (or set [server] allow_http = true) to opt into plain-HTTP listen mode for local testing.

TL;DR

  • Base URL: https://<host>:8443/v2
  • Test-environment shortcut: manta-server --allow-http --port 8080 starts the server on plain HTTP without needing any cert/key material. Use only against localhost or behind an upstream TLS terminator — bearer tokens travel in cleartext otherwise. The flag also has a config-file equivalent, [server] allow_http = true.
  • Auth: every request needs X-Manta-Site: <site> + Authorization: Bearer <token>, except for /health, /openapi.json, /docs, and /v2/auth/*.
  • Bootstrap a token: POST /v2/auth/token with { "username": "...", "password": "..." } → returns { "token": "..." } from the configured backend.
  • Reads / writes: standard GET / POST / PUT / DELETE per resource (sessions, configurations, nodes, groups, images, templates, boot/kernel parameters, redfish endpoints, hardware, group inventory, migrations, SAT files, power, ephemeral envs).
  • Streaming: SSE for CFS session logs (GET /sessions/{name}/logs); WebSocket upgrades for interactive consoles (/nodes/{xname}/console, /sessions/{name}/console).
  • Errors: uniform JSON { "error": "..." } body with conventional HTTP status codes; see the table below.
  • Interactive exploration: https://<host>:8443/docs (Swagger UI loads the spec from /openapi.json).

Starting the server

manta-server [--port 8443] [--listen-address 0.0.0.0] [--cert <cert.pem>] [--key <key.pem>] [--allow-http]

Each flag overrides the corresponding [server] field in server.toml for that invocation. The CLI does not ship a serve subcommand — manta-server is its own binary.

--allow-http is the only flag without a TLS-on counterpart: by default the server refuses to start when no cert/key is configured, so bearer tokens cannot accidentally land on the wire in cleartext. Pass it (or set [server] allow_http = true) for localhost smoke tests or for deployments where TLS terminates at an upstream proxy/sidecar.

Required headers

Every endpoint requires two headers:

Header Description
X-Manta-Site Site name as configured in server.toml [sites.X] (e.g. cscs_prod)
Authorization Bearer <shasta-token> — not required for /health, /openapi.json, /docs, or /v2/auth/*
X-Manta-Site: cscs_prod
Authorization: Bearer <shasta-token>

Base URL

https://<host>:8443/v2

Every endpoint section below includes a ready-to-paste curl invocation that uses the MANTA_HOST, MANTA_SITE, and MANTA_TOKEN shell variables defined in Reusable shell vars. Set those once and every example below works without further substitution.

Error responses

All errors return JSON with an error field:

{ "error": "description of what went wrong" }
Status Meaning
400 Bad request — invalid parameters or body
401 Missing or malformed Authorization header
404 Resource not found
409 Conflict — resource already exists
422 Unprocessable entity — required field missing or wrong type
500 Backend call failed
501 Feature requires per-site Vault / Kubernetes config not set (see Server configuration requirements)

Sessions

GET /sessions

List CFS sessions, optionally filtered.

Query parameters

Name Type Required Description
hsm_group string no Filter by HSM group name
xnames string no Comma-separated xnames to filter by
min_age string no Minimum session age (e.g. 1h, 2d)
max_age string no Maximum session age
session_type string no image or runtime
status string no pending, running, complete
name string no Exact session name
limit u8 no Maximum number of results

Response 200 — array of CFS session objects.

curl -k "$MANTA_HOST/v2/sessions?hsm_group=compute&limit=5" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /sessions

Create a CFS configuration and session from one or more git repositories.

Requires per-site Vault config (see Server configuration requirements). Vault is used to fetch the Gitea token.

Request body

{
  "repo_names": ["csm-config"],
  "repo_last_commit_ids": ["abc123def456"],
  "cfs_conf_sess_name": "my-session",
  "playbook_yaml_file_name": "site.yaml",
  "hsm_group": "compute",
  "ansible_limit": "x3000c0s1b0n0",
  "ansible_verbosity": "1",
  "ansible_passthrough": ""
}
Field Type Required Description
repo_names string[] yes Gitea repository names
repo_last_commit_ids string[] yes Commit SHA for each repo (same order)
cfs_conf_sess_name string no Name for the config and session (auto-generated if omitted)
playbook_yaml_file_name string no Ansible playbook file (default: site.yaml)
hsm_group string no Target HSM group
ansible_limit string no Comma-separated xnames (or NIDs / hostlist) to limit execution. Group names are not accepted — pre-resolve them client-side via GET /groups?name=….
ansible_verbosity string no Ansible verbosity flag (e.g. -v, -vvv) — forwarded verbatim to ansible-playbook
ansible_passthrough string no Extra arguments passed to ansible-playbook

Response 201

{
  "session_name": "my-session-20240101",
  "configuration_name": "my-session-20240101-config"
}
curl -k -X POST "$MANTA_HOST/v2/sessions" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "repo_names": ["csm-config"],
    "repo_last_commit_ids": ["abc123def456"],
    "hsm_group": "compute"
  }'

DELETE /sessions/{name}

Delete and cancel a CFS session.

Path parameters: name — session name.

Query parameters

Name Type Required Description
dry_run bool no If true, return what would be deleted without deleting (default: false)

Response 200 — on dry run: deletion context object. On delete: { "deleted": "<name>" }.

curl -k -X DELETE "$MANTA_HOST/v2/sessions/my-session" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

GET /sessions/{name}/logs

Stream CFS session logs as Server-Sent Events.

Requires per-site Vault + Kubernetes config (see Server configuration requirements).

Path parameters: name — CFS session name.

Query parameters

Name Type Required Description
timestamps bool no Include timestamps in log lines (default: false)

Response 200 — Content-Type: text/event-stream. Each log line is delivered as an SSE data: event.

curl -kN "$MANTA_HOST/v2/sessions/my-session/logs?timestamps=true" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

-N (also --no-buffer) disables curl's output buffering so SSE events appear as they arrive.


Configurations

GET /configurations

List CFS configurations, optionally filtered.

Query parameters

Name Type Required Description
name string no Exact configuration name
pattern string no Name pattern (glob)
hsm_group string no Filter by associated HSM group
limit u8 no Maximum number of results

Response 200 — array of CFS configuration objects, each enriched with a safe_to_delete: bool field. The field is true when no CFS component currently lists this configuration as its desired_config. The check is components-only — it does not consider BSS-referenced images built from the configuration; for the broader analysis surface, use the safe_to_delete field on GET /analysis/images together with this listing.

The verdict arrives on a single response; no client-side fan-out is required. The dedicated GET /analysis/configurations endpoint was removed in the same change that inlined this field — clients that previously called it should read safe_to_delete from this listing instead. The CLI's --only-safe-to-delete / --only-unsafe-to-delete flags on manta get configurations are client-side filters over this field.

curl -k "$MANTA_HOST/v2/configurations?pattern=compute-*" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

DELETE /configurations

Delete CFS configurations and their dependent images and session templates.

Query parameters

Name Type Required Description
pattern string no Name pattern to match configurations
since string no Delete configurations created after this datetime (YYYY-MM-DDTHH:MM:SS)
until string no Delete configurations created before this datetime
dry_run bool no Preview without deleting (default: false)

Response 200

{
  "deleted_configurations": ["config-a", "config-b"],
  "deleted_images": ["img-uuid-1"]
}
curl -k -X DELETE "$MANTA_HOST/v2/configurations?pattern=old-*&dry_run=true" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Nodes

GET /nodes

Get details for one or more nodes.

Query parameters

Name Type Required Description
xname string yes Comma-separated xnames, NIDs, or hostlist expression (e.g. x3000c0s1b0n[0-3] or nid000001,nid000002)
include_siblings bool no Include sibling nodes in the same blade (default: false)
status string no Filter by power status

Response 200 — array of node objects.

curl -k "$MANTA_HOST/v2/nodes?xname=x3000c0s1b0n0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /nodes

Register a new node.

Request body

{
  "id": "x3000c0s1b0n0",
  "group": "compute",
  "enabled": true,
  "arch": "X86"
}
Field Type Required
id string yes
group string yes
enabled bool no (default: false)
arch string no

Response 201 — { "id": "<xname>" }.

curl -k -X POST "$MANTA_HOST/v2/nodes" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"id":"x3000c0s1b0n0","group":"compute","enabled":true}'

DELETE /nodes/{id}

Delete a node by xname.

Path parameters: id — node xname.

Response 204 — no content.

curl -k -X DELETE "$MANTA_HOST/v2/nodes/x3000c0s1b0n0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Groups (HSM)

GET /groups

List HSM groups.

Query parameters

Name Type Required Description
name string no Exact group name

Response 200 — array of HSM group objects.

curl -k "$MANTA_HOST/v2/groups?name=compute" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

GET /groups/available

List the names of HSM groups the authenticated token is allowed to act on. Returns the subset of GET /groups that the caller's authorization permits.

Response 200 — array of group-name strings:

["compute", "gpu-cluster"]
curl -k "$MANTA_HOST/v2/groups/available" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /groups

Create a new HSM group. Admin only — non-admin callers receive 400 BadRequest: a brand-new group label has no membership to scope against, so there's no meaningful per-group authorization to apply.

Request body — HSM group object:

{
  "label": "my-group",
  "description": "My compute nodes",
  "members": { "ids": ["x3000c0s1b0n0", "x3000c0s3b0n0"] }
}

Response 201 — { "created": true }.

Response 400 — caller is not admin (or the body is malformed).

curl -k -X POST "$MANTA_HOST/v2/groups" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "label": "my-group",
    "description": "smoke test",
    "members": { "ids": ["x3000c0s1b0n0"] }
  }'

DELETE /groups/{label}

Delete an HSM group.

Path parameters: label — group label.

Query parameters

Name Type Required Description
force bool no Skip orphan-node check (default: false)

Response 204 — no content.

curl -k -X DELETE "$MANTA_HOST/v2/groups/my-group?force=true" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /groups/{name}/members

Add nodes to an HSM group.

Path parameters: name — group name.

Request body

{ "hosts_expression": "x3000c0s[1-4]b0n0" }

Response 200

{
  "added": ["x3000c0s1b0n0", "x3000c0s2b0n0"],
  "final_members": ["x3000c0s1b0n0", "x3000c0s2b0n0", "x3000c0s3b0n0"],
  "removed": ["x3000c0s1b0n0", "x3000c0s2b0n0", "x3000c0s3b0n0"]
}

final_members vs removed — final_members carries the final, sorted membership of the group after the update. removed is a deprecated alias that holds the same value for one transition release, kept so existing clients reading the old name keep working; new clients should read final_members. removed will be dropped in the next major bump.

curl -k -X POST "$MANTA_HOST/v2/groups/compute/members" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"hosts_expression":"x3000c0s[1-4]b0n0"}'

DELETE /groups/{name}/members

Remove nodes from an HSM group.

Path parameters: name — group name.

Request body

{
  "xnames_expression": "x3000c0s1b0n0,x3000c0s2b0n0",
  "dry_run": false
}
Field Type Required Description
xnames_expression string yes Hosts expression (xnames, nids, or hostlist notation)
dry_run bool no Preview without removing (default: false)

Response 204 — no content.

curl -k -X DELETE "$MANTA_HOST/v2/groups/compute/members" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"xnames_expression":"x3000c0s1b0n0","dry_run":true}'

Templates (BOS)

GET /templates

List BOS session templates.

Query parameters

Name Type Required Description
name string no Exact template name
hsm_group string no Filter by associated HSM group
limit u8 no Maximum number of results

Response 200 — array of BOS session template objects.

curl -k "$MANTA_HOST/v2/templates?hsm_group=compute" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /templates/{name}/sessions

Create a BOS session from a named template.

Path parameters: name — BOS session template name.

Request body

{
  "operation": "reboot",
  "limit": "compute",
  "session_name": "my-reboot",
  "include_disabled": false,
  "dry_run": false
}
Field Type Required Description
operation string yes boot, reboot, or shutdown
limit string yes Comma-separated xnames or HSM group names
session_name string no Name for the BOS session (auto-generated if omitted)
include_disabled bool no Include disabled nodes (default: false)
dry_run bool no Return the session object without creating it (default: false)

Response 201 (or 200 on dry run) — BOS session object.

curl -k -X POST "$MANTA_HOST/v2/templates/my-template/sessions" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"operation":"reboot","limit":"compute"}'

Images

GET /images

List IMS images.

Query parameters

Name Type Required Description
id string no Exact image ID
pattern string no Glob pattern matched against image name (e.g. csm-*, *-1.6.2-*). globset syntax, anchored to the full name.
since string no ISO-8601 timestamp (YYYY-MM-DDTHH:MM:SS). Only images created at or after this point. Inclusive.
until string no ISO-8601 timestamp (YYYY-MM-DDTHH:MM:SS). Only images created at or before this point. Inclusive.
limit u8 no Selects the newest N (selection happens before the display flip), so limit=1 is the newest image

Response 200 — array of IMS Image objects, oldest first (newest last):

[
  {
    "id": "93b4ea2a-1234-5678-abcd-ef0123456789",
    "name": "csm-image-1.0",
    "created": "2026-01-15T14:32:11Z",
    "link": {
      "path": "s3://boot-images/93b4ea2a-.../manifest.json",
      "etag": "abc123",
      "type": "s3"
    },
    "arch": "x86_64"
  }
]

Unlike /configurations, this endpoint does not carry an inline safe_to_delete field. For the deletion-safety verdict per image, call GET /analysis/images and join on image_id. The CLI's manta get images does exactly this fan-out internally.

400 when since/until is not a full ISO-8601 timestamp, or when since is later than until. IMS itself accepts no query parameters, so pattern, since, and until are all applied by manta-server after fetching the listing. Images whose created is missing or unparseable are omitted whenever either bound is set.

curl -k "$MANTA_HOST/v2/images?pattern=^csm-image-.*" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

# Images created in January 2026
curl -k "$MANTA_HOST/v2/images?since=2026-01-01T00:00:00&until=2026-01-31T23:59:59" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

DELETE /images

Delete one or more IMS images.

Query parameters

Name Type Required Description
ids string yes Comma-separated image IDs
dry_run bool no Preview without deleting (default: false)

Response 200

{ "deleted": ["uuid-1", "uuid-2"] }
curl -k -X DELETE "$MANTA_HOST/v2/images?ids=uuid-1,uuid-2&dry_run=true" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Analysis

Deletion-safety verdicts for cluster state that has its own retention/cleanup story. Currently scoped to IMS images; CFS configurations carry their verdict inline on GET /configurations and have no separate endpoint.

GET /analysis/images

One row per IMS image visible to the active site, each with a safe_to_delete verdict. The check is BSS-only: a row is safe_to_delete: true when no BSS boot-parameter record references the image as a node's boot image, and false otherwise.

The CLI's manta get images calls this endpoint internally to overlay the verdict onto its image listing; integrators can call it directly when they want the verdict without the rest of the IMS payload.

Query parameters: none.

Response 200 — Vec<ImageAnalysisRow>, one row per IMS image:

[
  {
    "image_id": "93b4ea2a-1234-5678-abcd-ef0123456789",
    "image_name": "csm-image-1.0",
    "image_created": "2026-01-15T14:32:11Z",
    "configuration_name": "csm-config-2026",
    "safe_to_delete": true
  }
]
Field Type Description
image_id string IMS image id. Row anchor; always present.
image_name string IMS image name.
image_created string | null ISO-8601 creation timestamp; null for images without one (sink to the bottom of the sort).
configuration_name string | null CFS configuration the image was built with (Image.configuration).
safe_to_delete bool true iff no BSS boot-parameter record names this image as a node's boot image.

Rows are sorted by image_created ascending (oldest first); ties or null timestamps break by image_id ascending.

curl -k "$MANTA_HOST/v2/analysis/images" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

A sibling GET /analysis/configurations previously existed and was removed in beta.56. The safe_to_delete verdict for configurations is now inlined on every GET /configurations response — clients should read it from there.


Boot parameters

GET /boot-parameters

Get BSS boot parameters.

Query parameters

Name Type Required Description
hsm_group string no Filter by HSM group
nodes string no Comma-separated xnames

Response 200 — boot parameters object.

curl -k "$MANTA_HOST/v2/boot-parameters?nodes=x3000c0s1b0n0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /boot-parameters

Add boot parameters.

Request body — BSS BootParameters object.

Response 201 — { "created": true }.

curl -k -X POST "$MANTA_HOST/v2/boot-parameters" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "hosts": ["x3000c0s1b0n0"],
    "params": "console=ttyS0,115200n8 quiet",
    "kernel": "s3://boot-images/kernel",
    "initrd": "s3://boot-images/initrd"
  }'

PUT /boot-parameters

Update boot parameters for specified nodes.

Request body

{
  "hosts": ["x3000c0s1b0n0"],
  "params": "console=ttyS0,115200n8 quiet",
  "kernel": "s3://boot-images/kernel",
  "initrd": "s3://boot-images/initrd"
}
Field Type Required Description
hosts string[] yes Target node xnames
params string yes Kernel command-line parameters string
kernel string yes S3 path to the kernel image
initrd string yes S3 path to the initrd image
nids u32[] no Node IDs (alternative identifier)
macs string[] no MAC addresses (alternative identifier)

Response 204 — no content.

curl -k -X PUT "$MANTA_HOST/v2/boot-parameters" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "hosts": ["x3000c0s1b0n0"],
    "params": "console=ttyS0,115200n8 quiet",
    "kernel": "s3://boot-images/kernel",
    "initrd": "s3://boot-images/initrd"
  }'

DELETE /boot-parameters

Delete boot parameters for a set of nodes.

Request body

{ "hosts": ["x3000c0s1b0n0"] }

Response 204 — no content.

curl -k -X DELETE "$MANTA_HOST/v2/boot-parameters" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"hosts":["x3000c0s1b0n0"]}'

Kernel parameters

GET /kernel-parameters

Get kernel parameters for nodes.

Query parameters

Name Type Required Description
hsm_group string no Filter by HSM group
nodes string no Comma-separated xnames

Response 200 — kernel parameters object.

curl -k "$MANTA_HOST/v2/kernel-parameters?nodes=x3000c0s1b0n0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /kernel-parameters/add

Append kernel parameters to a set of nodes without replacing existing ones.

Request body

{
  "params": "console=ttyS0,115200n8",
  "xnames_expression": "x3000c0s[1-4]b0n0",
  "hsm_group": null,
  "overwrite": false,
  "project_sbps": true,
  "dry_run": false
}
Field Type Required Description
params string yes Space-separated kernel parameters to add
xnames_expression string no Hosts expression (xnames, nids, or hostlist notation); mutually exclusive with hsm_group
hsm_group string no Target HSM group; mutually exclusive with xnames_expression
overwrite bool no Overwrite a parameter if it already exists (default: false)
project_sbps bool no Project SBPS images (default: true)
dry_run bool no Preview without persisting (default: false)

Response 200

{
  "applied": true,
  "has_changes": true,
  "xnames_to_reboot": ["x3000c0s1b0n0"]
}
curl -k -X POST "$MANTA_HOST/v2/kernel-parameters/add" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "params": "console=ttyS0,115200n8",
    "hsm_group": "compute",
    "dry_run": true
  }'

POST /kernel-parameters/apply

Add, replace, or delete kernel parameters for a set of nodes.

Request body

{
  "xnames_expression": "x3000c0s[1-4]b0n0",
  "hsm_group": null,
  "operation": "add",
  "params": "console=ttyS0,115200n8",
  "overwrite": false,
  "project_sbps": true,
  "dry_run": false
}
Field Type Required Description
operation string yes add, apply (replace all), or delete
params string yes Space-separated kernel parameters
xnames_expression string no Hosts expression; mutually exclusive with hsm_group
hsm_group string no Target HSM group; mutually exclusive with xnames_expression
overwrite bool no For add: overwrite existing params (default: false)
project_sbps bool no Project SBPS images (default: true)
dry_run bool no Preview without persisting (default: false)

Response 200

{
  "applied": true,
  "has_changes": true,
  "xnames_to_reboot": ["x3000c0s1b0n0"]
}
curl -k -X POST "$MANTA_HOST/v2/kernel-parameters/apply" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "operation": "add",
    "params": "console=ttyS0,115200n8",
    "hsm_group": "compute",
    "dry_run": true
  }'

DELETE /kernel-parameters

Remove specific kernel parameters from a set of nodes.

Request body

{
  "params": "console=ttyS0,115200n8",
  "xnames_expression": "x3000c0s[1-4]b0n0",
  "hsm_group": null,
  "dry_run": false
}
Field Type Required Description
params string yes Space-separated kernel parameters to remove
xnames_expression string no Hosts expression; mutually exclusive with hsm_group
hsm_group string no Target HSM group; mutually exclusive with xnames_expression
dry_run bool no Preview without persisting (default: false)

Response 200

{
  "applied": true,
  "has_changes": true,
  "xnames_to_reboot": ["x3000c0s1b0n0"]
}
curl -k -X DELETE "$MANTA_HOST/v2/kernel-parameters" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "params": "console=ttyS0,115200n8",
    "hsm_group": "compute",
    "dry_run": true
  }'

Boot configuration

POST /boot-config

Apply a combined boot configuration (image + runtime config + kernel params) to a set of nodes.

Request body

{
  "hosts_expression": "x3000c0s[1-4]b0n0",
  "boot_image_id": "ims-image-uuid",
  "boot_image_configuration": "csm-config-1.0",
  "kernel_parameters": "console=ttyS0",
  "runtime_configuration": "csm-config-1.0",
  "dry_run": false
}
Field Type Required Description
hosts_expression string yes Hosts expression (xnames, NIDs, or hostlist notation). Group names are not accepted here; resolve them first via GET /groups?name=<group> if needed.
boot_image_id string no IMS image ID to set as boot image
boot_image_configuration string no CFS configuration to link to the boot image
kernel_parameters string no Kernel parameters to set
runtime_configuration string no CFS configuration for runtime
dry_run bool no Preview without persisting (default: false)

Response 200

{
  "applied": true,
  "nodes": ["x3000c0s1b0n0"],
  "need_restart": false
}
curl -k -X POST "$MANTA_HOST/v2/boot-config" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "hosts_expression": "x3000c0s[1-4]b0n0",
    "boot_image_id": "ims-image-uuid",
    "kernel_parameters": "console=ttyS0",
    "dry_run": true
  }'

Power management

A power flow is split across two endpoints. POST /power kicks off a PCS transition and returns immediately with the transition id; the CLI then polls GET /power/transitions/{id} every few seconds until the snapshot reports transitionStatus = "completed". Long cluster transitions therefore don't depend on any HTTP timeout — each call is a millisecond-scale round-trip.

sequenceDiagram
  autonumber
  participant CLI as manta power …
  participant Srv as manta-server
  participant PCS

  CLI->>Srv: POST /power { action, host_expression, target_type, force }
  Srv->>PCS: POST /power-control/v1/transitions
  PCS-->>Srv: 200 { transitionID }
  Srv-->>CLI: 200 { transitionID, operation }

  loop until transitionStatus == "completed"
    CLI->>Srv: GET /power/transitions/{id}
    Srv->>PCS: GET /power-control/v1/transitions/{id}
    Srv-->>CLI: 200 TransitionResponse
  end
Loading

POST /power

Start a PCS power transition (on, off, or reset) against nodes or an entire HSM group. Returns immediately with the transition id; does not block until the transition completes.

Request body

{
  "action": "reset",
  "host_expression": "x3000c0s[1-4]b0n0",
  "target_type": "nodes",
  "force": false
}
Field Type Required Description
action string yes on, off, or reset (lowercase; serde rejects anything else with 422)
host_expression string yes Hosts expression (for target_type: nodes) or HSM group name (for target_type: cluster)
target_type string yes nodes or cluster
force bool no Hard power off/reset without graceful shutdown (default: false)

The server maps (action, force) to the PCS wire-level operation: on (force ignored), soft-off / force-off, soft-restart / hard-restart.

Response 200 — PCS TransitionStartOutput:

{
  "transitionID": "abc-123",
  "operation": "Reset"
}
Field Type Description
transitionID string The PCS transition id. Feed it into GET /power/transitions/{id} to track progress.
operation string The resolved PCS operation (On, SoftOff, ForceOff, SoftRestart, HardRestart).
curl -k -X POST "$MANTA_HOST/v2/power" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "action": "reset",
    "host_expression": "x3000c0s1b0n0",
    "target_type": "nodes",
    "force": false
  }'

GET /power/transitions/{id}

Snapshot an in-flight (or completed) PCS power transition by id. The CLI polls this every 3 seconds after POST /power returns the id, until transitionStatus is "completed". Mirrors PCS's own GET /power-control/v1/transitions/{id} response shape — the server is a thin pass-through.

Path parameters: id — the transition id returned by POST /power.

Response 200 — PCS TransitionResponse:

{
  "transitionID": "abc-123",
  "createTime": "2026-05-31T12:34:56Z",
  "automaticExpirationTime": "2026-05-31T13:34:56Z",
  "transitionStatus": "in-progress",
  "operation": "Reset",
  "taskCounts": {
    "total": 16, "new": 0, "in-progress": 5,
    "failed": 0, "succeeded": 11, "un-supported": 0
  },
  "tasks": [
    { "xname": "x3000c0s1b0n0", "taskStatus": "succeeded", "taskStatusDescription": "Transition complete", "error": null }
  ]
}

Response 404 — unknown transition id (the body is whatever PCS returned).

curl -k "$MANTA_HOST/v2/power/transitions/abc-123" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Redfish endpoints

GET /redfish-endpoints

List Redfish endpoints.

Query parameters

Name Type Required Description
id string no Filter by ID
fqdn string no Filter by FQDN
uuid string no Filter by UUID
macaddr string no Filter by MAC address
ipaddress string no Filter by IP address

Response 200 — array of Redfish endpoint objects.

curl -k "$MANTA_HOST/v2/redfish-endpoints?id=x3000c0s1b0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST /redfish-endpoints

Add a Redfish endpoint.

Request body — Redfish endpoint parameters object.

Response 201 — { "created": true }.

curl -k -X POST "$MANTA_HOST/v2/redfish-endpoints" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "ID": "x3000c0s1b0",
    "FQDN": "x3000c0s1b0",
    "Hostname": "x3000c0s1b0",
    "Enabled": true,
    "User": "root",
    "Password": "***"
  }'

PUT /redfish-endpoints

Update a Redfish endpoint.

Request body — Redfish endpoint parameters object.

Response 204 — no content.

curl -k -X PUT "$MANTA_HOST/v2/redfish-endpoints" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "ID": "x3000c0s1b0",
    "Enabled": false
  }'

DELETE /redfish-endpoints/{id}

Delete a Redfish endpoint.

Path parameters: id — endpoint ID.

Response 204 — no content.

curl -k -X DELETE "$MANTA_HOST/v2/redfish-endpoints/x3000c0s1b0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Group inventory

GET /groups/nodes

Get node details for an HSM group with optional power-status filtering.

Query parameters

Name Type Required Description
hsm_group string no HSM group name. When omitted the response covers every group the bearer token can access.
status string no Filter by node power status (ON, OFF, READY, …)

Response 200 — array of node-detail objects.

curl -k "$MANTA_HOST/v2/groups/nodes?hsm_group=compute&status=ON" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

GET /groups/hardware

Get a hardware component summary per node for an HSM group.

Query parameters

Name Type Required Description
hsm_group string no HSM group name. When omitted the response covers every group the bearer token can access.

Response 200 — object with hsm_group_name and node_summaries.

curl -k "$MANTA_HOST/v2/groups/hardware?hsm_group=compute" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

GET /clusters (deprecated)

Old alias for GET /groups/nodes. Same query parameters, same response. Continues to work for one release; the server logs a warning on every request. Drop in the next major release.

curl -k "$MANTA_HOST/v2/clusters?hsm_group=compute" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

GET /hardware-clusters (deprecated)

Old alias for GET /groups/hardware. Same query parameters, same response. Continues to work for one release; the server logs a warning on every request. Drop in the next major release.

curl -k "$MANTA_HOST/v2/hardware-clusters?hsm_group=compute" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

GET /hardware-nodes-list

Get hardware component details for specific nodes.

Query parameters

Name Type Required Description
xnames string yes Comma-separated xnames

Response 200 — object with a node_summaries array.

curl -k "$MANTA_HOST/v2/hardware-nodes-list?xnames=x3000c0s1b0n0,x3000c0s2b0n0" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Hardware-component group management

The write endpoints in this section still use /hardware-clusters/{target}/... in the URL and parent_cluster / target_cluster as JSON field names. A parallel rename to /groups/{target}/hardware/... + parent_group / target_group is planned for a future release and will mirror the deprecation pattern used by GET /clusters / GET /hardware-clusters above. Existing client code keeps working in the meantime.

POST /hardware-clusters/{target}/members

Add hardware components (nodes) to a target cluster, sourcing them from a parent cluster.

Path parameters: target — destination HSM group name.

Request body

{
  "parent_cluster": "nodes_free",
  "pattern": "Memory=512:Processors=2",
  "create_hsm_group": false,
  "dry_run": false
}
Field Type Required Description
parent_cluster string yes Source HSM group to draw nodes from
pattern string yes Hardware component pattern to match (e.g. Memory=512:Processors=2)
create_hsm_group bool no Create the target group if it doesn't exist (default: false)
dry_run bool no Preview without moving nodes (default: false)

Response 200

{
  "dry_run": false,
  "nodes_moved": ["x3000c0s1b0n0"],
  "target_cluster": "my-cluster",
  "target_nodes": ["x3000c0s1b0n0"],
  "parent_cluster": "nodes_free",
  "parent_nodes": ["x3000c0s2b0n0"]
}
curl -k -X POST "$MANTA_HOST/v2/hardware-clusters/my-cluster/members" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "parent_cluster": "nodes_free",
    "pattern": "Memory=512:Processors=2",
    "dry_run": true
  }'

DELETE /hardware-clusters/{target}/members

Remove hardware components from a target cluster and return them to a parent cluster.

Path parameters: target — source HSM group name.

Request body

{
  "parent_cluster": "nodes_free",
  "pattern": "Memory=512:Processors=2",
  "delete_hsm_group": false,
  "dry_run": false
}
Field Type Required Description
parent_cluster string yes Destination HSM group for returned nodes
pattern string yes Hardware component pattern to match
delete_hsm_group bool no Delete the target group if it becomes empty (default: false)
dry_run bool no Preview without moving nodes (default: false)

Response 200 — same shape as POST /hardware-clusters/{target}/members.

curl -k -X DELETE "$MANTA_HOST/v2/hardware-clusters/my-cluster/members" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "parent_cluster": "nodes_free",
    "pattern": "Memory=512:Processors=2",
    "dry_run": true
  }'

POST /hardware-clusters/{target}/configuration

Pin or unpin a hardware cluster configuration by moving nodes between the target and parent cluster according to a hardware component pattern.

Path parameters: target — target HSM group name.

Request body

{
  "parent_cluster": "nodes_free",
  "pattern": "Memory=512:Processors=2",
  "mode": "pin",
  "create_target_hsm_group": true,
  "delete_empty_parent_hsm_group": true,
  "dry_run": false
}
Field Type Required Description
parent_cluster string yes Source/destination HSM group
pattern string yes Hardware component pattern to match
mode string no "pin" (move nodes into target) or "unpin" (move nodes back to parent) — default: "pin"
create_target_hsm_group bool no Create the target group if it doesn't exist (default: true)
delete_empty_parent_hsm_group bool no Delete the parent group if it becomes empty (default: true)
dry_run bool no Preview without moving nodes (default: false)

Response 200

{
  "dry_run": false,
  "target_cluster": "my-cluster",
  "target_nodes": ["x3000c0s1b0n0"],
  "parent_cluster": "nodes_free",
  "parent_nodes": []
}
curl -k -X POST "$MANTA_HOST/v2/hardware-clusters/my-cluster/configuration" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "parent_cluster": "nodes_free",
    "pattern": "Memory=512:Processors=2",
    "mode": "pin",
    "dry_run": true
  }'

Migration

POST /migrate/nodes

Move nodes between HSM groups (vClusters).

Request body

{
  "target_hsm_names": ["target-cluster"],
  "parent_hsm_names": ["parent-cluster"],
  "hosts_expression": "x3000c0s[1-4]b0n0",
  "dry_run": false,
  "create_hsm_group": false
}
Field Type Required Description
target_hsm_names string[] yes Destination HSM group(s)
parent_hsm_names string[] yes Source HSM group(s)
hosts_expression string yes Nodes to migrate
dry_run bool no Preview without migrating (default: false)
create_hsm_group bool no Create the target group if it doesn't exist (default: false)

Response 200 — migration results object.

curl -k -X POST "$MANTA_HOST/v2/migrate/nodes" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "target_hsm_names": ["target-cluster"],
    "parent_hsm_names": ["parent-cluster"],
    "hosts_expression": "x3000c0s[1-4]b0n0",
    "dry_run": true
  }'

POST /migrate/backup

Back up vCluster configuration to files. Admin only.

Server-side filesystem confinement — both /migrate/* endpoints require the operator to configure [server] migrate_backup_root in server.toml (an absolute directory). When unset, the endpoints reject every request with 400 BadRequest even for admins. When set, every destination / *_file / image_dir path in the body is canonicalised and rejected with 400 BadRequest if it resolves outside that root.

Request body

{
  "bos": "my-cluster",
  "destination": "/var/lib/manta/migrate/cluster"
}
Field Type Required Description
bos string no BOS session-template name (or filter) to back up; omit to back up every template
destination string no Absolute filesystem path under migrate_backup_root where backup files are written; omit to use the backend default

Response 200 — { "completed": true }.

curl -k -X POST "$MANTA_HOST/v2/migrate/backup" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"bos":"my-cluster","destination":"/backups/cluster"}'

POST /migrate/restore

Restore a vCluster from backup files. Admin only, and subject to the same [server] migrate_backup_root confinement as POST /migrate/backup — every path in the body must resolve under that root or the request is rejected with 400 BadRequest.

Request body

{
  "bos_file": "/var/lib/manta/migrate/bos.yaml",
  "cfs_file": "/var/lib/manta/migrate/cfs.yaml",
  "hsm_file": "/var/lib/manta/migrate/hsm.yaml",
  "ims_file": "/var/lib/manta/migrate/ims.yaml",
  "image_dir": "/var/lib/manta/migrate/images",
  "overwrite": false
}

All fields optional. overwrite defaults to false.

Response 200 — { "completed": true }.

curl -k -X POST "$MANTA_HOST/v2/migrate/restore" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "bos_file": "/var/lib/manta/migrate/bos.yaml",
    "cfs_file": "/var/lib/manta/migrate/cfs.yaml",
    "hsm_file": "/var/lib/manta/migrate/hsm.yaml"
  }'

Ephemeral environments

POST /ephemeral-env

Create an ephemeral CFS environment from an existing image.

Request body

{ "image_id": "ims-image-uuid" }

Response 201 — { "hostname": "<allocated-hostname>" }.

curl -k -X POST "$MANTA_HOST/v2/ephemeral-env" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"image_id":"ims-image-uuid"}'

SAT files

The SAT (Shasta Artifact Template) workflow is split across per-element endpoints. manta apply sat-file walks the parsed SAT file into an ordered execution plan on the client side and dispatches one HTTP call per artifact:

  • POST /sat-file/configurations — one per configurations[] entry
  • POST /sat-file/images/cfs-session — start the CFS session for one images[] entry (CLI then monitors via GET /sessions?name=… or GET /sessions/{name}/logs)
  • POST /sat-file/images/stamp — once the session is terminal-complete, stamp the produced IMS image with provenance metadata
  • POST /sat-file/session-templates — one per session_templates[] entry

There is no whole-file POST /sat-file apply endpoint, and no pre-flight validation endpoint either (POST /sat-file/validate was removed in f9160d26). SAT files with a hardware: section are not supported by the current apply flow.

All SAT endpoints require per-site Vault + Kubernetes config (see Server configuration requirements).

Client responsibility. Jinja2 rendering, parsing the rendered YAML into a structured value, the image_only / session_template_only filters (drop top-level sections + prune unreferenced configurations/images), the topological sort of images by base.image_ref, and validation of in-file cross-references (no dangling image_ref, no cycles) all run client-side. The CLI also owns the image-build monitor loop (polling or log streaming). The server accepts each SAT entry as a serde_json::Value, threads it through the backend's per-element SatTrait method, and returns the created wire artifact. The canonical SAT schema lives in csm-rs — neither the CLI nor the server embed it.

sequenceDiagram
  autonumber
  participant CLI as manta apply sat-file
  participant Srv as manta-server
  participant Vault
  participant BE as Backend (csm-rs)

  CLI->>CLI: render Jinja2 → parse to Value → filter → build plan (topo-sorted)
  CLI-->>CLI: preview + confirm

  loop per Configuration element
    CLI->>Srv: POST /sat-file/configurations { configuration, flags }
    Srv->>Vault: fetch gitea_token + k8s secrets
    Srv->>BE: SatTrait::apply_configuration
    Srv-->>CLI: 200 CfsConfigurationResponse
  end

  loop per Image element (in dependency order)
    CLI->>Srv: POST /sat-file/images/cfs-session { image, ref_lookup, flags }
    Srv->>BE: SatTrait::apply_sat_image_create_session
    Srv-->>CLI: 201 CfsSessionGetResponse (name, status: pending/running)

    alt --watch-logs
      CLI->>Srv: GET /sessions/{name}/logs (SSE)
      Srv-->>CLI: log lines stream until pod terminates
    else default
      loop until terminal status
        CLI->>Srv: GET /sessions?name={name}
        Srv-->>CLI: 200 [CfsSessionGetResponse]
        CLI-->>CLI: sleep 10s
      end
    end

    CLI->>Srv: POST /sat-file/images/stamp { cfs_session_name }
    Srv->>BE: SatTrait::apply_sat_image_stamp_from_session
    Srv-->>CLI: 200 Image (id captured into CLI's ref_lookup; manta.image_session.* now patched)
  end

  loop per SessionTemplate element
    CLI->>Srv: POST /sat-file/session-templates { session_template, ref_lookup, flags }
    Srv->>BE: SatTrait::apply_session_template
    Srv-->>CLI: 200 { template, session? }
  end

  CLI-->>CLI: assemble { configurations, images, session_templates, bos_sessions }
Loading

POST /sat-file/configurations

Apply one entry from the SAT file's configurations section. csm-rs validates the entry against live CSM state, resolves any product: layer via the cray-product-catalog ConfigMap and any branch: via Gitea, posts the resulting CfsConfigurationRequest, and returns the created CFS configuration.

Request body

{
  "configuration": { "name": "cfg-v1", "layers": [/* ... */] },
  "overwrite": false,
  "dry_run": false
}
Field Type Required Description
configuration object yes One SAT configurations[] entry.
overwrite bool no Replace an existing CFS configuration of the same name (default: false).
dry_run bool no Validate without creating; response carries a mock CfsConfigurationResponse with the name set (default: false).

Response 200 — the created CfsConfigurationResponse.

curl -k -X POST "$MANTA_HOST/v2/sat-file/configurations" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "configuration": { "name": "cfg-v1", "layers": [] },
    "dry_run": true
  }'

POST /sat-file/images/cfs-session

Translate one SAT images[] entry into a CFS session and create it. This is the first of the three CLI-driven steps that replace the monolithic POST /sat-file/images: the server does the SAT-image → CFS-session translation and POSTs the session, but does not wait for it to finish or stamp the result. The CLI drives monitor + stamp itself.

The body includes the CLI's accumulated ref_lookup so the backend can resolve base.image_ref chains.

Request body

{
  "image": { "name": "img-v1", "ref_name": "base", "base": {/* ... */}, "configuration": "cfg-v1" },
  "ref_lookup": { "earlier-ref": "<image-id>" },
  "ansible_verbosity": 0,
  "ansible_passthrough": null,
  "dry_run": false
}
Field Type Required Description
image object yes One SAT images[] entry.
ref_lookup object no ref_name → image_id for images created earlier in the apply (default: empty).
ansible_verbosity u8 no Ansible verbosity level 0–4 for the CFS session that builds the image.
ansible_passthrough string no Extra arguments passed to ansible-playbook.
dry_run bool no Validate without creating; response is a mocked complete session with a DRYRUN-<uuid> result_id (default: false).

Response 201 — the freshly-created CfsSessionGetResponse (same wire type as GET /sessions). status.session.status will be pending or running; the CLI drives it to completion via GET /sessions?name=… or GET /sessions/{name}/logs.

curl -k -X POST "$MANTA_HOST/v2/sat-file/images/cfs-session" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "image": { "name": "img-v1", "configuration": "cfg-v1" },
    "ref_lookup": {},
    "dry_run": true
  }'

POST /sat-file/images/stamp

Given a CFS session name, the server fetches the session, derives manta.image_session.{base,groups,configuration} from it, and PATCHes them onto the IMS image the session produced. This is the third CLI-driven step (the second is the monitor loop on GET /sessions?name=… or GET /sessions/{name}/logs).

Fails fast with 400 Bad Request when the named session has no result_id — i.e. produced no image; no PATCH is attempted in that case.

Request body

{ "cfs_session_name": "sat-img-v1" }
Field Type Required Description
cfs_session_name string yes Name of the (terminal-complete) CFS session whose result image should be stamped.

Response 200 — the patched IMS Image (with metadata.manta.image_session.{base,groups,configuration} set).

curl -k -X POST "$MANTA_HOST/v2/sat-file/images/stamp" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "cfs_session_name": "sat-img-v1" }'

POST /sat-file/session-templates

Apply one entry from the SAT file's session_templates section. The CLI's ref_lookup is used to resolve image.image_ref. If create_bos_session=true (and not dry_run), a BOS session is also created to boot the targeted nodes through the template (typically a reboot).

Request body

{
  "session_template": { "name": "st-1", "image": { "image_ref": "base" }, "configuration": "cfg-v1", "bos_parameters": {/* ... */} },
  "ref_lookup": { "base": "<image-id>" },
  "create_bos_session": false,
  "dry_run": false
}
Field Type Required Description
session_template object yes One SAT session_templates[] entry.
ref_lookup object no ref_name → image_id for images created earlier in the apply (default: empty).
create_bos_session bool no After the template is created, create a BOS session from it so its target nodes boot via the new template — typically a reboot (default: false).
dry_run bool no Validate without creating; the response carries a mock template. When create_bos_session is also true, the response additionally carries a mock BOS session with status: null and name prefixed dry-run-, so the client can preview what session would have been created. Otherwise session is null.

Response 200

{
  "template": { /* BosSessionTemplate, ... */ },
  "session":  { /* BosSession, ... */ }
}

session is null when create_bos_session=false, and also when dry_run=true without create_bos_session. The dry-run + create_bos_session combination yields a mock session (no status, dry-run-<template-name>) — useful for previewing the boot that would follow without persisting anything.

curl -k -X POST "$MANTA_HOST/v2/sat-file/session-templates" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "session_template": { "name": "st-1", "configuration": "cfg-v1" },
    "ref_lookup": {},
    "create_bos_session": false,
    "dry_run": true
  }'

Authentication

The CLI obtains a bearer token by exchanging Keycloak credentials through the server. These endpoints do not themselves require an Authorization header (they're the bootstrap), but they do require X-Manta-Site so the server can pick the right backend. They sit under /v2/auth/* behind a per-source-IP rate limiter ([server].auth_rate_limit_per_minute, default 60) and a body-redaction logging layer.

POST /auth/token

Exchange username + password for a backend bearer token.

Request body

{ "username": "alice", "password": "..." }

Response 200

{ "token": "<backend-bearer-token>" }

Response 401 — { "error": "invalid credentials" }. The body is intentionally generic regardless of whether the user was unknown or the password was wrong; detail is kept in server-side logs only.

curl -k -X POST "$MANTA_HOST/v2/auth/token" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"..."}'

POST /auth/validate

Check whether a bearer token is still accepted by the backend.

Request body

{ "token": "<backend-bearer-token>" }

Response 200 — no body. The token is currently valid.

Response 401 — { "error": "invalid credentials" }. The token is missing, malformed, or rejected by the backend.

curl -k -X POST "$MANTA_HOST/v2/auth/validate" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H 'Content-Type: application/json' \
  -d "{\"token\":\"$MANTA_TOKEN\"}"

Interactive consoles (WebSocket)

Both console endpoints use the standard WebSocket upgrade handshake. Authentication is checked during the HTTP upgrade — a missing or invalid Authorization header returns 401 before the WebSocket connection is established. The server closes the connection after 30 minutes of inactivity (configurable via [server].console_inactivity_timeout_secs; inactivity tracks the client side only — server-side log scroll does not reset the timer).

Once connected, the WebSocket carries raw terminal I/O:

  • Text or binary frames sent by the client are forwarded as stdin to the console.
  • Binary frames sent by the server are stdout from the console.
  • Text frames matching {"type":"resize","cols":N,"rows":N} are consumed silently (dynamic resize is not yet supported by the backend trait).

curl cannot upgrade to a WebSocket; use websocat or wscat. The WSS host is the same as $MANTA_HOST with the https:// scheme swapped for wss://.

websocat -k \
  --header "X-Manta-Site: $MANTA_SITE" \
  --header "Authorization: Bearer $MANTA_TOKEN" \
  "${MANTA_HOST/https:/wss:}/v2/nodes/x3000c0s1b0n0/console"

WS /nodes/{xname}/console

Open an interactive console to a node.

Requires per-site Vault + Kubernetes config (see Server configuration requirements).

Path parameters: xname — node xname (e.g. x3000c0s1b0n0).

Query parameters

Name Type Required Description
cols u16 no Terminal width in columns (default: 80)
rows u16 no Terminal height in rows (default: 24)

Upgrade response 101 — WebSocket connection established.

websocat -k \
  --header "X-Manta-Site: $MANTA_SITE" \
  --header "Authorization: Bearer $MANTA_TOKEN" \
  "${MANTA_HOST/https:/wss:}/v2/nodes/x3000c0s1b0n0/console?cols=160&rows=48"

WS /sessions/{name}/console

Open an interactive console to the Ansible container of a running image-type CFS session.

Requires per-site Vault + Kubernetes config (see Server configuration requirements). The session must exist, be of type image, and have status running. Returns 409 if those conditions are not met.

Path parameters: name — CFS session name.

Query parameters

Name Type Required Description
cols u16 no Terminal width in columns (default: 80)
rows u16 no Terminal height in rows (default: 24)

Upgrade response 101 — WebSocket connection established.

websocat -k \
  --header "X-Manta-Site: $MANTA_SITE" \
  --header "Authorization: Bearer $MANTA_TOKEN" \
  "${MANTA_HOST/https:/wss:}/v2/sessions/my-session/console"

Health check

GET /health

Returns server health. Does not require authentication.

Response 200 — { "status": "ok" }.

curl -k "$MANTA_HOST/health"

API documentation

GET /openapi.json

Returns the OpenAPI 3.0 specification for the manta API as JSON. Does not require authentication.

Response 200 — OpenAPI 3.0 document.

curl -k "$MANTA_HOST/openapi.json" | jq .info

GET /docs

Serves the Swagger UI, pre-configured to load the spec from /openapi.json. Does not require authentication. Open in a browser to browse and try out all endpoints interactively.

# Verify the docs page is reachable; open $MANTA_HOST/docs in a browser to use it.
curl -kI "$MANTA_HOST/docs"

Server configuration requirements

Some endpoints require per-site Vault and Kubernetes settings in ~/.config/manta/server.toml. The relevant keys are nested under the selected [sites.X]:

[sites.alps.k8s]
api_url = "https://10.0.0.10:6443"

[sites.alps.k8s.authentication.vault]
base_url = "https://vault.example.com:8200"

When either is missing for the active site, the affected endpoints return 501 Not Implemented with an explanatory error body.

Required site config Used by
[sites.X.k8s.authentication.vault].base_url POST /sessions, GET /sessions/{name}/logs, POST /sat-file/configurations, POST /sat-file/images/cfs-session, WS /nodes/{xname}/console, WS /sessions/{name}/console
[sites.X.k8s].api_url GET /sessions/{name}/logs, POST /sat-file/configurations, POST /sat-file/images/cfs-session, WS /nodes/{xname}/console, WS /sessions/{name}/console

Troubleshooting

If a request fails before reaching the service layer, you'll get one of the codes below. Match the response code against the table, then re-issue with a corrected curl. Bumping the server's log filter to debug (log = "debug" in server.toml, then restart) makes it obvious which extractor rejected.

Status Most common cause
400 Bad Request Missing/malformed X-Manta-Site header, missing JSON body, or body not parseable as the declared request_body type.
401 Unauthorized No Authorization: Bearer … (on a protected endpoint), token expired, or /auth/token credentials rejected by the backend.
404 Not Found Wrong URL path or the resource ID does not exist for the active site.
405 Method Not Allowed Sent GET to a POST-only endpoint (or vice versa) — curl defaults to GET when -X is omitted.
408 Request Timeout The handler took longer than [server].request_timeout_secs (default 600, i.e. 10 min — bumped from 300 in beta.55 after large multi-site fetches consistently grazed the 5-min ceiling). Most endpoints return well under a second; the 10-min ceiling exists for the few operations that legitimately fan out across the upstream backend (large bulk CFS component fetches, SAT-file applies, migrate-restore re-hydrations). POST /power returns immediately with the PCS transition id and the CLI polls GET /power/transitions/{id} for completion, so 408 there indicates an unhealthy backend.
429 Too Many Requests Per-source-IP rate limit on /v2/auth/*. Tune [server].auth_rate_limit_per_minute or wait one minute.
500 Internal Server Error Server-side failure (backend unreachable, bad config). Check journalctl -u manta-server (or wherever the server's stderr is logged) for the actual cause.
501 Not Implemented The endpoint needs Vault or Kubernetes settings that the active site does not provide — see Server configuration requirements.

If curl can't connect at all (Connection refused, TLS handshake failure), the issue is below the HTTP layer: check the server is running on the port you're hitting, that https:// matches your cert/key configuration, and that any reverse proxy in front is healthy.

Reusable shell vars

The recipes below assume:

export MANTA_HOST=https://localhost:8443
export MANTA_SITE=alps
export MANTA_TOKEN=...   # see "Bootstrap a token" below

For a local server running plain HTTP (no cert/key configured), use MANTA_HOST=http://localhost:8443 and drop the -k flag.

Health & docs (no auth)

curl -k "$MANTA_HOST/health"
curl -k "$MANTA_HOST/openapi.json" | jq .info
# Open in browser: $MANTA_HOST/docs

Bootstrap a token

curl -k -X POST "$MANTA_HOST/v2/auth/token" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H 'Content-Type: application/json' \
  -d '{"username":"<user>","password":"<pass>"}'
# → { "token": "..." }

Then export it for the recipes below:

export MANTA_TOKEN=$(curl -ks -X POST "$MANTA_HOST/v2/auth/token" \
  -H "X-Manta-Site: $MANTA_SITE" -H 'Content-Type: application/json' \
  -d '{"username":"<user>","password":"<pass>"}' | jq -r .token)

Validate a token

curl -k -X POST "$MANTA_HOST/v2/auth/validate" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H 'Content-Type: application/json' \
  -d "{\"token\":\"$MANTA_TOKEN\"}"
# 200 OK = valid, 401 = rejected

GET a resource

curl -k "$MANTA_HOST/v2/sessions" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

POST a resource

curl -k -X POST "$MANTA_HOST/v2/groups" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"label":"test-group","description":"smoke test"}'

DELETE a resource

curl -k -X DELETE "$MANTA_HOST/v2/groups/test-group" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

Stream CFS session logs (SSE)

curl -kN "$MANTA_HOST/v2/sessions/<session-name>/logs?timestamps=true" \
  -H "X-Manta-Site: $MANTA_SITE" \
  -H "Authorization: Bearer $MANTA_TOKEN"

-N disables curl's output buffering so SSE events appear as they arrive.

Attach to an interactive console (WebSocket)

curl cannot upgrade to a WebSocket; use websocat or wscat:

websocat -k --header "X-Manta-Site: $MANTA_SITE" \
  --header "Authorization: Bearer $MANTA_TOKEN" \
  "wss://localhost:8443/v2/nodes/<xname>/console"

When -vvv still doesn't explain it

  1. Bump the server log: log = "debug" in server.toml, restart.
  2. Re-issue the request; the server now logs which extractor rejected (site lookup vs. JSON parse vs. backend call) and the round-trip into csm-rs/ochami-rs.
  3. For auth failures, the server logs the user/site/source-IP and the backend error message — the client only sees a generic invalid credentials 401 on purpose.