# check proxy access Source: https://docs.ankra.ai/api-reference/access-gate/check-proxy-access https://platform.ankra.app/openapi.json post /api/v1/access/check Validate the host-bound opaque session and check live organisation membership, SSO verification and exact organisation-wide role grants. # exchange proxy access Source: https://docs.ankra.ai/api-reference/access-gate/exchange-proxy-access https://platform.ankra.app/openapi.json post /api/v1/access/exchange Consume a one-time sign-in code with its verifier and current policy. Current organisation membership and roles are checked again. # Sign in to an organisation service using the Platform session and SSO Source: https://docs.ankra.ai/api-reference/access-gate/sign-in-to-an-organisation-service-using-the-platform-session-and-sso https://platform.ankra.app/openapi.json get /oauth/access/authorize Require the existing personal Platform browser session and MFA, then authorise against the organisation registered by the trusted relay. # start proxy access Source: https://docs.ankra.ai/api-reference/access-gate/start-proxy-access https://platform.ankra.app/openapi.json post /api/v1/access/requests Register an exact-host private service sign-in using a dedicated trusted relay credential. The browser cannot supply the policy or callback host. # Create an API token Source: https://docs.ankra.ai/api-reference/account-tokens/create-an-api-token https://platform.ankra.app/openapi.json post /api/v1/org/account/tokens Creates an API token for the calling user in the current organisation. The token value is returned only in this response; `expires_at` defaults to 90 days from now, and an empty `scopes` list creates a token without scope restriction. Returns 409 when a token with the same name already exists. # Delete an API token Source: https://docs.ankra.ai/api-reference/account-tokens/delete-an-api-token https://platform.ankra.app/openapi.json delete /api/v1/org/account/tokens/{token_id} Deletes one of the calling user's API tokens. The token must be revoked first (409 otherwise); returns 404 when it does not exist. # Exchange a GitHub Actions identity token Source: https://docs.ankra.ai/api-reference/account-tokens/exchange-a-github-actions-identity-token https://platform.ankra.app/openapi.json post /api/v1/oidc/github/token Exchanges a verified GitHub Actions identity token matching an organisation trust policy for a short-lived Ankra token. The subject token is the credential; no Ankra authentication is required. Repository IDs, issuer, audience and policy restrictions are verified. Rate limited per client and organisation. # List API tokens Source: https://docs.ankra.ai/api-reference/account-tokens/list-api-tokens https://platform.ankra.app/openapi.json get /api/v1/org/account/tokens Lists the calling user's API tokens in the current organisation with name, scopes, expiry, creation, last-used and revocation times. The token secret is never returned. # Renew an API token Source: https://docs.ankra.ai/api-reference/account-tokens/renew-an-api-token https://platform.ankra.app/openapi.json post /api/v1/org/account/tokens/{token_id}/rotate Mints a replacement for one of the calling user's API tokens with the same name, scopes, project pin and lifetime, returns the new value once, and keeps the old token valid for overlap_hours (default 24, 0 to 168) before it stops by itself. The old token is renamed with a -replaced- suffix. Its expiry notices close. # Revoke an API token Source: https://docs.ankra.ai/api-reference/account-tokens/revoke-an-api-token https://platform.ankra.app/openapi.json post /api/v1/org/account/tokens/{token_id}/revoke Revokes one of the calling user's API tokens so it can no longer authenticate, and returns the revocation time. # Confirm your contact email Source: https://docs.ankra.ai/api-reference/account/confirm-your-contact-email https://platform.ankra.app/openapi.json post /org/account/contact-email/verify Checks the code mailed to the pending address and, when it matches, stores that address as your confirmed contact email. Attempts per code are limited. # Get your contact email Source: https://docs.ankra.ai/api-reference/account/get-your-contact-email https://platform.ankra.app/openapi.json get /org/account/contact-email The address Ankra sends alerts and invoices to when it differs from the one you sign in with, when it was confirmed, and any address waiting for its code. # Remove your contact email Source: https://docs.ankra.ai/api-reference/account/remove-your-contact-email https://platform.ankra.app/openapi.json delete /org/account/contact-email Takes the contact address off your account, so mail goes to the address you sign in with again. Any code still waiting is cancelled. # Send a contact email code Source: https://docs.ankra.ai/api-reference/account/send-a-contact-email-code https://platform.ankra.app/openapi.json post /org/account/contact-email Mails a six-digit code to the address you want to use. The address is stored only once the code comes back through the verify route; sends are rate limited per account. # Cancel an agent run Source: https://docs.ankra.ai/api-reference/ai-agent-runs/cancel-an-agent-run https://platform.ankra.app/openapi.json post /api/v1/org/ai-agent-runs/{run_id}/cancel Cancel one live AI agent run and its session (organisation admins only). The runner interrupts the in-flight turn within seconds. # Get an agent run Source: https://docs.ankra.ai/api-reference/ai-agent-runs/get-an-agent-run https://platform.ankra.app/openapi.json get /api/v1/org/ai-agent-runs/{run_id} Fetch one dispatched AI agent run with its owning task's name, the full stored outcome summary (the list clips it), and tool_iterations - the tool calls the run's linked session made, or null when no session is attached. turns_used counts sessions, not tool calls. # Get an agent run's transcript Source: https://docs.ankra.ai/api-reference/ai-agent-runs/get-an-agent-runs-transcript https://platform.ankra.app/openapi.json get /api/v1/org/ai-agent-runs/{run_id}/transcript Read the run's linked session transcript (ai_session_event page after `since`). Events whose payload is a bare JSON string - the assistant's own prose above all - carry it in `payload_text`; `payload` keeps object payloads only. # List agent runs Source: https://docs.ankra.ai/api-reference/ai-agent-runs/list-agent-runs https://platform.ankra.app/openapi.json get /api/v1/org/ai-agent-runs List the organisation's dispatched AI agent runs across every task, newest first. # Get a condition's evidence timeline Source: https://docs.ankra.ai/api-reference/ai-conditions/get-a-conditions-evidence-timeline https://platform.ankra.app/openapi.json get /api/v1/org/ai-conditions/{condition_id} Returns one condition's evidence timeline: the tickets filed on it, how each closed and on what evidence, the probes, the playbook runs and the decisions people took. Answers 404 when the condition does not belong to the organisation. # Get a condition's evidence timeline (browser session) Source: https://docs.ankra.ai/api-reference/ai-conditions/get-a-conditions-evidence-timeline-browser-session https://platform.ankra.app/openapi.json get /org/ai-conditions/{condition_id} Returns one condition's evidence timeline: the tickets filed on it, how each closed and on what evidence, the probes, the playbook runs and the decisions people took. Answers 404 when the condition does not belong to the organisation. # List the organisation's conditions Source: https://docs.ankra.ai/api-reference/ai-conditions/list-the-organisations-conditions https://platform.ankra.app/openapi.json get /api/v1/org/ai-conditions Lists the organisation's conditions, the recurring problems AI tickets are filed against, most recently observed first, with totals of all, open and chronic conditions. `chronic=true` keeps only conditions closed as no-action three times in a row; `limit` defaults to 100 and is capped at 500. # List the organisation's conditions (browser session) Source: https://docs.ankra.ai/api-reference/ai-conditions/list-the-organisations-conditions-browser-session https://platform.ankra.app/openapi.json get /org/ai-conditions Lists the organisation's conditions, the recurring problems AI tickets are filed against, most recently observed first, with totals of all, open and chronic conditions. `chronic=true` keeps only conditions closed as no-action three times in a row; `limit` defaults to 100 and is capped at 500. # Acknowledge an engineering handoff with the tracker's reference Source: https://docs.ankra.ai/api-reference/ai-engineering-handoffs/acknowledge-an-engineering-handoff-with-the-trackers-reference https://platform.ankra.app/openapi.json post /api/v1/org/ai-engineering-handoffs/{handoff_id}/acknowledge Records the engineering tracker's reference for a handoff once the issue exists there (`tracker_reference`, required). Organisation admins only; answers 404 for an unknown handoff. # Acknowledge an engineering handoff with the tracker's reference (browser session) Source: https://docs.ankra.ai/api-reference/ai-engineering-handoffs/acknowledge-an-engineering-handoff-with-the-trackers-reference-browser-session https://platform.ankra.app/openapi.json post /org/ai-engineering-handoffs/{handoff_id}/acknowledge Records the engineering tracker's reference for a handoff once the issue exists there (`tracker_reference`, required). Organisation admins only; answers 404 for an unknown handoff. # List the organisation's engineering handoffs Source: https://docs.ankra.ai/api-reference/ai-engineering-handoffs/list-the-organisations-engineering-handoffs https://platform.ankra.app/openapi.json get /api/v1/org/ai-engineering-handoffs Lists the platform-level conditions Ankra handed to the organisation's engineering tracker instead of the AI ticket board. `status` filters by a comma-separated subset of `open`, `filed` and `resolved`; an unknown status answers 422. # List the organisation's engineering handoffs (browser session) Source: https://docs.ankra.ai/api-reference/ai-engineering-handoffs/list-the-organisations-engineering-handoffs-browser-session https://platform.ankra.app/openapi.json get /org/ai-engineering-handoffs Lists the platform-level conditions Ankra handed to the organisation's engineering tracker instead of the AI ticket board. `status` filters by a comma-separated subset of `open`, `filed` and `resolved`; an unknown status answers 422. # Create a demo base stack Source: https://docs.ankra.ai/api-reference/ai-environment/create-a-demo-base-stack https://platform.ankra.app/openapi.json post /org/ai-environment/demo-base-stacks Save a new shared demo base stack in status 'configured'. Nothing reaches the cluster here - materialisation happens on the first demo launch that references it. Organisation-admin only; browser session authentication with the X-Ankra-CSRF double-submit header (no bearer twin). Secret parameter values ride as ${SECRET_SLOT:} sentinels, never resolved values. # Delete a demo base stack Source: https://docs.ankra.ai/api-reference/ai-environment/delete-a-demo-base-stack https://platform.ankra.app/openapi.json delete /org/ai-environment/demo-base-stacks/{base_stack_id} Initiate teardown of a demo base stack. Deletion is refused with a 409 while any live demo references it. A never-materialised (configured) row is deleted outright; a materialised one goes to 'deleting' and the maintainer loop finishes the job. Organisation-admin only; browser session authentication with the X-Ankra-CSRF double-submit header (no bearer twin). # Get the AI environment Source: https://docs.ankra.ai/api-reference/ai-environment/get-the-ai-environment https://platform.ankra.app/openapi.json get /api/v1/org/ai-environment Read the organisation's AI environment settings, including dns_root_domain - the organisation's own Ankra root domain that every generated hostname nests under - and dns_root_domain_default, the platform default that applies when no custom domain is registered. # List the organisation's demo base stacks Source: https://docs.ankra.ai/api-reference/ai-environment/list-the-organisations-demo-base-stacks https://platform.ankra.app/openapi.json get /org/ai-environment/demo-base-stacks List the organisation's shared demo base stacks (every status except deleted) with their live-demo reference counts. Reads are member-visible; browser session authentication only (no bearer twin). # List the organisation's source-control bindings and their AI review switches Source: https://docs.ankra.ai/api-reference/ai-environment/list-the-organisations-source-control-bindings-and-their-ai-review-switches https://platform.ankra.app/openapi.json get /api/v1/org/ai-gateway/scm-bindings Every GitHub App installation and GitLab or Bitbucket credential of the organisation, with its AI review, mention replies, PR previews and merge-when-green switches, review model, draft handling, per-pull-request cap, custom instructions and repository rules. Any member of the organisation may read it. # List the repositories a source-control binding can reach Source: https://docs.ankra.ai/api-reference/ai-environment/list-the-repositories-a-source-control-binding-can-reach https://platform.ankra.app/openapi.json get /api/v1/org/ai-gateway/scm-bindings/{provider}/{binding_external_id}/repos The repositories the GitHub App installation can read, sorted by name, with the repo_full_name a repository rule takes. GitHub App installations only: other providers answer 404. Any member of the organisation may read it. # List the repositories related to each other for AI review Source: https://docs.ankra.ai/api-reference/ai-environment/list-the-repositories-related-to-each-other-for-ai-review https://platform.ankra.app/openapi.json get /api/v1/org/ai-gateway/related-repos Lists the repository relationships the organisation has stated for cross-repository AI review within one GitHub App installation (`binding_external_id`), with their total. Answers for the organisation's own installations only. # Remove a repository's AI review rule from a source-control binding Source: https://docs.ankra.ai/api-reference/ai-environment/remove-a-repositorys-ai-review-rule-from-a-source-control-binding https://platform.ankra.app/openapi.json delete /api/v1/org/ai-gateway/scm-bindings/{provider}/{binding_external_id}/repo-overrides Organisation admin only. Removes one repository's rule so the repository follows the binding-level settings again. removed is false when the repository had no rule. # Remove a stated relationship between two repositories Source: https://docs.ankra.ai/api-reference/ai-environment/remove-a-stated-relationship-between-two-repositories https://platform.ankra.app/openapi.json delete /api/v1/org/ai-gateway/related-repos/{related_repo_id} Removes a stated repository relationship and answers 204. Organisation admins only; answers 404 when the relationship does not belong to the organisation. # Set a repository's AI review rule on a source-control binding Source: https://docs.ankra.ai/api-reference/ai-environment/set-a-repositorys-ai-review-rule-on-a-source-control-binding https://platform.ankra.app/openapi.json put /api/v1/org/ai-gateway/scm-bindings/{provider}/{binding_external_id}/repo-overrides Organisation admin only. Writes one repository's own switches on the binding, which then apply to that repository instead of the binding-level settings: ai_review turns the AI review on or off for the repository. repo_full_name, ai_review, mention_replies and pr_previews are required; review_model, review_drafts and max_reviews_per_pr are optional and an omitted member keeps its stored value. Writing the same repository again replaces its rule. # State that two repositories are related for AI review Source: https://docs.ankra.ai/api-reference/ai-environment/state-that-two-repositories-are-related-for-ai-review https://platform.ankra.app/openapi.json post /api/v1/org/ai-gateway/related-repos States that two repositories (`repo_full_name` and `related_repo_full_name`, as `owner/name`) are related, so an AI review of one may read the other. Organisation admins only; GitHub is the only supported provider, and both repositories must be reachable by the GitHub App installation named in `binding_external_id`. # Update a demo base stack Source: https://docs.ankra.ai/api-reference/ai-environment/update-a-demo-base-stack https://platform.ankra.app/openapi.json put /org/ai-environment/demo-base-stacks/{base_stack_id} Change the mutable designation of a demo base stack: profile, profile version, parameters, and idle TTL. The name (and thus the namespace) is immutable and ignored here; an omitted profile_id keeps the current profile. Changes are refused while the stack is deploying or deleting, and take effect on the next materialisation. Organisation-admin only; browser session authentication with the X-Ankra-CSRF double-submit header (no bearer twin). # Update a source-control binding's AI review switches Source: https://docs.ankra.ai/api-reference/ai-environment/update-a-source-control-bindings-ai-review-switches https://platform.ankra.app/openapi.json put /api/v1/org/ai-gateway/scm-bindings/{provider}/{binding_external_id} Organisation admin only. ai_review and mention_replies are required; every other member is optional and an omitted member keeps its stored value. merge_when_green is the switch behind merge-when-green: with it on, and the binding's AI mode set to Agent, an '@ankraai merge when green' comment, the portal button or the scm_merge_pull_request tool records a merge intent that Ankra settles once the pull request is green. # Update the AI environment Source: https://docs.ankra.ai/api-reference/ai-environment/update-the-ai-environment https://platform.ankra.app/openapi.json put /api/v1/org/ai-environment Update the organisation's AI environment settings (organisation admin only). Only the members present in the body are changed. Setting dns_root_domain registers the organisation's own Ankra root domain; it must be a bare domain the organisation owns, already delegated to the Ankra nameservers, and held by no other organisation. The switch is refused with 400 while cluster DNS zones or DNS records still live under the old domain - the response's blocking_cluster_zones and blocking_dns_records members list exactly what to remove first, each capped at 100 entries with a matching *_truncated flag set to true when more exist (read the full lists from /api/v1/org/dns/cluster-zones and /api/v1/org/dns/records). # Create an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/create-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks Creates an agent task and answers 201. `name`, `goal_prompt` and `trigger` are required; `tool_profile` defaults to `read_only`. A name already in use answers 409. # Delete an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/delete-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json delete /api/v1/org/ai-tasks/{task_id} Deletes an agent task so it is no longer scheduled, and answers 204. Answers 404 when it does not exist. # Hire an agent from a role template (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/hire-an-agent-from-a-role-template-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks/templates/{template_id}/hire Creates an agent task from a built-in role, optionally scoped to `cluster_id`, and answers 201. Hiring the Reviewer also makes it the ticket desk's reviewer when none is set; answers 409 when an agent with the role's name already exists. # List agent hire templates (ai.use) Source: https://docs.ankra.ai/api-reference/ai-management/list-agent-hire-templates-aiuse https://platform.ankra.app/openapi.json get /api/v1/org/ai-tasks/templates Lists the built-in agent roles that can be hired, each with its goal prompt and whether the organisation has already hired it. # List agent task runs (ai.use) Source: https://docs.ankra.ai/api-reference/ai-management/list-agent-task-runs-aiuse https://platform.ankra.app/openapi.json get /api/v1/org/ai-tasks/{task_id}/runs Lists an agent task's recent runs, newest first. `limit` defaults to 20 and is capped at 100; answers 404 when the task does not exist. # List organisation agent tasks (ai.use) Source: https://docs.ankra.ai/api-reference/ai-management/list-organisation-agent-tasks-aiuse https://platform.ankra.app/openapi.json get /api/v1/org/ai-tasks Lists the organisation's agent tasks: the AI agents that run on a schedule, on an alert or on demand, with their goal, tool profile, autonomy level and budget. # Pause an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/pause-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks/{task_id}/pause Stops scheduling an agent task. A run already in progress is allowed to finish its current turn; use stop to cancel it as well. # Pause or resume all organisation AI (ai.manage) Source: https://docs.ankra.ai/api-reference/ai-management/pause-or-resume-all-organisation-ai-aimanage https://platform.ankra.app/openapi.json put /api/v1/org/ai-settings/pause Pauses (`paused: true`, with an optional `reason`) or resumes all AI in the organisation. Pausing cancels every running chat session and agent run and expires open confirmation cards, reporting how many; both directions are idempotent. Requires `ai.manage`. # Read an agent task (ai.use) Source: https://docs.ankra.ai/api-reference/ai-management/read-an-agent-task-aiuse https://platform.ankra.app/openapi.json get /api/v1/org/ai-tasks/{task_id} Returns one agent task of the organisation. Answers 404 when it does not exist. # Read the organisation AI pause switch (ai.manage) Source: https://docs.ankra.ai/api-reference/ai-management/read-the-organisation-ai-pause-switch-aimanage https://platform.ankra.app/openapi.json get /api/v1/org/ai-settings/pause Returns the organisation's AI kill switch: whether all AI is paused, who paused it, when and why. Requires `ai.manage`. # Reapply the current role envelope (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/reapply-the-current-role-envelope-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks/{task_id}/reapply-template Brings an agent up to its role template's current envelope. An envelope an operator customised is only replaced with `force: true`; every refusal answers 409 with a machine-readable `reason`. # Resume an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/resume-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks/{task_id}/resume Re-enables a paused agent task and restores its schedule from its trigger. # Run an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/run-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks/{task_id}/run Queues an enabled agent task for immediate dispatch and answers 202; the scheduler picks it up within its poll interval, and does nothing if a run is already live. Answers 404 when the task does not exist or is disabled. # Stop an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/stop-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json post /api/v1/org/ai-tasks/{task_id}/stop Pauses an agent task and cancels its live run in the same step, returning `cancelled_run_id` and `cancelled_session_id` when something was running. # Update an agent task (ai.manage and organisation admin) Source: https://docs.ankra.ai/api-reference/ai-management/update-an-agent-task-aimanage-and-organisation-admin https://platform.ankra.app/openapi.json put /api/v1/org/ai-tasks/{task_id} Updates an agent task's definition: name, goal, trigger, tool profile, autonomy level, budget, capabilities, model and whether it is enabled. Answers 404 when the task does not exist. # Verify the token's user and organisation (ai.use) Source: https://docs.ankra.ai/api-reference/ai-management/verify-the-tokens-user-and-organisation-aiuse https://platform.ankra.app/openapi.json get /api/v1/org/ai-settings/identity Returns the `organisation_id` and `user_id` the API token resolves to, so an embedded management client can confirm its binding before it forwards requests. # Clear AI memories Source: https://docs.ankra.ai/api-reference/ai-memory/clear-ai-memories https://platform.ankra.app/openapi.json delete /api/v1/org/ai-memory/memories Deletes every memory the assistant has stored for the calling user in the current organisation and returns how many were removed. Other members' memories are not touched. # Delete an AI memory Source: https://docs.ankra.ai/api-reference/ai-memory/delete-an-ai-memory https://platform.ankra.app/openapi.json delete /api/v1/org/ai-memory/memories/{memory_id} Deletes one of the calling user's memories; returns 404 when it does not exist, belongs to someone else, or is already deleted. # Get the learned AI profile Source: https://docs.ankra.ai/api-reference/ai-memory/get-the-learned-ai-profile https://platform.ankra.app/openapi.json get /api/v1/org/ai-memory/profile Returns what the assistant has learned about the calling user (communication style, expertise, preferred tools) and whether learning is on. # List AI memories Source: https://docs.ankra.ai/api-reference/ai-memory/list-ai-memories https://platform.ankra.app/openapi.json get /api/v1/org/ai-memory/memories Lists the cross-session memories the assistant has stored for the calling user in the current organisation, newest first, up to limit entries (default 50, at most 200). # Reset the learned AI profile Source: https://docs.ankra.ai/api-reference/ai-memory/reset-the-learned-ai-profile https://platform.ankra.app/openapi.json delete /api/v1/org/ai-memory/profile Clears what the assistant has learned about the calling user's preferences. Stored memories are not affected. # Update the learned AI profile Source: https://docs.ankra.ai/api-reference/ai-memory/update-the-learned-ai-profile https://platform.ankra.app/openapi.json put /api/v1/org/ai-memory/profile Replaces the calling user's learned profile and/or turns learning on or off; a member left out keeps its stored value. # Adopt an AI playbook Source: https://docs.ankra.ai/api-reference/ai-playbooks/adopt-an-ai-playbook https://platform.ankra.app/openapi.json post /api/v1/org/ai-playbooks/{playbook_id}/adopt Approves a playbook for use: a platform template becomes the organisation's own approved playbook, and one of the organisation's candidate playbooks becomes approved. Organisation admins only; answers 409 when the playbook cannot be adopted. # List AI playbooks Source: https://docs.ankra.ai/api-reference/ai-playbooks/list-ai-playbooks https://platform.ankra.app/openapi.json get /api/v1/org/ai-playbooks Lists the organisation's AI playbooks together with the platform templates it can adopt. # Retire an AI playbook Source: https://docs.ankra.ai/api-reference/ai-playbooks/retire-an-ai-playbook https://platform.ankra.app/openapi.json post /api/v1/org/ai-playbooks/{playbook_id}/retire Retires one of the organisation's own playbooks, with an optional `reason`. Organisation admins only; platform templates cannot be retired, and a playbook that cannot be retired answers 409. # Read the auto-remediation policy Source: https://docs.ankra.ai/api-reference/ai-remediation/read-the-auto-remediation-policy https://platform.ankra.app/openapi.json get /api/v1/org/ai-remediation/policy The bearer-token twin of the browser read, authorised the same way it is: any authenticated member of the organisation, with no named permission, because the portal already shows this document to every member. The PUT that writes the policy stays a browser session + CSRF + organisation-admin lane and has no token twin. An organisation that never configured a policy reads the disabled default document (enabled false, autonomy_level 'propose', updated_at null) rather than a 404. # Choose the model one AI lane runs on Source: https://docs.ankra.ai/api-reference/ai-settings/choose-the-model-one-ai-lane-runs-on https://platform.ankra.app/openapi.json put /api/v1/org/ai-settings/lane-models/{lane} Stores the lane's model: a catalog key or an exact OpenRouter model id. Profile summaries accept only enabled, saved organisation catalog keys and never fall back. A null or empty model_key clears the selection, returning other lanes to their default tier and leaving profile summaries unselected. Requires the ai.manage permission. # List the model each AI lane runs on Source: https://docs.ankra.ai/api-reference/ai-settings/list-the-model-each-ai-lane-runs-on https://platform.ankra.app/openapi.json get /api/v1/org/ai-settings/lane-models Every registered AI lane - deploy analysis, troubleshooting, CI/CD generation, stack README generation and AI code review - with the organisation's selection and the model the lane resolves to today. Member-level. Profile summaries require an explicit saved organisation model; their effective fields are empty when unselected or unavailable. # Add an AI ticket link Source: https://docs.ankra.ai/api-reference/ai-tickets/add-an-ai-ticket-link https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/links Links a record to a ticket by `target_kind` and `target_id`, with a `relation` that defaults to `relates_to`, and answers 201 with the link it created (200 with the existing link when it was already there). A ticket target is the ticket's id or its number (`T-42`), must be another ticket of the organisation, and is stored by id: the ticket itself answers 400, an unknown ticket or one of another organisation 404. A session cannot be linked as `spawned` or `evidence`: the platform records those relations itself. # Answer an AI ticket's decision Source: https://docs.ankra.ai/api-reference/ai-tickets/answer-an-ai-tickets-decision https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/decision Answers the decision a blocked ticket is waiting on, with one of the offered options (`option_key`), a free-text `answer`, or both, so the agent can resume. Answers 409 when the ticket is not waiting on a decision. # Assign an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/assign-an-ai-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/assign Assigns a ticket to an agent (`assignee_agent_task_id`) or an organisation member (`assignee_user_id`), never both; send both as null to unassign. Answers 404 when the agent or member is not in the organisation. # Attach a workspace to an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/attach-a-workspace-to-an-ai-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/workspace Attaches an AI workspace of the organisation to the ticket, or detaches the current one when `workspace_id` is null. # Close an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/close-an-ai-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/close Closes a ticket with a `resolution` and an optional `note`. A close as `fixed` can name what confirmed the fix in `evidence_kind` and `evidence_reference`; without them the caller is recorded as the evidence. A close as `duplicate` can name the open ticket covering the same cause in `duplicate_of`; it is linked to that ticket's canonical keeper, and the call answers 422 when `duplicate_of` accompanies another resolution or names no open ticket of the organisation. Answers 409 when the ticket cannot be closed from its current state. # Comment on an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/comment-on-an-ai-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/comments Adds a comment to a ticket's history as the caller and answers 201 with the recorded event. # Create an AI sub-ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/create-an-ai-sub-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/sub-tickets Files a sub-ticket under an open ticket and answers 201. `title` is required; `kind` defaults to `request` and `priority` to `medium`. Answers 400 when the parent ticket is closed. # Create an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/create-an-ai-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets Supports idempotent submission: pass an Idempotency-Key header (or the equivalent client_token body field). While the ticket filed for a key is live (not done/cancelled), repeating the submission with the same key returns that ticket instead of filing a duplicate. The key is stored on the ticket as source_kind 'client_token' / source_id. # Create an AI ticket sync connection Source: https://docs.ankra.ai/api-reference/ai-tickets/create-an-ai-ticket-sync-connection https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/sync/connections Creates an outbound sync connection that mirrors tickets to Jira, Linear or a generic webhook; `name` and `provider` are required. Organisation admins only; answers 409 when the name is taken and 502 when the external tracker rejects the setup. # Delete an AI ticket sync connection Source: https://docs.ankra.ai/api-reference/ai-tickets/delete-an-ai-ticket-sync-connection https://platform.ankra.app/openapi.json delete /api/v1/org/ai-tickets/sync/connections/{connection_id} Deletes a sync connection and answers 204. Organisation admins only; answers 404 when the connection does not exist. # Detach an AI ticket from its root Source: https://docs.ankra.ai/api-reference/ai-tickets/detach-an-ai-ticket-from-its-root https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/detach Makes a symptom a top-level ticket again, with an event on both timelines. Its status and any question it carries are left as they are, and it is worked like any other ticket from then on. A ticket that sits under no root is returned unchanged; a subtask is refused with 409. # Draft an AI ticket plan Source: https://docs.ankra.ai/api-reference/ai-tickets/draft-an-ai-ticket-plan https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/plan Records a plan written by the caller on the ticket (`plan_document`). Answers 404 when the ticket does not exist or is already closed. # Get AI ticket desk settings Source: https://docs.ankra.ai/api-reference/ai-tickets/get-ai-ticket-desk-settings https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/settings Returns the organisation's ticket desk settings: the Slack channel and Microsoft Teams conversation tickets are announced in, the reviewing agent and whether agent review is required, which insight severities are filed automatically, and the channel notification policy. An organisation that never saved settings reads the defaults. # Get an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/get-an-ai-ticket https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/{ticket_id} Returns one ticket of the organisation. Answers 404 when it does not exist. # Get an AI ticket sync connection Source: https://docs.ankra.ai/api-reference/ai-tickets/get-an-ai-ticket-sync-connection https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/sync/connections/{connection_id} Returns one sync connection of the organisation, without its stored credential. Answers 404 when it does not exist. # Get the AI ticket board summary Source: https://docs.ankra.ai/api-reference/ai-tickets/get-the-ai-ticket-board-summary https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/summary Returns the board's counts for top-level tickets: per status, per kind and priority, tickets needing a human, tickets in flight, tickets done in the last 7 days, and open tickets assigned to the caller or to each agent. Accepts the same narrowing filters as the ticket list, so the counts match the rows it returns. # List AI ticket events Source: https://docs.ankra.ai/api-reference/ai-tickets/list-ai-ticket-events https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/{ticket_id}/events Lists a ticket's history events in order. Pass `after_sequence` to read only events after one already seen; `limit` defaults to 100 and is capped at 500. # List AI ticket links Source: https://docs.ankra.ai/api-reference/ai-tickets/list-ai-ticket-links https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/{ticket_id}/links Lists the records linked to a ticket, such as chat sessions, agent runs, insights, pending actions, workspaces, merge requests and other tickets, with the relation of each, and under `incoming_links` the ticket links other tickets of the organisation hold on this one. A link to a ticket carries `linked_ticket`: the other ticket's number, title, status and resolution. # List AI ticket sync connections Source: https://docs.ankra.ai/api-reference/ai-tickets/list-ai-ticket-sync-connections https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets/sync/connections Lists the organisation's outbound ticket sync connections (Jira, Linear or a generic webhook). Stored credentials are never returned. # List AI ticket sync destinations Source: https://docs.ankra.ai/api-reference/ai-tickets/list-ai-ticket-sync-destinations https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/sync/destinations Lists the destinations a tracker credential can reach (for example the Linear teams it can file into), so a connection can target one by name. Send either a new `credential` or the `connection_id` of a saved connection; nothing is written. Organisation admins only. # List AI tickets Source: https://docs.ankra.ai/api-reference/ai-tickets/list-ai-tickets https://platform.ankra.app/openapi.json get /api/v1/org/ai-tickets Tickets are ordered by updated_at DESC, id DESC (stable). Prefer keyset pagination: pass the previous page's next_cursor as cursor; offset is ignored when cursor is set and remains only for existing clients. total_count is always the full filtered population. The search filter is a case-insensitive substring match on title and body; %, _ and \ in the search text match literally. # Nest an AI ticket under a root Source: https://docs.ankra.ai/api-reference/ai-tickets/nest-an-ai-ticket-under-a-root https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/nest Moves an existing ticket under a root ticket as a symptom: the root carries the work, the symptom gets no agent run or thread of its own, and it settles or detaches when the root closes. Name the root with `root_ticket_id` or `root_ticket_number`; a symptom named as the root resolves to its own root. A ticket a person owes an answer on keeps its question. Answers 422 when the root is the ticket itself or does not exist in the organisation, and 409 with the reason when the tickets' state refuses it, for example a closed root, a subtask or a loop. # Remove an AI ticket link Source: https://docs.ankra.ai/api-reference/ai-tickets/remove-an-ai-ticket-link https://platform.ankra.app/openapi.json delete /api/v1/org/ai-tickets/{ticket_id}/links/{link_id} Removes one link from a ticket, records a `link_removed` event naming who removed it and what it pointed at, and answers 204. Answers 404 when the link does not exist. # Review an AI ticket plan Source: https://docs.ankra.ai/api-reference/ai-tickets/review-an-ai-ticket-plan https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/plan/review Submits a review of the ticket's plan with `verdict` `approve` or `request_changes` and an optional `rationale`. Pass `expected_updated_at` to be refused with 409 if the ticket changed since it was read. # Transition an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/transition-an-ai-ticket https://platform.ankra.app/openapi.json post /api/v1/org/ai-tickets/{ticket_id}/transition Moves a ticket to another `status`, with an optional `note`. Answers 409 when the transition is not allowed from the ticket's current status. # Update AI ticket desk settings Source: https://docs.ankra.ai/api-reference/ai-tickets/update-ai-ticket-desk-settings https://platform.ankra.app/openapi.json put /api/v1/org/ai-tickets/settings Updates the ticket desk settings sent in the body and keeps the rest. Only organisation admins can change them (403 otherwise). # Update an AI ticket Source: https://docs.ankra.ai/api-reference/ai-tickets/update-an-ai-ticket https://platform.ankra.app/openapi.json patch /api/v1/org/ai-tickets/{ticket_id} Updates a ticket's metadata: title, body, priority, labels, cluster, staffing and due date. Fields left out keep their values, and an explicit `null` clears `cluster_id` or `due_at`. # Update an AI ticket sync connection Source: https://docs.ankra.ai/api-reference/ai-tickets/update-an-ai-ticket-sync-connection https://platform.ankra.app/openapi.json put /api/v1/org/ai-tickets/sync/connections/{connection_id} Updates a sync connection; fields left out of the body keep their current values. Organisation admins only; answers 404 for an unknown connection. # Create an alert ingest credential Source: https://docs.ankra.ai/api-reference/alerts/create-an-alert-ingest-credential https://platform.ankra.app/openapi.json post /api/v1/org/alerts/ingest-credentials Mints an ingest credential and returns its token together with the webhook path (`/webhooks/alerts/{credential_id}`) that accepts Alertmanager webhook payloads. The token is shown only in this response; `scope` is `cluster`, `platform` or `mixed`, a `mixed` credential must be pinned to a cluster with `cluster_id`, and a `platform` one must not be. # Create an alert integration Source: https://docs.ankra.ai/api-reference/alerts/create-an-alert-integration https://platform.ankra.app/openapi.json post /api/v1/org/alerts/integrations Creates an alert destination. `integration_type` defaults to `slack`; a Slack or Microsoft Teams channel destination carries `channel_id` (Teams also needs `teams_tenant_id`) and every other destination needs a webhook `url`. A name already in use answers 400. # Delete an alert ingest credential Source: https://docs.ankra.ai/api-reference/alerts/delete-an-alert-ingest-credential https://platform.ankra.app/openapi.json delete /api/v1/org/alerts/ingest-credentials/{credential_id} Revokes an ingest credential so its token can no longer deliver alerts. Answers 404 when the credential does not belong to the organisation. # Delete an alert integration Source: https://docs.ankra.ai/api-reference/alerts/delete-an-alert-integration https://platform.ankra.app/openapi.json delete /api/v1/org/alerts/integrations/{integration_id} Deletes an alert integration so no further alerts are delivered to it. Answers 404 when it does not exist in the organisation. # Get an alert integration Source: https://docs.ankra.ai/api-reference/alerts/get-an-alert-integration https://platform.ankra.app/openapi.json get /api/v1/org/alerts/integrations/{integration_id} Returns one alert integration of the organisation. Answers 404 when it does not exist. # List alert ingest credentials Source: https://docs.ankra.ai/api-reference/alerts/list-alert-ingest-credentials https://platform.ankra.app/openapi.json get /api/v1/org/alerts/ingest-credentials Lists the organisation's alert ingest credentials: the per-credential endpoints external notifiers such as Prometheus Alertmanager use to deliver alerts into Ankra. Tokens are never returned. # List alert integrations Source: https://docs.ankra.ai/api-reference/alerts/list-alert-integrations https://platform.ankra.app/openapi.json get /api/v1/org/alerts/integrations Lists the organisation's alert integrations (the webhook and channel destinations alerts are delivered to), paginated with `page` and `page_size` (default 20, at most 100). `search` filters by name and `enabled` by state. # List Microsoft Teams channels for alert integrations Source: https://docs.ankra.ai/api-reference/alerts/list-microsoft-teams-channels-for-alert-integrations https://platform.ankra.app/openapi.json get /api/v1/org/alerts/integrations/teams/channels Lists the channels of every team the Ankra bot is installed in across the organisation's connected Microsoft Teams tenants, for choosing a channel destination. Answers 404 when no Teams tenant is connected. # List Slack channels for alert integrations Source: https://docs.ankra.ai/api-reference/alerts/list-slack-channels-for-alert-integrations https://platform.ankra.app/openapi.json get /api/v1/org/alerts/integrations/slack/channels Lists the Slack channels the Ankra bot can post to in the organisation's connected Slack workspace, for choosing a channel destination. Answers 404 when no workspace is connected and 409 with `missing_scopes` when the Slack grant is too narrow to list channels. # Test an alert integration Source: https://docs.ankra.ai/api-reference/alerts/test-an-alert-integration https://platform.ankra.app/openapi.json post /api/v1/org/alerts/integrations/{integration_id}/test Sends a test alert to a saved integration (its webhook URL or its Slack or Teams channel) and returns whether delivery succeeded, with the HTTP status and response time. The result is recorded as the integration's last test. # Test an alert integration URL Source: https://docs.ankra.ai/api-reference/alerts/test-an-alert-integration-url https://platform.ankra.app/openapi.json post /api/v1/org/alerts/integrations/test-url Sends a test alert to a webhook `url` before it is saved, optionally rendered with `template`, and returns whether delivery succeeded with the HTTP status and response time. Answers 400 for a missing or malformed URL. # Update an alert integration Source: https://docs.ankra.ai/api-reference/alerts/update-an-alert-integration https://platform.ankra.app/openapi.json put /api/v1/org/alerts/integrations/{integration_id} Updates the fields sent in the body (name, URL, channel, template, description, enabled) and leaves the rest unchanged. Answers 404 for an unknown integration and 400 when the new name is already in use. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/vms Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Add a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/add-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/vms Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Add node group Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/node-groups Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Add node group (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/add-node-group-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/node-groups Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Check Kubernetes upgrade readiness (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/check-kubernetes-upgrade-readiness-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Create Ankra Cloud cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/create-ankra-cloud-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.create`. Creates a self-managed kubeadm / k3s cluster on Ankra Cloud servers: a private RFC 1918 network (`network_ip_range`, or an adopted `private_network_id`), a bastion and the cluster nodes on the private network only. By default (`network_mode` ipv6_only) the nodes reach IPv4 through the zone's NAT64 and the bastion holds only its public IPv6; `network_mode` ipv4_nat adds a router with NAT for IPv4 egress and gives the bastion a public IPv4. Plans replace instance types (`bastion_plan`, `control_plane_plan`, `worker_plan`, `etcd_plan`; node groups keep `instance_type`), `template` is the OS image (default debian-13). The preflight runs first and the first failing item is the refusal. kubeadm requires Cilium; the ankra-cloud-ccm is always installed. `bastion_port` is accepted for contract symmetry and ignored: the bastion listens on 22. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # Create Ankra Cloud cluster (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/create-ankra-cloud-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.create`. Creates a self-managed kubeadm / k3s cluster on Ankra Cloud servers: a private RFC 1918 network (`network_ip_range`, or an adopted `private_network_id`), a bastion and the cluster nodes on the private network only. By default (`network_mode` ipv6_only) the nodes reach IPv4 through the zone's NAT64 and the bastion holds only its public IPv6; `network_mode` ipv4_nat adds a router with NAT for IPv4 egress and gives the bastion a public IPv4. Plans replace instance types (`bastion_plan`, `control_plane_plan`, `worker_plan`, `etcd_plan`; node groups keep `instance_type`), `template` is the OS image (default debian-13). The preflight runs first and the first failing item is the refusal. kubeadm requires Cilium; the ankra-cloud-ccm is always installed. `bastion_port` is accepted for contract symmetry and ignored: the bastion listens on 22. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/ankracloud/{cluster_id}/vms/{vm_id} Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/delete-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/ankracloud/{cluster_id}/vms/{vm_id} Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete node group Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name} Bearer PAT authentication; RBAC permission `clusters.delete`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete node group (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/delete-node-group-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name} Browser session authentication; RBAC permission `clusters.delete`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Deprovision an Ankra Cloud cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/deprovision-an-ankra-cloud-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/ankracloud/{cluster_id} Bearer PAT authentication; RBAC permission `clusters.delete`. Ordered teardown preserves explicitly retained storage and reports orphaned provider IDs. Answers 404 while the organisation's `aws_provider` feature flag is off. # Deprovision an Ankra Cloud cluster (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/deprovision-an-ankra-cloud-cluster-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/ankracloud/{cluster_id} Browser session authentication; RBAC permission `clusters.delete`. Ordered teardown preserves explicitly retained storage and reports orphaned provider IDs. Answers 404 while the organisation's `aws_provider` feature flag is off. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Diagnose bastion (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/diagnose-bastion-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/bastion/diagnose Browser session authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Diagnose node Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `ankracloud_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get bastion health Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. Answers 404 while the organisation's `aws_provider` feature flag is off. # Get bastion health (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/get-bastion-health-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/bastion/health Browser session authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. Answers 404 while the organisation's `aws_provider` feature flag is off. # Latest restorable state snapshot of a Ankra Cloud cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/latest-restorable-state-snapshot-of-a-ankra-cloud-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the Ankra Cloud cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List Ankra Cloud CPU pools Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-cpu-pools https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/cpu-pools Bearer PAT authentication; RBAC permission `clusters.read`. The CPU pools of one Ankra Cloud zone: compute nodes grouped by exact CPU, each with its x86-64 microarchitecture level (`x86_64_level`, 3 or more has AVX2) and `has_avx2`. A server is placed in one pool and only moves within it, so a node group pinned to a pool (`cpu_pool` on node-group add) decides which CPU instructions its workloads may use. `zone` is required. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud CPU pools (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-cpu-pools-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/cpu-pools RBAC permission `clusters.read`. The CPU pools of one Ankra Cloud zone: compute nodes grouped by exact CPU, each with its x86-64 microarchitecture level (`x86_64_level`, 3 or more has AVX2) and `has_avx2`. A server is placed in one pool and only moves within it, so a node group pinned to a pool (`cpu_pool` on node-group add) decides which CPU instructions its workloads may use. `zone` is required. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud networks Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-networks https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/networks Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Private networks the cluster may adopt instead of creating one. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud networks (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-networks-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/networks Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Private networks the cluster may adopt instead of creating one. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud plans Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-plans https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/plans Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Server plans with monthly and hourly prices in cents. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud plans (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-plans-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/plans Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Server plans with monthly and hourly prices in cents. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud pricing Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-pricing https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/pricing Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Storage tier prices and the public IPv4 add-on price, in cents; incomplete pricing carries explicit reasons. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud pricing (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-pricing-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/pricing Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Storage tier prices and the public IPv4 add-on price, in cents; incomplete pricing carries explicit reasons. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud templates Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-templates https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/templates Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Public OS templates a node may be created from. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud templates (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-templates-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/templates Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Public OS templates a node may be created from. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud zones Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-zones https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/zones Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Zones the credential may place servers in; network-only zones are left out. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List Ankra Cloud zones (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-ankra-cloud-zones-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/zones Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. Zones the credential may place servers in; network-only zones are left out. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # List instance types for an Ankra Cloud cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-instance-types-for-an-ankra-cloud-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/instance-types Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. The plan catalog under the cluster's own credential; /instance-types is the alias the shared node-group pickers read. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # List instance types for an Ankra Cloud cluster (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-instance-types-for-an-ankra-cloud-cluster-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/instance-types Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. The plan catalog under the cluster's own credential; /instance-types is the alias the shared node-group pickers read. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # List live cluster Ankra Cloud CPU pools Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-live-cluster-ankra-cloud-cpu-pools https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/cpu-pools Bearer PAT authentication; RBAC permission `clusters.read`. The CPU pools of the cluster's own Ankra Cloud zone under its credential, each with its x86-64 microarchitecture level (`x86_64_level`, 3 or more has AVX2) and `has_avx2`, for choosing the `cpu_pool` a node group pins its servers to. # List live cluster Ankra Cloud CPU pools (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-live-cluster-ankra-cloud-cpu-pools-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/cpu-pools RBAC permission `clusters.read`. The CPU pools of the cluster's own Ankra Cloud zone under its credential, each with its x86-64 microarchitecture level (`x86_64_level`, 3 or more has AVX2) and `has_avx2`, for choosing the `cpu_pool` a node group pins its servers to. # List live cluster Ankra Cloud plans Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-live-cluster-ankra-cloud-plans https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/plans Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.read`. The plan catalog under the cluster's own credential; /instance-types is the alias the shared node-group pickers read. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # List live cluster Ankra Cloud plans (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-live-cluster-ankra-cloud-plans-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/plans Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.read`. The plan catalog under the cluster's own credential; /instance-types is the alias the shared node-group pickers read. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # List node groups Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/node-groups Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List node groups (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-node-groups-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/node-groups Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List nodes Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/nodes Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List nodes (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-nodes-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/nodes Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List standalone VMs Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/vms Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List standalone VMs (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/list-standalone-vms-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/vms Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Preflight Ankra Cloud cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/preflight-ankra-cloud-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/preflight Bearer PAT authentication; RBAC permission `clusters. RBAC permission `clusters.create`. Checks the create request against the account without creating anything: the credential authenticates, the zone offers compute, every plan and the template exist, an adopted network lives in the zone, and the counts are within bounds. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # Preflight Ankra Cloud cluster (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/preflight-ankra-cloud-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/preflight Browser session authentication; RBAC permission `clusters. RBAC permission `clusters.create`. Checks the create request against the account without creating anything: the credential authenticates, the zone offers compute, every plan and the template exist, an adopted network lives in the zone, and the counts are within bounds. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. With no `credential_id`, the organisation's built-in Ankra Cloud credential is used, and the request answers 503 with Retry-After when it cannot be created or renewed right now. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/control-plane Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read control-plane sizing (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-control-plane-sizing-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/control-plane Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read Kubernetes version Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/k8s-version Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read Kubernetes version (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-kubernetes-version-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/k8s-version Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/nodes/{node_id} Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-node-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/nodes/{node_id} Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `aws_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node-group autoscaling Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-node-group-autoscaling https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/autoscaling Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node-group autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-node-group-autoscaling-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/autoscaling Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read private cluster access Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/access-info Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read private cluster access (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-private-cluster-access-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/access-info Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read SSH keys Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/ssh-keys Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-ssh-keys-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/ssh-keys Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read worker count Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/worker-count Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read worker count (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/read-worker-count-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/worker-count Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/vms/{vm_id}/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/vms/{vm_id}/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/control-plane/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize control-plane nodes (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-control-plane-nodes-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/control-plane/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize node group Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize node group (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-node-group-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize the public gateway Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-the-public-gateway https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/bastion/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize the public gateway (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resize-the-public-gateway-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/bastion/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Restart node Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/restart-node https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/nodes/{node_id}/restart Bearer PAT authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Restart node (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/restart-node-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/nodes/{node_id}/restart Browser session authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/ssh-keys/resync Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resync SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/resync-ssh-keys-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/ssh-keys/resync Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale node group Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/scale Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale node group (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/scale-node-group-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/scale Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale workers Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/scale-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/scale-workers Bearer PAT authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale workers (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/scale-workers-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/scale-workers Browser session authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Start a stopped cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/start-a-stopped-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/start Bearer PAT authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Start a stopped cluster (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/start-a-stopped-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/start Browser session authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Stop a cluster Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/stop-a-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/stop Bearer PAT authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Stop a cluster (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/stop-a-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/stop Browser session authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # The persistent volumes deleting an Ankra Cloud cluster removes Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/the-persistent-volumes-deleting-an-ankra-cloud-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/ankracloud/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/aws/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. Answers 404 while the organisation's `aws_provider` feature flag is off. # The persistent volumes deleting an Ankra Cloud cluster removes (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/the-persistent-volumes-deleting-an-ankra-cloud-cluster-removes-browser-session https://platform.ankra.app/openapi.json get /org/clusters/ankracloud/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/aws/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update control-plane count Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/control-plane Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update control-plane count (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-control-plane-count-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/control-plane Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group autoscaling Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-node-group-autoscaling https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/autoscaling Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-node-group-autoscaling-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/autoscaling Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group labels Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/labels Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group labels (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-node-group-labels-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/labels Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group taints Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/taints Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group taints (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-node-group-taints-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/node-groups/{group_name}/taints Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update SSH keys Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/ankracloud/{cluster_id}/ssh-keys Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/update-ssh-keys-browser-session https://platform.ankra.app/openapi.json put /org/clusters/ankracloud/{cluster_id}/ssh-keys Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Upgrade the Kubernetes version Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/upgrade-the-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/ankracloud/{cluster_id}/upgrade-k8s-version Bearer PAT authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Upgrade the Kubernetes version (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-clusters/upgrade-the-kubernetes-version-browser-session https://platform.ankra.app/openapi.json post /org/clusters/ankracloud/{cluster_id}/upgrade-k8s-version Browser session authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Create an Ankra Cloud credential Source: https://docs.ankra.ai/api-reference/ankra-cloud-credentials/create-an-ankra-cloud-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/ankracloud Bearer PAT authentication; RBAC permission `credentials.write`. The secret is stored in Vault and connection-tested without echoing key material. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # Create an Ankra Cloud credential (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-credentials/create-an-ankra-cloud-credential-browser-session https://platform.ankra.app/openapi.json post /org/credentials/ankracloud Browser session authentication; RBAC permission `credentials.write`. The secret is stored in Vault and connection-tested without echoing key material. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # List Ankra Cloud credentials Source: https://docs.ankra.ai/api-reference/ankra-cloud-credentials/list-ankra-cloud-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/ankracloud Bearer PAT authentication; RBAC permission `credentials.read`. Returns credential metadata only; api_token is never returned. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # List Ankra Cloud credentials (browser session) Source: https://docs.ankra.ai/api-reference/ankra-cloud-credentials/list-ankra-cloud-credentials-browser-session https://platform.ankra.app/openapi.json get /org/credentials/ankracloud Browser session authentication; RBAC permission `credentials.read`. Returns credential metadata only; api_token is never returned. Answers 404 while the organisation's `ankra_cloud_provider` feature flag is off. # Add a branch to the application's pipeline Source: https://docs.ankra.ai/api-reference/applications/add-a-branch-to-the-applications-pipeline https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/pipeline/push-branches Proposes adding one branch to on.push.branches of the application's .ankra/pipeline.yaml as a pull request, so an environment following that branch has builds to deploy. Only that list changes; every other key and comment stays. A branch the list already matches answers already_covered with no pull request, and a second request while the pull request is open answers already_open. Held to the applications.write permission and audited. # Add a branch to the application's pipeline (browser session) Source: https://docs.ankra.ai/api-reference/applications/add-a-branch-to-the-applications-pipeline-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/pipeline/push-branches Proposes adding one branch to on.push.branches of the application's .ankra/pipeline.yaml as a pull request, so an environment following that branch has builds to deploy. Only that list changes; every other key and comment stays. A branch the list already matches answers already_covered with no pull request, and a second request while the pull request is open answers already_open. Held to the applications.write permission and audited. # Add a scan to an application's pipeline Source: https://docs.ankra.ai/api-reference/applications/add-a-scan-to-an-applications-pipeline https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/pipeline-scan Adds the requested scanners to the application's `.ankra/pipeline.yaml` and opens a pull request proposing it: the missing scanner is appended to the scan stage that follows each build, or a report-only scan stage is inserted after the build. The rest of the file, comments included, is left as it was. A second call while the pull request is open stacks on it. Requires `applications.write`. # Adopt reviewed operating expectations Source: https://docs.ankra.ai/api-reference/applications/adopt-reviewed-operating-expectations https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/environments/{environment_id}/operating-expectations Environment-scoped operating intent. Targets do not provision or certify runtime controls. Adoption checks the reviewed revision and commits intent with an audit record atomically. # Adopt reviewed operating expectations (browser session) Source: https://docs.ankra.ai/api-reference/applications/adopt-reviewed-operating-expectations-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/environments/{environment_id}/operating-expectations Environment-scoped operating intent. Targets do not provision or certify runtime controls. Adoption checks the reviewed revision and commits intent with an audit record atomically. # Apply application environment secrets Source: https://docs.ankra.ai/api-reference/applications/apply-application-environment-secrets https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/env-secrets/apply Apply the application's stored environment secrets to what is already running: re-seal them into the Secrets the manifests read on every deployment and roll the workloads that read them. An application whose last key has been cleared applies too, and the apply then removes the sealed Secret from the deployment's stack and rolls the workloads off those values - without it, every reconcile put the deleted values back. The values only reach a running workload at deploy time otherwise, so this is the explicit one-click close of that gap - it is never triggered implicitly by setting a value. It takes no body (the values it applies are the ones already stored) and returns none. The deploy parameters of each deployment are left untouched: only the environment-secret manifest and the pod templates that read it are written. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # Apply application environment secrets (browser session) Source: https://docs.ankra.ai/api-reference/applications/apply-application-environment-secrets-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/env-secrets/apply Apply the application's stored environment secrets to what is already running: re-seal them into the Secrets the manifests read on every deployment and roll the workloads that read them. An application whose last key has been cleared applies too, and the apply then removes the sealed Secret from the deployment's stack and rolls the workloads off those values - without it, every reconcile put the deleted values back. The values only reach a running workload at deploy time otherwise, so this is the explicit one-click close of that gap - it is never triggered implicitly by setting a value. It takes no body (the values it applies are the ones already stored) and returns none. The deploy parameters of each deployment are left untouched: only the environment-secret manifest and the pod templates that read it are written. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # Approve a security fix merge Source: https://docs.ankra.ai/api-reference/applications/approve-a-security-fix-merge https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/security-fixes/{run_id}/merge Approves the merge of one security fix pull request at the head the person reviewed. Ankra merges it at once when its checks and review are green (squash, or through the repository's merge train when it has one), and otherwise refuses with the reason; a push to the branch clears an approval. The person's approval is the authority for the merge, so a paused organisation or application does not hold it. Requires security.manage and the merge authority (an organisation administrator, the pull request's author, or a member whose verified GitHub login can push to the repository); a service account token is refused. # Approve a security fix merge (browser session) Source: https://docs.ankra.ai/api-reference/applications/approve-a-security-fix-merge-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/security-fixes/{run_id}/merge Approves the merge of one security fix pull request at the head the person reviewed. Ankra merges it at once when its checks and review are green (squash, or through the repository's merge train when it has one), and otherwise refuses with the reason; a push to the branch clears an approval. The person's approval is the authority for the merge, so a paused organisation or application does not hold it. Requires security.manage and the merge authority (an organisation administrator, the pull request's author, or a member whose verified GitHub login can push to the repository); a service account token is refused. The browser route also requires the X-Ankra-CSRF header. # Approve an application's Launch Plan Source: https://docs.ankra.ai/api-reference/applications/approve-an-applications-launch-plan https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/launch-plan/approvals Approves the application's live Launch Plan once for a bounded, enumerated action list (ankra-bjkszh.8): the stacks the plan installs in the target cluster, the environment values a person enters (by name; a value only with secret_value_consent), applying them, merging setup pull requests Ankra opened and proved on the application's own repository, the deploy, and the ankra.cc domain. The approval binds to plan_hash from the Launch Plan read and is refused when the plan changed; every open decision must be accepted. Only a person approves: a browser session, or a token whose scopes name ai_actions.approve. The approver must hold every approved action's permission, and the approval expires (one hour by default, at most 24). Requires applications.write. Audited. # Approve an application's Launch Plan (browser session) Source: https://docs.ankra.ai/api-reference/applications/approve-an-applications-launch-plan-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/launch-plan/approvals Approves the application's live Launch Plan once for a bounded, enumerated action list (ankra-bjkszh.8): the stacks the plan installs in the target cluster, the environment values a person enters (by name; a value only with secret_value_consent), applying them, merging setup pull requests Ankra opened and proved on the application's own repository, the deploy, and the ankra.cc domain. The approval binds to plan_hash from the Launch Plan read and is refused when the plan changed; every open decision must be accepted. Only a person approves: a browser session, or a token whose scopes name ai_actions.approve. The approver must hold every approved action's permission, and the approval expires (one hour by default, at most 24). Requires applications.write. Audited. # Check a branch's demo build Source: https://docs.ankra.ai/api-reference/applications/check-a-branchs-demo-build https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/demos/build Preflight a branch demo: resolve the branch's head SHA and probe the container registry for a matching image tag. GitHub or registry problems are reported inside the 200 payload's error and guidance fields, not as HTTP errors. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Check an application's Secret references Source: https://docs.ankra.ai/api-reference/applications/check-an-applications-secret-references https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/deploy/clusters/{cluster_id}/secret-references Which Secrets the application's workloads read without `optional: true`, rendered as its next deploy to this cluster namespace would render them, and whether each one will be there when they start: `produced` by the same deploy (its environment secrets, a Secret manifest in the set, or a CloudNativePG Cluster X publishing X-app), `exists` on the cluster, or `missing`. `state` is `unknown` when the manifests could not be rendered or the cluster's Secrets could not be read recently enough to vouch for an absence; an unknown answer never blocks a deploy. # Check an application's Secret references (browser session) Source: https://docs.ankra.ai/api-reference/applications/check-an-applications-secret-references-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deploy/clusters/{cluster_id}/secret-references Which Secrets the application's workloads read without `optional: true`, rendered as its next deploy to this cluster namespace would render them, and whether each one will be there when they start: `produced` by the same deploy (its environment secrets, a Secret manifest in the set, or a CloudNativePG Cluster X publishing X-app), `exists` on the cluster, or `missing`. `state` is `unknown` when the manifests could not be rendered or the cluster's Secrets could not be read recently enough to vouch for an absence; an unknown answer never blocks a deploy. # Claim an approved Launch Plan action Source: https://docs.ankra.ai/api-reference/applications/claim-an-approved-launch-plan-action https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/launch-plan/approvals/{approval_id}/claims Claims one approved action under a plan-level approval, once, before the caller performs it: the gate an Ankra AI run or an MCP or API caller passes to act on the approver's single approval instead of asking again. Refused unless the approval is the caller's own, unexpired and unrevoked, the application's live Launch Plan still hashes as approved, the action is in the approved list and not yet claimed, and the caller still holds the action's permission. Answers the action with the tool that performs it and its bound parameters. Requires applications.write. Audited, refusals included. # Claim an approved Launch Plan action (browser session) Source: https://docs.ankra.ai/api-reference/applications/claim-an-approved-launch-plan-action-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/launch-plan/approvals/{approval_id}/claims Claims one approved action under a plan-level approval, once, before the caller performs it: the gate an Ankra AI run or an MCP or API caller passes to act on the approver's single approval instead of asking again. Refused unless the approval is the caller's own, unexpired and unrevoked, the application's live Launch Plan still hashes as approved, the action is in the approved list and not yet claimed, and the caller still holds the action's permission. Answers the action with the tool that performs it and its bound parameters. Requires applications.write. Audited, refusals included. # Clone an application environment onto another cluster Source: https://docs.ankra.ai/api-reference/applications/clone-an-application-environment-onto-another-cluster https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/environments/{environment_id}/clone Clones one environment of the application onto another cluster: the same image, deploy mode and accepted deploy options (replicas, resources, ports, every option but the addresses), queued as a deploy. Hostnames are claimed organisation-wide, so the copy keeps the target's own address or derives a fresh one. A cluster the application already runs on is overwritten in place. With include_secrets the source's own secret overrides are re-written under the target's Vault path, its suppressions are copied, and the target's other overrides return to the shared value; only key names are returned and each one is audited. environment_id is a stored environment id or the cluster id an unnamed environment is known by. Held to the applications.deploy permission, and applications.write as well when secrets are included. Bearer personal access token; the CLI twin of the browser route (no CSRF header). # Clone an application environment onto another cluster (browser session) Source: https://docs.ankra.ai/api-reference/applications/clone-an-application-environment-onto-another-cluster-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/environments/{environment_id}/clone Clones one environment of the application onto another cluster: the same image, deploy mode and accepted deploy options (replicas, resources, ports, every option but the addresses), queued as a deploy. Hostnames are claimed organisation-wide, so the copy keeps the target's own address or derives a fresh one. A cluster the application already runs on is overwritten in place. With include_secrets the source's own secret overrides are re-written under the target's Vault path, its suppressions are copied, and the target's other overrides return to the shared value; only key names are returned and each one is audited. environment_id is a stored environment id or the cluster id an unnamed environment is known by. Held to the applications.deploy permission, and applications.write as well when secrets are included. # Create an application Source: https://docs.ankra.ai/api-reference/applications/create-an-application https://platform.ankra.app/openapi.json post /api/v1/org/applications Registers a GitHub repository as an application in the organisation and turns on push-to-deploy for it. Requires `applications.write`; `name`, `app_repo_credential_name`, `app_repo_owner` and `app_repo_name` are required and the branch defaults to `main`. A name another application already uses is answered with an error entry in `errors` and no `id`. # Delete an application Source: https://docs.ankra.ai/api-reference/applications/delete-an-application https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id} Deletes the application. Requires `applications.delete`. Every deployment of the application is queued for teardown so its workloads do not outlive it, and its environment secrets and registry robot are removed with it; an unknown application answers `success: false`. An application a managed service that has not been retired serves is refused with `409` naming the services. # Delete an application environment secret Source: https://docs.ankra.ai/api-reference/applications/delete-an-application-environment-secret https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id}/env-secrets/{secret_key} Clear one environment secret: the catalogue entry and the stored value. The -env Secret stops carrying the key on the application's next deploy. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # Delete an application environment secret (browser session) Source: https://docs.ankra.ai/api-reference/applications/delete-an-application-environment-secret-browser-session https://platform.ankra.app/openapi.json delete /org/applications/{application_id}/env-secrets/{secret_key} Clear one environment secret: the catalogue entry and the stored value. The -env Secret stops carrying the key on the application's next deploy. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # Deploy an application Source: https://docs.ankra.ai/api-reference/applications/deploy-an-application https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/deploy Preserves omitted installation options. reset_inputs deliberately removes named overrides so repository defaults apply. Returns a durable operation identity. # Deploy an application (browser session) Source: https://docs.ankra.ai/api-reference/applications/deploy-an-application-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/deploy Preserves omitted installation options. reset_inputs deliberately removes named overrides so repository defaults apply. Returns a durable operation identity. # Deploy an application demo Source: https://docs.ankra.ai/api-reference/applications/deploy-an-application-demo https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/demos Deploy a throwaway demo of the application onto the organisation's staging cluster. Provide exactly one of branch or a positive pr_number; PR demos additionally require an explicit image_tag. Launch env entries are merged over the application's saved demo defaults (overrides win by name) and database overrides the saved throwaway-database default (null keeps it). A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Describe the application's registry robot Source: https://docs.ankra.ai/api-reference/applications/describe-the-applications-registry-robot https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/registry-robot Report whether a push robot minted on the registry this application publishes to backs its repository login, and when none does, why - the organisation's project is not provisioned, or the declared registry names no admin_credential_name for Ankra to mint one with. Never contacts the registry. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Fix a branch's demo build Source: https://docs.ankra.ai/api-reference/applications/fix-a-branchs-demo-build https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/demos/fix-build Fix a branch whose demo build is missing: apply the deterministic Ankra-side fixes first (publish-readiness auto-fix, failed-run rerun, failed-setup retry) and, when those cannot produce an image, dispatch a one-shot mission agent that investigates the repository and opens a pull request with the fix. The answer points at the dispatched mission rather than carrying its result, so an unattended caller polls the agent run. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Fix a failed application demo Source: https://docs.ankra.ai/api-reference/applications/fix-a-failed-application-demo https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/demos/{workspace_id}/fix Dispatch the demo pre-setup mission agent for a failed demo whose container crashed on startup. The agent reads the crash logs, generates the missing demo environment, saves it as the application's demo defaults, and redeploys the demo. No request body. A malformed workspace_id is reported as the same 404 as an unknown demo. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Get a data migration Source: https://docs.ankra.ai/api-reference/applications/get-a-data-migration https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/launch-plan/data-migration/runs/{run_id} One data-migration run of the application: its status (running, cut-over, completed, failed, rolling-back, rolled-back, rollback-failed), each step and rollback step with what happened, when the rollback window closes, and whether a rollback was requested. Requires applications.read. # Get a data migration (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-a-data-migration-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/launch-plan/data-migration/runs/{run_id} One data-migration run of the application: its status (running, cut-over, completed, failed, rolling-back, rolled-back, rollback-failed), each step and rollback step with what happened, when the rollback window closes, and whether a rollback was requested. Requires applications.read. # Get a Launch Plan approval Source: https://docs.ankra.ai/api-reference/applications/get-a-launch-plan-approval https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/launch-plan/approvals/{approval_id} One plan-level approval of the application's Launch Plan with its approved actions, its status (active, expired or revoked) and the actions claimed under it. Requires applications.read. # Get a Launch Plan approval (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-a-launch-plan-approval-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/launch-plan/approvals/{approval_id} One plan-level approval of the application's Launch Plan with its approved actions, its status (active, expired or revoked) and the actions claimed under it. Requires applications.read. # Get a repository's launch status Source: https://docs.ankra.ai/api-reference/applications/get-a-repositorys-launch-status https://platform.ankra.app/openapi.json get /api/v1/org/applications/launch-status Where a GitHub repository is on its way to a working app on Ankra (ankra-bjkszh.7): not connected, analysing, N decisions left in the Launch Plan, ready for approval, proving the setup pull request, proof failed, or live. The state the Ankra Chrome extension shows on GitHub, Vercel, Lovable and Claude Design pages. Every application the caller can see that builds the repository is listed, the one building branch first. Requires applications.read. # Get a repository's launch status (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-a-repositorys-launch-status-browser-session https://platform.ankra.app/openapi.json get /org/applications/launch-status Where a GitHub repository is on its way to a working app on Ankra (ankra-bjkszh.7): not connected, analysing, N decisions left in the Launch Plan, ready for approval, proving the setup pull request, proof failed, or live. The state the Ankra Chrome extension shows on GitHub, Vercel, Lovable and Claude Design pages. Every application the caller can see that builds the repository is listed, the one building branch first. Requires applications.read. # Get an application Source: https://docs.ankra.ai/api-reference/applications/get-an-application https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id} Returns one application of the organisation: its state, generated files, detected stack, latest workflow and delivery status, and a summary of its deployments. Answers 404 when the application does not exist in the organisation. # Get an application demo's details Source: https://docs.ankra.ai/api-reference/applications/get-an-application-demos-details https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/demos/{workspace_id}/detail Return the demo record plus the live picture of its namespace on the staging cluster: the derived provisioning steps, the bill of materials, the namespace's Kubernetes events, and the pod/container names that drive the log viewer. A malformed workspace_id is reported as the same 404 as an unknown demo. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Get an application demo's logs Source: https://docs.ankra.ai/api-reference/applications/get-an-application-demos-logs https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/demos/{workspace_id}/logs Collect a bounded log tail from one of the demo's pods. An empty pod resolves to the demo's own pod. tail_lines defaults to 200 and is capped at 2000. A malformed workspace_id is reported as the same 404 as an unknown demo. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Get an application's AI configuration Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-ai-configuration https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/ai-config Return the AI lanes configured for this application's repository: for each lane whether it runs, whether that decision is inherited from the organisation or overridden for this application, the settings it actually runs with, and the organisation default behind them. An application with no connected source-control binding answers 200 with binding.connected false and a reason, so the AI tab can render its empty state rather than an error. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Get an application's App Contract Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-app-contract https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/app-contract The App Contract the setup run extracted from an analysed commit of the application (ankra-bjkszh.1): its components, listen port and IP family, environment variables marked required, optional or secret with the file and line that reads each, the external services it talks to, its migrations, build inputs, health routes and exposure. The latest analysed commit by default; commit_sha names one. Every field but application_id is null when no analysis has extracted a contract yet. Requires applications.read. # Get an application's App Contract (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-app-contract-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/app-contract The App Contract the setup run extracted from an analysed commit of the application (ankra-bjkszh.1): its components, listen port and IP family, environment variables marked required, optional or secret with the file and line that reads each, the external services it talks to, its migrations, build inputs, health routes and exposure. The latest analysed commit by default; commit_sha names one. Every field but application_id is null when no analysis has extracted a contract yet. Requires applications.read. # Get an application's demo configuration Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-demo-configuration https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/demo-config Return the application's saved demo defaults: the environment entries, the throwaway-database flag, and the optional stack-profile and base-stack dependency designations. Secret environment values and secret stack parameters ride as ${SECRET_SLOT:} sentinels, never resolved values. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Get an application's Launch Plan Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-launch-plan https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/launch-plan The Launch Plan for the application (ankra-bjkszh.2, ankra-bjkszh.16), resolved on read from the App Contract of an analysed commit: per external service its lock-in tier with the reason and evidence, the default choice (bring your own, Ankra open source in the cluster, off, a portability PR, or staying at the vendor) and the alternatives; where each environment variable's value comes from; the migration step, port, IP family and exposure; and the decisions only a person can make. The plan is resolved for a target (target): whether it is a playground decides the database default for a Supabase app; other cluster facts are not read yet and say unknown. The latest analysed commit by default; commit_sha names one. Requires applications.read. data_migration is the plan's data move off its vendors for the selection in backup_vault_id and data_migration_choices; without a selection the plan's defaults keep every vendor and it moves nothing. # Get an application's Launch Plan (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-launch-plan-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/launch-plan The Launch Plan for the application (ankra-bjkszh.2, ankra-bjkszh.16), resolved on read from the App Contract of an analysed commit: per external service its lock-in tier with the reason and evidence, the default choice (bring your own, Ankra open source in the cluster, off, a portability PR, or staying at the vendor) and the alternatives; where each environment variable's value comes from; the migration step, port, IP family and exposure; and the decisions only a person can make. The plan is resolved for a target (target): whether it is a playground decides the database default for a Supabase app; other cluster facts are not read yet and say unknown. The latest analysed commit by default; commit_sha names one. Requires applications.read. data_migration is the plan's data move off its vendors for the selection in backup_vault_id and data_migration_choices; without a selection the plan's defaults keep every vendor and it moves nothing. # Get an application's subject state Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-subject-state https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/subject-state One platform read of everything Ankra records about the application in an environment (ankra-ab5fog.8): the application's record, the place it runs (the one the query names by cluster and namespace, else the environment's one deployment, else every place listed), the installation's current state and the k8s mirror's view of its pods, the newest deploy runs and platform builds with their errors, the setup pull request and the reviews Ankra AI wrote, the active platform errors with the remedy their code names, and the named conversation's actions on it. Every section is three-state: a read that failed or was never attempted is unknown, never empty. Requires applications.read. # Get an application's subject state (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-an-applications-subject-state-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/subject-state One platform read of everything Ankra records about the application in an environment (ankra-ab5fog.8): the application's record, the place it runs (the one the query names by cluster and namespace, else the environment's one deployment, else every place listed), the installation's current state and the k8s mirror's view of its pods, the newest deploy runs and platform builds with their errors, the setup pull request and the reviews Ankra AI wrote, the active platform errors with the remedy their code names, and the named conversation's actions on it. Every section is three-state: a read that failed or was never attempted is unknown, never empty. Requires applications.read. # Get the application timeline Source: https://docs.ankra.ai/api-reference/applications/get-the-application-timeline https://platform.ankra.app/openapi.json get /org/applications/{application_id}/timeline The application's unified timeline, newest first: platform builds, deploys, promotions, rollbacks, demos, AI reviews, what Ankra did on its own and lifecycle events from the platform's own records, capped with the cap reported, a cursor to the next page, and every unreadable source named - an unreadable source is unknown, not a source with no events. Live-fetched lanes (GitHub workflow runs, registry scans) keep their own endpoints. # Get the application's environments Source: https://docs.ankra.ai/api-reference/applications/get-the-applications-environments https://platform.ankra.app/openapi.json get /org/applications/{application_id}/environments The environment ladder: every place the application runs, one row per environment (wave 1: one per cluster), derived from the deployments read and the installation rows. status is the deployment-state ladder applied to that environment's targets alone; read_state is unknown when the deployments read failed, and environments is then empty - which a client must render as could-not-check, never as no environments. address carries the raw ingress inputs; the verdict is the client's existing rule. custom_domains lists the hostnames each placement's stored ingress_extra_hosts names, each with the certificate verdict the synced cert-manager Certificates and Ingresses give it; its read_state separates none from not-on-record. # Get the application's planned address Source: https://docs.ankra.ai/api-reference/applications/get-the-applications-planned-address https://platform.ankra.app/openapi.json get /org/applications/{application_id}/planned-address The address the application's first deploy would publish on, knowable from birth when the organisation's preview domain is published by one of its clusters. A read, not a reservation: the deploy's own derivation stays the authority on what is published. state is planned (host named), none (nothing to name yet - the reason says why) or unknown (the organisation's DNS settings could not be read, which is not an organisation without a preview domain). # Get whether Ankra is paused for an application Source: https://docs.ankra.ai/api-reference/applications/get-whether-ankra-is-paused-for-an-application https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/autonomy-pause Whether a person paused Ankra's autonomous actions for this application. While it is paused, auto-remediation does not act on the application's namespaces (or on alerts about a whole cluster it runs on) and Cost Autopilot does not file or run changes on its clusters. Chat and read-only investigation keep working. A failed read is an error, never paused: false. # Get whether Ankra is paused for an application (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-whether-ankra-is-paused-for-an-application-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/autonomy-pause Whether a person paused Ankra's autonomous actions for this application. While it is paused, auto-remediation does not act on the application's namespaces (or on alerts about a whole cluster it runs on) and Cost Autopilot does not file or run changes on its clusters. Chat and read-only investigation keep working. A failed read is an error, never paused: false. # Get who runs each environment Source: https://docs.ankra.ai/api-reference/applications/get-who-runs-each-environment https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/autonomy Who runs it: for each of the application's environments, the effective autonomy level (you_run_it, together, ankra_runs_it) in every area, the rule that decided it, and the limits. The stored organisation, application and environment layers apply first; where the organisation layer sets nothing it is seeded from existing settings (auto-remediation policy, Cost Autopilot tier, push-to-deploy) and seeded_from names which. read_state is partial when one of those seeds could not be read and unknown when the environments or the stored policy could not be read, in which case environments is empty and must be rendered as could-not-check, never as no environments. # Get who runs each environment (browser session) Source: https://docs.ankra.ai/api-reference/applications/get-who-runs-each-environment-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/autonomy Who runs it: for each of the application's environments, the effective autonomy level (you_run_it, together, ankra_runs_it) in every area, the rule that decided it, and the limits. The stored organisation, application and environment layers apply first; where the organisation layer sets nothing it is seeded from existing settings (auto-remediation policy, Cost Autopilot tier, push-to-deploy) and seeded_from names which. read_state is partial when one of those seeds could not be read and unknown when the environments or the stored policy could not be read, in which case environments is empty and must be rendered as could-not-check, never as no environments. # Import a Claude Design application Source: https://docs.ankra.ai/api-reference/applications/import-a-claude-design-application https://platform.ankra.app/openapi.json post /api/v1/org/applications/imports/claude-design Convert a Claude Design export (the .dc.html artboards, canvas.json and images, a zip of them, or a saved canvas page) into a static site, create a GitHub repository for it under the given credential and owner, commit the site with a Dockerfile, and register the application on that repository. Refusals a caller can act on come back as `errors` with a 200, in the create route's shape. The lane ships behind the `claude_design_import` organisation feature flag: while it is off the route answers 404, like the platform build routes. # Inspect an installation’s live database capture sources Source: https://docs.ankra.ai/api-reference/applications/inspect-an-installation’s-live-database-capture-sources https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/installations/{installation_id}/recovery-sources Requires applications.read and backups.read with backups enabled. Resolves CNPG databases from the owned deployed manifest and fresh operator-owned primary Services. Returns 503 for unreadable or ambiguous evidence. This inventory is not a backup or restore receipt. Other database engines are not inventoried. # Inspect an installation’s live database capture sources (browser session) Source: https://docs.ankra.ai/api-reference/applications/inspect-an-installation’s-live-database-capture-sources-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/installations/{installation_id}/recovery-sources Requires applications.read and backups.read with backups enabled. Resolves CNPG databases from the owned deployed manifest and fresh operator-owned primary Services. Returns 503 for unreadable or ambiguous evidence. This inventory is not a backup or restore receipt. Other database engines are not inventoried. # List an application's data migrations Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-data-migrations https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/launch-plan/data-migration/runs The application's newest data-migration runs (at most 20), newest first, each with its plan, the state machine's run, the targets and the names of the staged environment values (never a value). Requires applications.read. # List an application's data migrations (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-data-migrations-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/launch-plan/data-migration/runs The application's newest data-migration runs (at most 20), newest first, each with its plan, the state machine's run, the targets and the names of the staged environment values (never a value). Requires applications.read. # List an application's demos Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-demos https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/demos Return the application's active demos plus everything the demos tab needs in one round-trip: the caller's TTL policy, the staging-cluster status, and the default container port (the generated Dockerfile's EXPOSE port when known). A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List an application's deploy runs Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-deploy-runs https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deploy-runs List the deploy runs recorded for this application, newest first. application_deploy_run is written when the user presses Deploy and carries the cluster, the ordered roles and the stack name confirmed for each, so an application can show a deploy that is queued, in flight or failed without the browser having remembered its run id - which was previously the only way to reach one. The listing is capped at 50 runs and reports truncated=true when older runs exist beyond it. An unknown application answers 404 rather than an empty list. Browser session authentication only (no bearer twin). # List an application's image versions with SBOM and findings Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-image-versions-with-sbom-and-findings https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/security/versions Lists the application's image versions, one row per pushed tag, with the SBOM and vulnerability finding metadata the Security Center holds for each. A version no cluster reports on reads the pipeline's complete image scan of its digest, so a release that has not been deployed yet is not listed as unscanned. `component` narrows the list to one component; answers 404 when the application does not exist in the organisation. # List an application's image versions with SBOM and findings (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-image-versions-with-sbom-and-findings-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/security/versions Lists the application's image versions, one row per pushed tag, with the SBOM and vulnerability finding metadata the Security Center holds for each. A version no cluster reports on reads the pipeline's complete image scan of its digest, so a release that has not been deployed yet is not listed as unscanned. `component` narrows the list to one component; answers 404 when the application does not exist in the organisation. # List an application's jobs Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-jobs https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/jobs Lists the platform jobs run for the application, paginated with `page` and `page_size` (default 25, at most 100). Answers 404 when the application does not exist in the organisation. # List an application's rollbacks Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-rollbacks https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/rollbacks The recorded rollback history of this application's deployment on the named cluster, newest first. It is what explains a deployment running an older build than the newest one the platform observed. Bearer personal access token; the CLI twin of the browser route. # List an application's rollbacks (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-rollbacks-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/rollbacks The recorded rollback history of this application's deployment on the named cluster, newest first. It is what explains a deployment running an older build than the newest one the platform observed. Browser session authentication; the bearer twin is GET /api/v1/org/applications/{application_id}/rollbacks. # List an application's security fixes Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-security-fixes https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/security-fixes Every security fix pull request opened for the application's findings, newest first (at most 50), each with the remediation run that follows it: the findings it fixes, the checks on its latest pipeline run, the AI review of its head, and whether Ankra merges it when green or waits for a person. Requires applications.read. # List an application's security fixes (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-an-applications-security-fixes-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/security-fixes Every security fix pull request opened for the application's findings, newest first (at most 50), each with the remediation run that follows it: the findings it fixes, the checks on its latest pipeline run, the AI review of its head, and whether Ankra merges it when green or waits for a person. Requires applications.read. # List application deployment placements Source: https://docs.ankra.ai/api-reference/applications/list-application-deployment-placements https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/deployments Returns owned application placements and optional workload identity for independent live image verification. Workload references are the applied manifest identities, not proof of running images or availability. # List application deployment placements (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-application-deployment-placements-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deployments Returns owned application placements and optional workload identity for independent live image verification. Workload references are the applied manifest identities, not proof of running images or availability. # List application environment secrets Source: https://docs.ankra.ai/api-reference/applications/list-application-environment-secrets https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/env-secrets List the environment-secret keys of an application: which keys its generated manifests read out of the Secrets Ankra fills for it, which of them have a stored value, when each was last set, and - in requirements_state - whether those manifests could be read at all, so an empty missing_keys is never mistaken for a fully configured application. Values are never returned - there is no reveal endpoint on this surface, because a value the platform writes into a cluster Secret has no reason to travel back out. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # List application environment secrets (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-application-environment-secrets-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/env-secrets List the environment-secret keys of an application: which keys its generated manifests read out of the Secrets Ankra fills for it, which of them have a stored value, when each was last set, and - in requirements_state - whether those manifests could be read at all, so an empty missing_keys is never mistaken for a fully configured application. Values are never returned - there is no reveal endpoint on this surface, because a value the platform writes into a cluster Secret has no reason to travel back out. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # List applications Source: https://docs.ankra.ai/api-reference/applications/list-applications https://platform.ankra.app/openapi.json get /api/v1/org/applications Lists the organisation's applications, paginated with `page` and `page_size` (default 25, at most 100). `search` filters by a case-insensitive match on the application name, and `order` accepts `created` (the default, newest first), `attention` (needs-you-first, A to Z by name within each step) or `name` (A to Z by name). # List platform builds Source: https://docs.ankra.ai/api-reference/applications/list-platform-builds https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/builds The application's platform builds, newest first, capped at 50 with the cap reported - a truncated listing that does not say so reads as complete. Answers 404 for every organisation without the platform_builds flag. Bearer PAT authentication (Authorization: Bearer ), with the organisation selected by the token or the X-Ankra-Organisation-Id override. No CSRF header: the double-submit gate belongs to the cookie surface only. # List platform builds (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-platform-builds-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/builds The application's platform builds, newest first, capped at 50 with the cap reported - a truncated listing that does not say so reads as complete. Answers 404 for every organisation without the platform_builds flag. # List the application's branch builds Source: https://docs.ankra.ai/api-reference/applications/list-the-applications-branch-builds https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/branch-builds The newest published build of each branch of the application (at most 200 branches, newest first), with the tracked branch: what an environment that follows a branch rule deploys from, so a client can say which build a rule would run or that a branch has published nothing yet. Held to the applications.read permission. # List the application's branch builds (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-the-applications-branch-builds-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/branch-builds The newest published build of each branch of the application (at most 200 branches, newest first), with the tracked branch: what an environment that follows a branch rule deploys from, so a client can say which build a rule would run or that a branch has published nothing yet. Held to the applications.read permission. # List what Ankra did on its own Source: https://docs.ankra.ai/api-reference/applications/list-what-ankra-did-on-its-own https://platform.ankra.app/openapi.json get /api/v1/org/applications/autonomous-actions What Ankra did on its own across every application, newest first: auto-remediation runs on an application's placements, Cost Autopilot actions the autopilot approved itself on an application's cluster, and build fixes the automatic lane sent. Each action is one item naming every application it was on, with who acted, the rule that allowed it and whether it can be undone. At most 20 actions; a source with more is named in capped_sources and a source that could not be read in unavailable_sources. # List what Ankra did on its own (browser session) Source: https://docs.ankra.ai/api-reference/applications/list-what-ankra-did-on-its-own-browser-session https://platform.ankra.app/openapi.json get /org/applications/autonomous-actions What Ankra did on its own across every application, newest first: auto-remediation runs on an application's placements, Cost Autopilot actions the autopilot approved itself on an application's cluster, and build fixes the automatic lane sent. Each action is one item naming every application it was on, with who acted, the rule that allowed it and whether it can be undone. At most 20 actions; a source with more is named in capped_sources and a source that could not be read in unavailable_sources. # Mint or rotate the application's registry robot Source: https://docs.ankra.ai/api-reference/applications/mint-or-rotate-the-applications-registry-robot https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/registry-robot Make sure a push robot minted for this application backs its repository login: mint one when none does (with Ankra's registry administrator on the organisation's own registry, or with image_registry.admin_credential_name on a registry the organisation operates), refresh its secret when rotate is set, store the login as the platform-managed registry credential ankra-harbor-app-, and write it into the repository's Actions secrets. A registry Ankra cannot administer answers 200 with provisioned false and a message naming what would let it. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Open an approved portability pull request Source: https://docs.ankra.ai/api-reference/applications/open-an-approved-portability-pull-request https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/launch-plan/approvals/{approval_id}/portability-pull-requests Claims one approved portability pull request action under a plan-level Launch Plan approval, once, and queues the operation that opens it on the application's own repository: the adapter pull request of a dependency the approver moved to needs-portability-pr, or the next.config pull request of a Vercel source. Refused unless the approval is the caller's own, unexpired and unrevoked, the application's live Launch Plan still hashes as approved, the action is an approved portability pull request not yet claimed, and the caller still holds applications.write. The operation renders the change from the newest App Contract at the plan's commit and opens it through the setup write guard; when Ankra cannot make the change with confidence it fails with the reason a person has to, verbatim. Ankra records the pull request for the PR shepherd and never merges it. Audited, refusals included. # Open an approved portability pull request (browser session) Source: https://docs.ankra.ai/api-reference/applications/open-an-approved-portability-pull-request-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/launch-plan/approvals/{approval_id}/portability-pull-requests Claims one approved portability pull request action under a plan-level Launch Plan approval, once, and queues the operation that opens it on the application's own repository: the adapter pull request of a dependency the approver moved to needs-portability-pr, or the next.config pull request of a Vercel source. Refused unless the approval is the caller's own, unexpired and unrevoked, the application's live Launch Plan still hashes as approved, the action is an approved portability pull request not yet claimed, and the caller still holds applications.write. The operation renders the change from the newest App Contract at the plan's commit and opens it through the setup write guard; when Ankra cannot make the change with confidence it fails with the reason a person has to, verbatim. Ankra records the pull request for the PR shepherd and never merges it. Audited, refusals included. # Park an application deployment Source: https://docs.ankra.ai/api-reference/applications/park-an-application-deployment https://platform.ankra.app/openapi.json post /org/applications/{application_id}/deployments/park Park one deployment of this application: scale its workload to zero replicas on purpose, remembering the prior replica count as parked_replicas inside the stored deploy parameters. The write is the rollback's partial-update shape - one jsonb update of the stored inputs plus the platform's existing deploy job - and the push-to-deploy watermark is moved onto the newest observed build first so a build observed before the park is not converged onto the resting workload. The installation, its manifests and its published Ingress host all stay: parked is a deliberate rest, never not_deployed. Held to the applications.deploy permission. Browser session authentication with the X-Ankra-CSRF double-submit header only (no bearer twin). # Pause Ankra for an application Source: https://docs.ankra.ai/api-reference/applications/pause-ankra-for-an-application https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/autonomy-pause Pauses Ankra's autonomous actions for this application. From the next dispatch, auto-remediation does not act on the application's namespaces (or on alerts about a whole cluster it runs on) and Cost Autopilot does not file or run changes on its clusters; a remediation run already acting there stops before its next mutating step and reports the step it did not run. Chat and read-only investigation keep working, and pausing never starts anything. Pausing a paused application changes nothing and answers its current pause. Each change is audited with who made it. Requires applications.write. # Pause Ankra for an application (browser session) Source: https://docs.ankra.ai/api-reference/applications/pause-ankra-for-an-application-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/autonomy-pause Pauses Ankra's autonomous actions for this application. From the next dispatch, auto-remediation does not act on the application's namespaces (or on alerts about a whole cluster it runs on) and Cost Autopilot does not file or run changes on its clusters; a remediation run already acting there stops before its next mutating step and reports the step it did not run. Chat and read-only investigation keep working, and pausing never starts anything. Pausing a paused application changes nothing and answers its current pause. Each change is audited with who made it. Requires applications.write. # Preview a promotion between two environments Source: https://docs.ankra.ai/api-reference/applications/preview-a-promotion-between-two-environments https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/promotions/preview What promoting the source environment's build into the target would do (ankra-ryfuy.38.4): the build that would move, what the target serves now, the deploy options that differ and stay as they are, the secrets check, the target's manifest-revision drift verdict, and every reason the promotion would be refused, with a fix path each. state is ready, already_current, blocked, or unknown when a read behind the verdict failed - never ready on a guess. Environment ids are the ids the environments read answers with. Held to the applications.deploy permission, as the write is; secrets.missing_keys is null unless the caller also holds applications.write, the env-secrets endpoints' permission. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Preview a promotion between two environments (browser session) Source: https://docs.ankra.ai/api-reference/applications/preview-a-promotion-between-two-environments-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/promotions/preview What promoting the source environment's build into the target would do (ankra-ryfuy.38.4): the build that would move, what the target serves now, the deploy options that differ and stay as they are, the secrets check, the target's manifest-revision drift verdict, and every reason the promotion would be refused, with a fix path each. state is ready, already_current, blocked, or unknown when a read behind the verdict failed - never ready on a guess. Environment ids are the ids the environments read answers with. Held to the applications.deploy permission, as the write is; secrets.missing_keys is null unless the caller also holds applications.write, the env-secrets endpoints' permission. # Promote a proven build into another environment Source: https://docs.ankra.ai/api-reference/applications/promote-a-proven-build-into-another-environment https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/promotions Promote the build the source environment has proven into the target environment (ankra-ryfuy.38.4). Exactly one thing moves: the image tag the source runs, written into each target installation's stored deploy inputs and deployed through the same deploy_application job the Deploy button and push-to-deploy enqueue - never a rebuild, never configuration. The target's push-to-deploy watermark is stamped with the promoted build and one application_promotion row records the move. Refused (409) with one of the preview's blocker sentences verbatim: "Source environment is not serving a proven build", "Source environment's build cannot be identified", "Source environment's targets disagree on what they serve", "Target environment follows pushes", "Ankra could not read whether the target environment follows pushes", "Target environment has a deploy in flight", "Target environment is missing required secrets". A target already running the build answers 200 with state already_current and no deploy. Held to the applications.deploy permission. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Promote a proven build into another environment (browser session) Source: https://docs.ankra.ai/api-reference/applications/promote-a-proven-build-into-another-environment-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/promotions Promote the build the source environment has proven into the target environment (ankra-ryfuy.38.4). Exactly one thing moves: the image tag the source runs, written into each target installation's stored deploy inputs and deployed through the same deploy_application job the Deploy button and push-to-deploy enqueue - never a rebuild, never configuration. The target's push-to-deploy watermark is stamped with the promoted build and one application_promotion row records the move. Refused (409) with one of the preview's blocker sentences verbatim: "Source environment is not serving a proven build", "Source environment's build cannot be identified", "Source environment's targets disagree on what they serve", "Target environment follows pushes", "Ankra could not read whether the target environment follows pushes", "Target environment has a deploy in flight", "Target environment is missing required secrets". A target already running the build answers 200 with state already_current and no deploy. Held to the applications.deploy permission. # Queue a platform build Source: https://docs.ankra.ai/api-reference/applications/queue-a-platform-build https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/builds Ask Ankra to build the application's image on platform infrastructure (the M2 build lane). Idempotent on (component, head_sha) while a request is live: a redelivery, a retry press and a demo request for the same commit converge on one build, and already_requested reports the join. Answers 404 for every organisation without the platform_builds flag - the surface stays invisible during the dark rollout. Browser session authentication with the X-Ankra-CSRF double-submit header; the bearer twin of this route is POST /api/v1/org/applications/{application_id}/builds. Bearer PAT authentication (Authorization: Bearer ), with the organisation selected by the token or the X-Ankra-Organisation-Id override. No CSRF header: the double-submit gate belongs to the cookie surface only. # Queue a platform build (browser session) Source: https://docs.ankra.ai/api-reference/applications/queue-a-platform-build-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/builds Ask Ankra to build the application's image on platform infrastructure (the M2 build lane). Idempotent on (component, head_sha) while a request is live: a redelivery, a retry press and a demo request for the same commit converge on one build, and already_requested reports the join. Answers 404 for every organisation without the platform_builds flag - the surface stays invisible during the dark rollout. Browser session authentication with the X-Ankra-CSRF double-submit header; the bearer twin of this route is POST /api/v1/org/applications/{application_id}/builds. # Re-bind the application to another GitHub credential Source: https://docs.ankra.ai/api-reference/applications/re-bind-the-application-to-another-github-credential https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/repository-credential Move the application onto a GitHub credential of the organisation whose App installation reaches its repository. The credential must exist (400 otherwise); whether its installation reaches the repository is reported in the answer rather than refused. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read a deployment operation and current workload verification Source: https://docs.ankra.ai/api-reference/applications/read-a-deployment-operation-and-current-workload-verification https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/deployment-verifications/{operation_id} Scoped live verification of the exact deployment operation and installation. Does not certify recovery or historical availability. # Read a deployment operation and current workload verification (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-a-deployment-operation-and-current-workload-verification-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deployment-verifications/{operation_id} Scoped live verification of the exact deployment operation and installation. Does not certify recovery or historical availability. # Read a platform build Source: https://docs.ankra.ai/api-reference/applications/read-a-platform-build https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/builds/{build_id} One platform build: its recipe, the image it pushed, or the error class that routes its failure. Answers 404 for every organisation without the platform_builds flag. Bearer PAT authentication (Authorization: Bearer ), with the organisation selected by the token or the X-Ankra-Organisation-Id override. No CSRF header: the double-submit gate belongs to the cookie surface only. # Read a platform build (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-a-platform-build-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/builds/{build_id} One platform build: its recipe, the image it pushed, or the error class that routes its failure. Answers 404 for every organisation without the platform_builds flag. # Read a platform build request Source: https://docs.ankra.ai/api-reference/applications/read-a-platform-build-request https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/builds/requests/{request_id} Read one queued build request. Queueing a build answers with a request id before there is a build to read - the build row is created when the scheduler claims the request - so this is how a caller follows that gap: status is pending until the claim, and build_id names the build from then on. Answers 404 for every organisation without the platform_builds flag. Bearer PAT authentication (Authorization: Bearer ), with the organisation selected by the token or the X-Ankra-Organisation-Id override. # Read a platform build request (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-a-platform-build-request-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/builds/requests/{request_id} Read one queued build request. Queueing a build answers with a request id before there is a build to read - the build row is created when the scheduler claims the request - so this is how a caller follows that gap: status is pending until the claim, and build_id names the build from then on. Answers 404 for every organisation without the platform_builds flag. Browser session authentication. # Read an application deploy run Source: https://docs.ankra.ai/api-reference/applications/read-an-application-deploy-run https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deploy-runs/{run_id} Read one deploy run and its per-role progress. This is the wizard's progress display: the run is driven by the server, so the read is equally correct in a tab that was never open while it ran. # Read an application's automatic build fixing setting Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-automatic-build-fixing-setting https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/auto-fix-builds Read whether Ankra automatically runs its fix-build lane - deterministic repairs first, a one-shot mission agent opening a pull request as the fallthrough - when a build of the application's tracked branch fails. On by default: the flag exists to opt out, and automatic dispatch is bounded to one mission per application per UTC day. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read an application's automatic build fixing setting (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-automatic-build-fixing-setting-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/auto-fix-builds Read whether Ankra automatically runs its fix-build lane - deterministic repairs first, a one-shot mission agent opening a pull request as the fallthrough - when a build of the application's tracked branch fails. On by default: the flag exists to opt out, and automatic dispatch is bounded to one mission per application per UTC day. # Read an application's proposed setup contract Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-proposed-setup-contract https://platform.ankra.app/openapi.json get /org/applications/{application_id}/setup-contract Read the setup Ankra proposed for this application before anything is deployed: the detected stack, whether a database was generated, and the deploy option contract of record. The contract and database each carry a three-valued status - a proposal Ankra could not read answers "unknown", never an empty option list. # Read an application's push-to-deploy setting Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-push-to-deploy-setting https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/auto-deploy Read whether a successful build of the application's tracked branch redeploys it automatically, together with the newest build observed on that branch. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read an application's push-to-deploy setting (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-push-to-deploy-setting-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/auto-deploy Read whether a successful build of the application's tracked branch redeploys it automatically, together with the newest build observed on that branch. # Read an application's rollback targets Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-rollback-targets https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/rollback-targets What this application's deployment on the named cluster could be rolled back to: the shape of deployment found, the image tag it is running, the builds still in the registry, whether push-to-deploy is armed, and the list of things a rollback does NOT undo. Bearer personal access token; the CLI twin of the browser route. # Read an application's rollback targets (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-rollback-targets-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/rollback-targets What this application's deployment on the named cluster could be rolled back to: the shape of deployment found, the image tag it is running, the builds still in the registry, whether push-to-deploy is armed, and the list of things a rollback does NOT undo. Browser session authentication; the bearer twin is GET /api/v1/org/applications/{application_id}/rollback-targets. # Read an application's setup pull request auto-merge setting Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-setup-pull-request-auto-merge-setting https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/auto-merge-setup-pull-requests Read whether Ankra may merge this application's setup pull requests on its own once every check and Ankra's proof of the head are green. enabled is the application's own answer, or null when it follows its organisation's auto_merge_setup_pull_requests; effective is what the merge acts on: on only when the application says on, or when it follows an organisation that opted in. Readable by any member. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read an application's setup pull request auto-merge setting (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-an-applications-setup-pull-request-auto-merge-setting-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/auto-merge-setup-pull-requests Read whether Ankra may merge this application's setup pull requests on its own once every check and Ankra's proof of the head are green. enabled is the application's own answer, or null when it follows its organisation's auto_merge_setup_pull_requests; effective is what the merge acts on: on only when the application says on, or when it follows an organisation that opted in. Readable by any member. # Read operating expectations Source: https://docs.ankra.ai/api-reference/applications/read-operating-expectations https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/environments/{environment_id}/operating-expectations Environment-scoped operating intent. Targets do not provision or certify runtime controls. Adoption checks the reviewed revision and commits intent with an audit record atomically. # Read operating expectations (browser session) Source: https://docs.ankra.ai/api-reference/applications/read-operating-expectations-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/environments/{environment_id}/operating-expectations Environment-scoped operating intent. Targets do not provision or certify runtime controls. Adoption checks the reviewed revision and commits intent with an audit record atomically. # Read the demo password Source: https://docs.ankra.ai/api-reference/applications/read-the-demo-password https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/demo-config/protection/reveal Answer the password that opens this application's protected demos, for a member who may configure demos. Ankra keeps each minted password encrypted in its secret store and verifies it against the enforced hash before answering, so the answer always opens the current previews. The response is not cacheable. 409 demo_not_protected when the demos have no password; 409 demo_password_not_retrievable when the password was set before Ankra kept preview passwords (rotate it to get one that can be read); 503 secret_store_unavailable when the platform runs without a secret store. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read the organisation's application CI settings Source: https://docs.ankra.ai/api-reference/applications/read-the-organisations-application-ci-settings https://platform.ankra.app/openapi.json get /api/v1/org/applications/settings Read the organisation's application CI settings: the GitHub Actions runner label every pipeline Ankra generates runs on, and the setup pull-request auto-merge opt-in. An organisation that has never chosen a runner reads the default (ubuntu-latest) with is_default true, which is what its existing pipelines already carry; auto_merge_setup_pull_requests defaults to false. Readable by any member. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Reconcile an application Source: https://docs.ankra.ai/api-reference/applications/reconcile-an-application https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/reconcile Hands the application back to the platform reconciler; an application whose analysis failed is reset so it is analysed again. Requires `applications.deploy`, and answers 422 with a message when the application cannot be reconciled. # Remove an application deployment from one cluster Source: https://docs.ankra.ai/api-reference/applications/remove-an-application-deployment-from-one-cluster https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/deployments/remove Remove one deployment of this application: take it off one cluster and namespace while the application and its other deployments stay. The installation is marked removing and the same undeploy job an application delete enqueues per installation is enqueued for it; the job removes the deploy stack it can prove belongs to this application (shared operators are kept) and deletes the installation once the cluster has it. The application, its env secrets, images and pipeline history are kept. While the removal runs, a deploy to the same cluster and namespace is refused with 409. Held to the applications.deploy permission. Bearer personal access token; the CLI twin of the browser route (no CSRF header). # Remove an application deployment from one cluster (browser session) Source: https://docs.ankra.ai/api-reference/applications/remove-an-application-deployment-from-one-cluster-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/deployments/remove Remove one deployment of this application: take it off one cluster and namespace while the application and its other deployments stay. The installation is marked removing and the same undeploy job an application delete enqueues per installation is enqueued for it; the job removes the deploy stack it can prove belongs to this application (shared operators are kept) and deletes the installation once the cluster has it. The application, its env secrets, images and pipeline history are kept. While the removal runs, a deploy to the same cluster and namespace is refused with 409. Held to the applications.deploy permission. Browser session authentication with the X-Ankra-CSRF double-submit header; the bearer twin is POST /api/v1/org/applications/{application_id}/deployments/remove. # Rename an application environment Source: https://docs.ankra.ai/api-reference/applications/rename-an-application-environment https://platform.ankra.app/openapi.json patch /api/v1/org/applications/{application_id}/environments/{environment_id} Renames one named environment of the application. Only the name changes: its identity, role, order and deployments stay as they are, and the change is audited. An environment still named after its cluster cannot be renamed yet (409 environment_named_after_cluster). Held to the applications.write permission. Bearer personal access token; the CLI twin of the browser route (no CSRF header). # Rename an application environment (browser session) Source: https://docs.ankra.ai/api-reference/applications/rename-an-application-environment-browser-session https://platform.ankra.app/openapi.json patch /org/applications/{application_id}/environments/{environment_id} Renames one named environment of the application. Only the name changes: its identity, role, order and deployments stay as they are, and the change is audited. An environment still named after its cluster cannot be renamed yet (409 environment_named_after_cluster). Held to the applications.write permission. # Reset an application's AI configuration Source: https://docs.ankra.ai/api-reference/applications/reset-an-applications-ai-configuration https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id}/ai-config Drop this application's AI lane overrides entirely so every lane follows the organisation default again, and answer with the resolved configuration. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF header). # Restore the shared default in one environment Source: https://docs.ankra.ai/api-reference/applications/restore-the-shared-default-in-one-environment https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/env-secrets/{secret_key}/inherit Remove only this environment override or suppression. Its next apply or deployment consumes the shared default if one exists. Secret values are never returned. # Restore the shared default in one environment (browser session) Source: https://docs.ankra.ai/api-reference/applications/restore-the-shared-default-in-one-environment-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/env-secrets/{secret_key}/inherit Remove only this environment override or suppression. Its next apply or deployment consumes the shared default if one exists. Secret values are never returned. # Resume Ankra for an application Source: https://docs.ankra.ai/api-reference/applications/resume-ankra-for-an-application https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id}/autonomy-pause Lifts the application's pause, so what Ankra does unasked on its placements runs again under the application's own autonomy setting. Resuming an application that is not paused changes nothing and answers its current state. Each change is audited with who made it and the pause it lifted. Requires applications.write. # Resume Ankra for an application (browser session) Source: https://docs.ankra.ai/api-reference/applications/resume-ankra-for-an-application-browser-session https://platform.ankra.app/openapi.json delete /org/applications/{application_id}/autonomy-pause Lifts the application's pause, so what Ankra does unasked on its placements runs again under the application's own autonomy setting. Resuming an application that is not paused changes nothing and answers its current state. Each change is audited with who made it and the pause it lifted. Requires applications.write. # Retry an application Source: https://docs.ankra.ai/api-reference/applications/retry-an-application https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/retry Re-runs the repository analysis of an application that is up. Requires `applications.deploy`, and answers 422 when the application is not in a retryable state or already has an active job. # Retry the fix-build mission for a failed pipeline run Source: https://docs.ankra.ai/api-reference/applications/retry-the-fix-build-mission-for-a-failed-pipeline-run https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/fix-build Dispatch the fix-build mission for one failed Ankra Pipelines run of the application again: the same mission, evidence and checks the automatic fix loop uses (a setup pull request or pull request run commits the fix to its own branch, a tracked-branch run opens a pull request). Use it after a fix mission ended without the fix. While a fix-build mission for the run's branch is still running nothing new is dispatched and the answer names that mission (outcome already_running). A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Revise an application's proposed setup contract Source: https://docs.ankra.ai/api-reference/applications/revise-an-applications-proposed-setup-contract https://platform.ankra.app/openapi.json patch /org/applications/{application_id}/setup-contract Change named defaults on the application's deploy option contract. The revised .ankra/ankra.yaml is committed to the setup branch and the stored parameters are reprojected from it; nothing is deployed. The revision is all-or-nothing: any refused member leaves the proposal exactly as it was. Every answered request is a 200 whose outcome the client must switch on - a refusal carries its reasons in refusals rather than as an empty changes list. # Revoke a Launch Plan approval Source: https://docs.ankra.ai/api-reference/applications/revoke-a-launch-plan-approval https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/launch-plan/approvals/{approval_id}/revoke Ends a plan-level approval before its expiry; nothing more can be claimed under it. Revoking a revoked approval changes nothing. Requires applications.write. Audited. # Revoke a Launch Plan approval (browser session) Source: https://docs.ankra.ai/api-reference/applications/revoke-a-launch-plan-approval-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/launch-plan/approvals/{approval_id}/revoke Ends a plan-level approval before its expiry; nothing more can be claimed under it. Revoking a revoked approval changes nothing. Requires applications.write. Audited. # Revoke the application's registry robot Source: https://docs.ankra.ai/api-reference/applications/revoke-the-applications-registry-robot https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id}/registry-robot Delete the push robot Ankra minted for this application from its registry, drop the credential holding its login, and forget the record. The repository's Actions secrets are left holding a login that no longer works, which is the point of a revoke. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Roll an application deploy back Source: https://docs.ankra.ai/api-reference/applications/roll-an-application-deploy-back https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/rollbacks Put a previously published image back on this application's deployment. The tag is confirmed present in the registry first, the push-to-deploy watermark is moved onto the newest observed build so the convergence loop does not undo the rollback, and the tag is then written through the same narrow path a push-triggered deploy uses. Held to the applications.deploy permission; a scoped token must carry the applications.deploy scope. Bearer personal access token; the CLI twin of the browser route (no CSRF header). # Roll an application deploy back (browser session) Source: https://docs.ankra.ai/api-reference/applications/roll-an-application-deploy-back-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/rollbacks Put a previously published image back on this application's deployment. The tag is confirmed present in the registry first, the push-to-deploy watermark is moved onto the newest observed build so the convergence loop does not undo the rollback, and the tag is then written through the same narrow path a push-triggered deploy uses. Held to the applications.deploy permission. Browser session authentication with the X-Ankra-CSRF double-submit header; the bearer twin is POST /api/v1/org/applications/{application_id}/rollbacks. # Roll back a data migration Source: https://docs.ankra.ai/api-reference/applications/roll-back-a-data-migration https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/launch-plan/data-migration/runs/{run_id}/rollback Asks a data migration to roll back: the pre-cutover environment goes back and every vendor source is made writable again. The request is recorded and the next pass applies it. Refused before the cutover has started, once the rollback window has closed, for a finished run, and while a rollback is requested or running. Nothing at a vendor is deleted. Requires applications.write at the route and applications.deploy on the run's cluster. Audited. # Roll back a data migration (browser session) Source: https://docs.ankra.ai/api-reference/applications/roll-back-a-data-migration-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/launch-plan/data-migration/runs/{run_id}/rollback Asks a data migration to roll back: the pre-cutover environment goes back and every vendor source is made writable again. The request is recorded and the next pass applies it. Refused before the cutover has started, once the rollback window has closed, for a finished run, and while a rollback is requested or running. Nothing at a vendor is deleted. Requires applications.write at the route and applications.deploy on the run's cluster. Audited. # Rotate the demo password Source: https://docs.ankra.ai/api-reference/applications/rotate-the-demo-password https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/demo-config/protection/rotate Mint a new password for this application's protected demos and answer it. Ankra keeps it encrypted in its secret store, so it can be read again through POST demo-config/protection/reveal; running demos keep the previous password until their next redeploy (a pull request push redeploys its preview). 400 when the application's demos are not protected: turn protection on through PUT demo-config first, which mints the first password. # Set an application environment secret Source: https://docs.ankra.ai/api-reference/applications/set-an-application-environment-secret https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/env-secrets/{secret_key} Store the value of one environment secret, creating the entry or rotating it in place. The value goes to the platform secret store; the deploy renders it into the -env Secret, encrypted with SOPS to the organisation's age recipient before it is stored or pushed to Git. The value is not echoed in the response and is never logged. Takes effect on the application's next deploy. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # Set an application environment secret (browser session) Source: https://docs.ankra.ai/api-reference/applications/set-an-application-environment-secret-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/env-secrets/{secret_key} Store the value of one environment secret, creating the entry or rotating it in place. The value goes to the platform secret store; the deploy renders it into the -env Secret, encrypted with SOPS to the organisation's age recipient before it is stored or pushed to Git. The value is not echoed in the response and is never logged. Takes effect on the application's next deploy. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). With environment_id, resolve or modify only that environment; deletion suppresses a shared default locally, and apply never changes another target. Without it, existing shared catalogue behaviour is retained; all-target apply still preserves environment overrides. # Set an application's automatic build fixing setting Source: https://docs.ankra.ai/api-reference/applications/set-an-applications-automatic-build-fixing-setting https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/auto-fix-builds Turn automatic build fixing off or back on for this application. The flag is read fresh on every failed build, so opting out needs no redeploy; turning it back on restores the default. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Set an application's automatic build fixing setting (browser session) Source: https://docs.ankra.ai/api-reference/applications/set-an-applications-automatic-build-fixing-setting-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/auto-fix-builds Turn automatic build fixing off or back on for this application. The flag is read fresh on every failed build, so opting out needs no redeploy; turning it back on restores the default. # Set an application's push-to-deploy setting Source: https://docs.ankra.ai/api-reference/applications/set-an-applications-push-to-deploy-setting https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/auto-deploy Turn push-to-deploy on or off for this application. Enabling records the build currently observed as every installation's baseline, so switching it on is not itself a deploy: only a build newer than that one is rolled out. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). Stored environment IDs scope the write to their active installations across targets. A legacy cluster ID is accepted only when it names one complete environment. No installation identity is assigned by this operation. # Set an application's push-to-deploy setting (browser session) Source: https://docs.ankra.ai/api-reference/applications/set-an-applications-push-to-deploy-setting-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/auto-deploy Turn push-to-deploy on or off for this application. Enabling records the build currently observed as every installation's baseline, so switching it on is not itself a deploy: only a build newer than that one is rolled out. Stored environment IDs scope the write to their active installations across targets. A legacy cluster ID is accepted only when it names one complete environment. No installation identity is assigned by this operation. # Set an application's setup pull request auto-merge setting Source: https://docs.ankra.ai/api-reference/applications/set-an-applications-setup-pull-request-auto-merge-setting https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/auto-merge-setup-pull-requests Set whether Ankra may merge this application's setup pull requests on its own: true or false is the application's own answer and wins over its organisation's setting either way, and null clears it so the application follows its organisation again. Read fresh on every merge decision, so it needs no redeploy. Organisation administrators only. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Set an application's setup pull request auto-merge setting (browser session) Source: https://docs.ankra.ai/api-reference/applications/set-an-applications-setup-pull-request-auto-merge-setting-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/auto-merge-setup-pull-requests Set whether Ankra may merge this application's setup pull requests on its own: true or false is the application's own answer and wins over its organisation's setting either way, and null clears it so the application follows its organisation again. Read fresh on every merge decision, so it needs no redeploy. Organisation administrators only. # Set the organisation's CI runner label Source: https://docs.ankra.ai/api-reference/applications/set-the-organisations-ci-runner-label https://platform.ankra.app/openapi.json put /api/v1/org/applications/settings Set the organisation's application CI settings. Organisation admins only. ci_runner_label is the GitHub Actions runner label generated pipelines run on, validated rather than accepted as free text: one label of at most 64 characters, starting and ending with a letter or digit, containing only letters, digits, '-', '_' and '.' (for example ubuntu-latest, depot-ubuntu-24.04, self-hosted). Anything else answers 400 - a label outside that grammar produces a workflow GitHub cannot parse or run, and the generators concatenate it into a YAML scalar. An explicit null clears the choice and puts every future generation back on the default. auto_merge_setup_pull_requests opts the organisation into the platform merging an application's setup pull request itself once the PR's checks pass (squash; branch protection is honoured, never forced). Each member only moves when named: an absent member leaves the stored value untouched, and a body naming neither answers 422. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Set who runs an application Source: https://docs.ankra.ai/api-reference/applications/set-who-runs-an-application https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/autonomy Store the application's own "who runs it" layer: a level for every area, per-area levels and limits. It replaces the stored layer. Anyone who may write the application may set anything within what the organisation allows; asking for more needs an organisation admin and is stored as widened_by_admin. Every write is audited. # Set who runs an application (browser session) Source: https://docs.ankra.ai/api-reference/applications/set-who-runs-an-application-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/autonomy Store the application's own "who runs it" layer: a level for every area, per-area levels and limits. It replaces the stored layer. Anyone who may write the application may set anything within what the organisation allows; asking for more needs an organisation admin and is stored as widened_by_admin. Every write is audited. # Set who runs an environment Source: https://docs.ankra.ai/api-reference/applications/set-who-runs-an-environment https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/environments/{environment_id}/autonomy Store one environment's "who runs it" layer, which applies over the application's. It replaces the stored layer and follows the same rule: more than the environment inherits needs an organisation admin. Every write is audited. # Set who runs an environment (browser session) Source: https://docs.ankra.ai/api-reference/applications/set-who-runs-an-environment-browser-session https://platform.ankra.app/openapi.json put /org/applications/{application_id}/environments/{environment_id}/autonomy Store one environment's "who runs it" layer, which applies over the application's. It replaces the stored layer and follows the same rule: more than the environment inherits needs an organisation admin. Every write is audited. # Show which GitHub credential the application's repository calls ride on Source: https://docs.ankra.ai/api-reference/applications/show-which-github-credential-the-applications-repository-calls-ride-on https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/repository-credential The GitHub credential the application names, and whether it resolves to a GitHub App installation Ankra can mint a token for. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Start a Launch Plan data migration Source: https://docs.ankra.ai/api-reference/applications/start-a-launch-plan-data-migration https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/launch-plan/data-migration/runs Starts the data migration a plan-level approval covers (ankra-bjkszh.15): moves the application's data off the vendors the approval selected, through the backup vault it named, to the in-cluster servers in targets, and flips the variables in environment at the cutover. Starting claims every data-migration action of the plan under the approval in one transaction, through the same gate a single claim passes, so it is refused unless the approval is the caller's own, unexpired, unrevoked, still bound to the live plan, lists every data-migration action, and the caller still holds each action's permission. The environment values go to the secret store only and are never answered. One data migration is in flight per application. Requires applications.write. Audited, refusals included. # Start a Launch Plan data migration (browser session) Source: https://docs.ankra.ai/api-reference/applications/start-a-launch-plan-data-migration-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/launch-plan/data-migration/runs Starts the data migration a plan-level approval covers (ankra-bjkszh.15): moves the application's data off the vendors the approval selected, through the backup vault it named, to the in-cluster servers in targets, and flips the variables in environment at the cutover. Starting claims every data-migration action of the plan under the approval in one transaction, through the same gate a single claim passes, so it is refused unless the approval is the caller's own, unexpired, unrevoked, still bound to the live plan, lists every data-migration action, and the caller still holds each action's permission. The environment values go to the secret store only and are never answered. One data migration is in flight per application. Requires applications.write. Audited, refusals included. # Start or resume an application deploy run Source: https://docs.ankra.ai/api-reference/applications/start-or-resume-an-application-deploy-run https://platform.ankra.app/openapi.json post /org/applications/{application_id}/deploy-runs Hand the deploy wizard's whole ordered multi-stack deploy to the server. The run is persisted before anything is created - roles, drafts, and the stack name derived for each - and the maintenance supervisor creates each stack in turn, waiting for every stack that has another behind it to report up. Closing the browser tab no longer strands the remaining stacks. A request made while a run for the same application and cluster is already in flight joins that run and answers with resumed=true rather than starting a rival queue. Browser session authentication with the X-Ankra-CSRF double-submit header only (no bearer twin). # Stop an application demo Source: https://docs.ankra.ai/api-reference/applications/stop-an-application-demo https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id}/demos/{workspace_id} Stop a demo and tear its namespace down. A malformed workspace_id is reported as the same 404 as an unknown demo. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Update an application's AI configuration Source: https://docs.ankra.ai/api-reference/applications/update-an-applications-ai-configuration https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/ai-config Patch this application's AI lane overrides and answer with the freshly resolved configuration. Lanes the body does not mention are left untouched. A lane sent with override false is removed from the application's override so it follows the organisation again; a lane sent with override true stores whichever of enabled and settings the body carries. Overrides are stored on the ai-gateway's per-repository binding profile, the same rows that gate the webhook events. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF header). # Update an application's demo configuration Source: https://docs.ankra.ai/api-reference/applications/update-an-applications-demo-configuration https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/demo-config Replace the application's saved demo defaults. env and database are replaced wholesale on every PUT. The stack_profile and base_stack blocks are written only when their key is present in the body: an absent key preserves the stored designation, an explicit null clears it, so a client that predates either block cannot silently drop a demo's dependencies. Secret environment values are submitted and echoed as ${SECRET_SLOT:} sentinels. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication plus the X-Ankra-CSRF double-submit header). # Wake an application deployment Source: https://docs.ankra.ai/api-reference/applications/wake-an-application-deployment https://platform.ankra.app/openapi.json post /org/applications/{application_id}/deployments/wake Wake a parked deployment of this application: restore the remembered replica count (parked_replicas, or the descriptor default when none was pinned), remove the park marker, and enqueue the platform's existing deploy job to apply it. Refused with 409 when the deployment is not parked. Held to the applications.deploy permission. Browser session authentication with the X-Ankra-CSRF double-submit header only (no bearer twin). # Connect Avura Source: https://docs.ankra.ai/api-reference/avura/connect-avura https://platform.ankra.app/openapi.json put /api/v1/org/avura/connection Store or rotate the platform service token an Avura organization minted for Ankra Platform. The token is verified against base_url before anything is stored. Requires an organisation admin. # Create Avura DNS Record Source: https://docs.ankra.ai/api-reference/avura/create-avura-dns-record https://platform.ankra.app/openapi.json post /api/v1/org/avura/domains/{domain_id}/records Creates a DNS record in a shared Avura domain and answers 201 with it. `record_type` is one of A, AAAA, CAA, CNAME, MX, NS, SRV or TXT; `ttl` is optional (60-604800 seconds). Requires an org admin. # Delete Avura DNS Record Source: https://docs.ankra.ai/api-reference/avura/delete-avura-dns-record https://platform.ankra.app/openapi.json delete /api/v1/org/avura/domains/{domain_id}/records/{record_id} Deletes a record from a shared Avura domain and answers 204. The record must belong to that domain. Requires an org admin. # Disconnect Avura Source: https://docs.ankra.ai/api-reference/avura/disconnect-avura https://platform.ankra.app/openapi.json delete /api/v1/org/avura/connection Remove the linked Avura organization. Refused with 409 while custom DNS zones use the connection. # Get Avura Connection Source: https://docs.ankra.ai/api-reference/avura/get-avura-connection https://platform.ankra.app/openapi.json get /api/v1/org/avura/connection The linked Avura organization and a live count of the domains it shared with Ankra Platform. connected is false when none is linked; zone_count is null when Avura could not be asked. # List Avura DNS Records Source: https://docs.ankra.ai/api-reference/avura/list-avura-dns-records https://platform.ankra.app/openapi.json get /api/v1/org/avura/domains/{domain_id}/records Lists the DNS records of one shared Avura domain. The domain must be one the connected Avura workspace shares; any other id answers 404. # List Avura Domains Source: https://docs.ankra.ai/api-reference/avura/list-avura-domains https://platform.ankra.app/openapi.json get /api/v1/org/avura/domains Lists the domains the organisation's connected Avura workspace shares with Ankra (the ones opted in with Use with Ankra Platform), read live through the org's `avura` credential. # Update Avura DNS Record Source: https://docs.ankra.ai/api-reference/avura/update-avura-dns-record https://platform.ankra.app/openapi.json patch /api/v1/org/avura/domains/{domain_id}/records/{record_id} Updates the content and, optionally, the TTL of a record in a shared Avura domain. The record must belong to that domain. Requires an org admin. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/aws-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/vms Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Add a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/add-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/vms Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Add node group Source: https://docs.ankra.ai/api-reference/aws-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/node-groups Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Add node group (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/add-node-group-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/node-groups Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/aws-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Check Kubernetes upgrade readiness (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/check-kubernetes-upgrade-readiness-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Create an AWS EC2 cluster Source: https://docs.ankra.ai/api-reference/aws-clusters/create-an-aws-ec2-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/aws Bearer PAT authentication; RBAC permission `clusters.create`. Creates a self-managed k3s / kubeadm cluster on EC2 (ADR 0015), in a VPC Ankra creates when `vpc_id` is omitted (from `network_ip_range` over `availability_zones`, NAT-gateway egress by default) or in the customer's VPC when `vpc_id`, `node_subnet_ids` and `bastion_subnet_id` are given. The preflight runs first and the first error item is the refusal. Defaults: t3.medium nodes, a t3.small bastion, Ubuntu 24.04 on amd64, a 40 GiB encrypted gp3 root volume, one control plane and one worker, Cilium for either distribution, 10.0.0.0/16 for a created VPC over one zone (three when `control_plane_count` >= 3). `egress_mode` omitted is `nat_gateway` for a created VPC and, for an adopted one, resolved by preflight (`existing` when every node subnet already has egress, `bastion_nat` when none has and none carries a foreign instance). Flannel is accepted but cannot enforce the IMDS guard, so the preflight reports a warning for it; the AWS cloud-controller-manager is always installed. Answers 404 while the organisation's `aws_provider` feature flag is off. # Create an AWS EC2 cluster (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/create-an-aws-ec2-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws Browser session authentication; RBAC permission `clusters.create`. Creates a self-managed k3s / kubeadm cluster on EC2 (ADR 0015), in a VPC Ankra creates when `vpc_id` is omitted (from `network_ip_range` over `availability_zones`, NAT-gateway egress by default) or in the customer's VPC when `vpc_id`, `node_subnet_ids` and `bastion_subnet_id` are given. The preflight runs first and the first error item is the refusal. Defaults: t3.medium nodes, a t3.small bastion, Ubuntu 24.04 on amd64, a 40 GiB encrypted gp3 root volume, one control plane and one worker, Cilium for either distribution, 10.0.0.0/16 for a created VPC over one zone (three when `control_plane_count` >= 3). `egress_mode` omitted is `nat_gateway` for a created VPC and, for an adopted one, resolved by preflight (`existing` when every node subnet already has egress, `bastion_nat` when none has and none carries a foreign instance). Flannel is accepted but cannot enforce the IMDS guard, so the preflight reports a warning for it; the AWS cloud-controller-manager is always installed. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/aws-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/aws/{cluster_id}/vms/{vm_id} Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/delete-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/aws/{cluster_id}/vms/{vm_id} Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete node group Source: https://docs.ankra.ai/api-reference/aws-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name} Bearer PAT authentication; RBAC permission `clusters.delete`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Delete node group (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/delete-node-group-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/aws/{cluster_id}/node-groups/{group_name} Browser session authentication; RBAC permission `clusters.delete`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Deprovision an AWS EC2 cluster Source: https://docs.ankra.ai/api-reference/aws-clusters/deprovision-an-aws-ec2-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/aws/{cluster_id} Bearer PAT authentication; RBAC permission `clusters.delete`. Ordered teardown preserves explicitly retained storage and reports orphaned provider IDs. Answers 404 while the organisation's `aws_provider` feature flag is off. # Deprovision an AWS EC2 cluster (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/deprovision-an-aws-ec2-cluster-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/aws/{cluster_id} Browser session authentication; RBAC permission `clusters.delete`. Ordered teardown preserves explicitly retained storage and reports orphaned provider IDs. Answers 404 while the organisation's `aws_provider` feature flag is off. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/aws-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Diagnose bastion (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/diagnose-bastion-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/bastion/diagnose Browser session authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Diagnose node Source: https://docs.ankra.ai/api-reference/aws-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `aws_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get AWS cluster quotas Source: https://docs.ankra.ai/api-reference/aws-clusters/get-aws-cluster-quotas https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/quotas Bearer PAT authentication; RBAC permission `clusters.read`. Quota reporting is not implemented for self-managed AWS clusters yet: the route answers 404 with a detail saying so. Documented so the shape is stable once the vCPU / elastic-IP report ships. Answers 404 while the organisation's `aws_provider` feature flag is off. # Get AWS cluster quotas (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/get-aws-cluster-quotas-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/quotas Browser session authentication; RBAC permission `clusters.read`. Quota reporting is not implemented for self-managed AWS clusters yet: the route answers 404 with a detail saying so. Documented so the shape is stable once the vCPU / elastic-IP report ships. Answers 404 while the organisation's `aws_provider` feature flag is off. # Get AWS pricing Source: https://docs.ankra.ai/api-reference/aws-clusters/get-aws-pricing https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/pricing Bearer PAT authentication; RBAC permission `clusters.read`. Instance-type on-demand prices plus the encrypted gp3 root-volume price, with explicit incomplete-pricing reasons. Answers 404 while the organisation's `aws_provider` feature flag is off. # Get AWS pricing (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/get-aws-pricing-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/pricing Browser session authentication; RBAC permission `clusters.read`. Instance-type on-demand prices plus the encrypted gp3 root-volume price, with explicit incomplete-pricing reasons. Answers 404 while the organisation's `aws_provider` feature flag is off. # Get bastion health Source: https://docs.ankra.ai/api-reference/aws-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. Answers 404 while the organisation's `aws_provider` feature flag is off. # Get bastion health (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/get-bastion-health-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/bastion/health Browser session authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. Answers 404 while the organisation's `aws_provider` feature flag is off. # Latest restorable state snapshot of a AWS cluster Source: https://docs.ankra.ai/api-reference/aws-clusters/latest-restorable-state-snapshot-of-a-aws-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the AWS cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List AWS availability zones Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-availability-zones https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/availability-zones Bearer PAT authentication; RBAC permission `clusters.read`. Lists the region's availability zones. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS availability zones (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-availability-zones-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/availability-zones Browser session authentication; RBAC permission `clusters.read`. Lists the region's availability zones. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS instance types Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-instance-types https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/instance-types Bearer PAT authentication; RBAC permission `clusters.read`. Lists the amd64 instance types offered in the region with on-demand prices; a nil price is unknown, never free, and `pricing_complete` says whether every type was priced. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS instance types (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-instance-types-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/instance-types Browser session authentication; RBAC permission `clusters.read`. Lists the amd64 instance types offered in the region with on-demand prices; a nil price is unknown, never free, and `pricing_complete` says whether every type was priced. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS regions Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-regions https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/regions Bearer PAT authentication; RBAC permission `clusters.read`. Lists the regions enabled for the credential's account. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS regions (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-regions-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/regions Browser session authentication; RBAC permission `clusters.read`. Lists the regions enabled for the credential's account. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS subnets Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-subnets https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/subnets Bearer PAT authentication; RBAC permission `clusters.read`. Lists a VPC's subnets with each one's IPv4 default-route classification and the count of instances Ankra did not create; both are three-state and never collapse a failed read into a verdict. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS subnets (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-subnets-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/subnets Browser session authentication; RBAC permission `clusters.read`. Lists a VPC's subnets with each one's IPv4 default-route classification and the count of instances Ankra did not create; both are three-state and never collapse a failed read into a verdict. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS Ubuntu images Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-ubuntu-images https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/images Bearer PAT authentication; RBAC permission `clusters.read`. Lists the Ubuntu series whose Canonical AMI parameter resolves in the region and the architectures offered (amd64 only until the image catalogue is arm64-audited). Answers 503 rather than an empty list when no series could be resolved. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS Ubuntu images (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-ubuntu-images-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/images Browser session authentication; RBAC permission `clusters.read`. Lists the Ubuntu series whose Canonical AMI parameter resolves in the region and the architectures offered (amd64 only until the image catalogue is arm64-audited). Answers 503 rather than an empty list when no series could be resolved. Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS VPCs Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-vpcs https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/vpcs Bearer PAT authentication; RBAC permission `clusters.read`. Lists the region's VPCs with their CIDR and DHCP domain-name state (set / empty / unknown). Answers 404 while the organisation's `aws_provider` feature flag is off. # List AWS VPCs (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-aws-vpcs-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/vpcs Browser session authentication; RBAC permission `clusters.read`. Lists the region's VPCs with their CIDR and DHCP domain-name state (set / empty / unknown). Answers 404 while the organisation's `aws_provider` feature flag is off. # List instance types for an AWS cluster Source: https://docs.ankra.ai/api-reference/aws-clusters/list-instance-types-for-an-aws-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/instance-types Bearer PAT authentication; RBAC permission `clusters.read`. The instance-type catalog in the cluster's own region and credential. Answers 404 while the organisation's `aws_provider` feature flag is off. # List instance types for an AWS cluster (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-instance-types-for-an-aws-cluster-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/instance-types Browser session authentication; RBAC permission `clusters.read`. The instance-type catalog in the cluster's own region and credential. Answers 404 while the organisation's `aws_provider` feature flag is off. # List node groups Source: https://docs.ankra.ai/api-reference/aws-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/node-groups Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List node groups (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-node-groups-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/node-groups Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List nodes Source: https://docs.ankra.ai/api-reference/aws-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/nodes Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List nodes (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-nodes-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/nodes Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List standalone VMs Source: https://docs.ankra.ai/api-reference/aws-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/vms Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # List standalone VMs (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/list-standalone-vms-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/vms Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Preflight an AWS EC2 cluster create Source: https://docs.ankra.ai/api-reference/aws-clusters/preflight-an-aws-ec2-cluster-create https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/preflight Bearer PAT authentication; RBAC permission `clusters.create`. Runs every check the create runs against AWS without creating anything. Each item is three-state: `ok`, `warning` (a read Ankra could not perform, or a caveat such as flannel not enforcing the IMDS guard), or `error`. `network_ownership` says which shape the request describes; a created network is checked for its CIDR plan (`cidr`), its zones (`zones` - chosen here when the request names none), the VPC, NAT-gateway and elastic-IP quotas, while an adopted VPC is checked for its VPC, subnets, egress routes and bastion subnet. `resolved_egress_mode` is the mode the create will run under, null when it could not be decided; `resolved_availability_zones` the zones the cluster will spread over. Answers 404 while the organisation's `aws_provider` feature flag is off. # Preflight an AWS EC2 cluster create (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/preflight-an-aws-ec2-cluster-create-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/preflight Browser session authentication; RBAC permission `clusters.create`. Runs every check the create runs against AWS without creating anything. Each item is three-state: `ok`, `warning` (a read Ankra could not perform, or a caveat such as flannel not enforcing the IMDS guard), or `error`. `network_ownership` says which shape the request describes; a created network is checked for its CIDR plan (`cidr`), its zones (`zones` - chosen here when the request names none), the VPC, NAT-gateway and elastic-IP quotas, while an adopted VPC is checked for its VPC, subnets, egress routes and bastion subnet. `resolved_egress_mode` is the mode the create will run under, null when it could not be decided; `resolved_availability_zones` the zones the cluster will spread over. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/aws-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/control-plane Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read control-plane sizing (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-control-plane-sizing-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/control-plane Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read Kubernetes version Source: https://docs.ankra.ai/api-reference/aws-clusters/read-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/k8s-version Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read Kubernetes version (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-kubernetes-version-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/k8s-version Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node Source: https://docs.ankra.ai/api-reference/aws-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/nodes/{node_id} Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-node-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/nodes/{node_id} Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/aws-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `aws_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node-group autoscaling Source: https://docs.ankra.ai/api-reference/aws-clusters/read-node-group-autoscaling https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name}/autoscaling Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read node-group autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-node-group-autoscaling-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/node-groups/{group_name}/autoscaling Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read private cluster access Source: https://docs.ankra.ai/api-reference/aws-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/access-info Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read private cluster access (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-private-cluster-access-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/access-info Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read SSH keys Source: https://docs.ankra.ai/api-reference/aws-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/ssh-keys Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-ssh-keys-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/ssh-keys Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read worker count Source: https://docs.ankra.ai/api-reference/aws-clusters/read-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/worker-count Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Read worker count (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/read-worker-count-browser-session https://platform.ankra.app/openapi.json get /org/clusters/aws/{cluster_id}/worker-count Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/vms/{vm_id}/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/vms/{vm_id}/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/control-plane/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize control-plane nodes (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-control-plane-nodes-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/control-plane/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize node group Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name}/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize node group (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-node-group-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/node-groups/{group_name}/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize the public gateway Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-the-public-gateway https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/bastion/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resize the public gateway (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/resize-the-public-gateway-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/bastion/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Restart node Source: https://docs.ankra.ai/api-reference/aws-clusters/restart-node https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/nodes/{node_id}/restart Bearer PAT authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Restart node (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/restart-node-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/nodes/{node_id}/restart Browser session authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/aws-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/ssh-keys/resync Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Resync SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/resync-ssh-keys-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/ssh-keys/resync Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale node group Source: https://docs.ankra.ai/api-reference/aws-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name}/scale Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale node group (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/scale-node-group-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/node-groups/{group_name}/scale Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale workers Source: https://docs.ankra.ai/api-reference/aws-clusters/scale-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/scale-workers Bearer PAT authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Scale workers (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/scale-workers-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/scale-workers Browser session authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Start a stopped cluster Source: https://docs.ankra.ai/api-reference/aws-clusters/start-a-stopped-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/start Bearer PAT authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Start a stopped cluster (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/start-a-stopped-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/start Browser session authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Stop a cluster Source: https://docs.ankra.ai/api-reference/aws-clusters/stop-a-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/stop Bearer PAT authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Stop a cluster (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/stop-a-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/stop Browser session authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # The persistent volumes deleting an AWS cluster removes Source: https://docs.ankra.ai/api-reference/aws-clusters/the-persistent-volumes-deleting-an-aws-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/aws/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/aws/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update control-plane count Source: https://docs.ankra.ai/api-reference/aws-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/control-plane Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update control-plane count (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/update-control-plane-count-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/control-plane Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group autoscaling Source: https://docs.ankra.ai/api-reference/aws-clusters/update-node-group-autoscaling https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name}/autoscaling Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/update-node-group-autoscaling-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/node-groups/{group_name}/autoscaling Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group labels Source: https://docs.ankra.ai/api-reference/aws-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name}/labels Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group labels (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/update-node-group-labels-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/node-groups/{group_name}/labels Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group taints Source: https://docs.ankra.ai/api-reference/aws-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/node-groups/{group_name}/taints Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update node-group taints (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/update-node-group-taints-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/node-groups/{group_name}/taints Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update SSH keys Source: https://docs.ankra.ai/api-reference/aws-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/aws/{cluster_id}/ssh-keys Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Update SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/update-ssh-keys-browser-session https://platform.ankra.app/openapi.json put /org/clusters/aws/{cluster_id}/ssh-keys Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. Answers 404 while the organisation's `aws_provider` feature flag is off. # Upgrade the Kubernetes version Source: https://docs.ankra.ai/api-reference/aws-clusters/upgrade-the-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/aws/{cluster_id}/upgrade-k8s-version Bearer PAT authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Upgrade the Kubernetes version (browser session) Source: https://docs.ankra.ai/api-reference/aws-clusters/upgrade-the-kubernetes-version-browser-session https://platform.ankra.app/openapi.json post /org/clusters/aws/{cluster_id}/upgrade-k8s-version Browser session authentication; RBAC permission `clusters.operate`. Answers 404 while the organisation's `aws_provider` feature flag is off. # Create an AWS access-key credential Source: https://docs.ankra.ai/api-reference/aws-credentials/create-an-aws-access-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/aws/keys Bearer PAT authentication. Registers an access-key credential after an STS GetCallerIdentity check; a name clash answers 409, a failed check 400. # Create an AWS access-key credential (browser session) Source: https://docs.ankra.ai/api-reference/aws-credentials/create-an-aws-access-key-credential-browser-session https://platform.ankra.app/openapi.json post /org/credentials/aws/keys Browser session authentication. Registers an access-key credential after an STS GetCallerIdentity check; a name clash answers 409, a failed check 400. # Create an AWS role credential Source: https://docs.ankra.ai/api-reference/aws-credentials/create-an-aws-role-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/aws/role Bearer PAT authentication. Registers an STS role credential after assuming it. A `self_managed` scope is additionally proven against EC2 (a DryRun DescribeVpcs) and the Canonical Ubuntu AMI SSM parameters; unknown scopes answer 400, a name clash 409, a failed connection test 400. # Create an AWS role credential (browser session) Source: https://docs.ankra.ai/api-reference/aws-credentials/create-an-aws-role-credential-browser-session https://platform.ankra.app/openapi.json post /org/credentials/aws/role Browser session authentication. Registers an STS role credential after assuming it. A `self_managed` scope is additionally proven against EC2 (a DryRun DescribeVpcs) and the Canonical Ubuntu AMI SSM parameters; unknown scopes answer 400, a name clash 409, a failed connection test 400. # Get AWS onboarding details Source: https://docs.ankra.ai/api-reference/aws-credentials/get-aws-onboarding-details https://platform.ankra.app/openapi.json get /api/v1/credentials/aws/onboarding Bearer PAT authentication. Generates an external ID and the CloudFormation launch-stack URL for the requested scope (`cost`, `provisioning`, or `self_managed` for the AnkraSelfManagedProvisioning role the k3s / kubeadm lane uses). Unknown scopes answer 400. # Get AWS onboarding details (browser session) Source: https://docs.ankra.ai/api-reference/aws-credentials/get-aws-onboarding-details-browser-session https://platform.ankra.app/openapi.json get /org/credentials/aws/onboarding Browser session authentication. Generates an external ID and the CloudFormation launch-stack URL for the requested scope (`cost`, `provisioning`, or `self_managed` for the AnkraSelfManagedProvisioning role the k3s / kubeadm lane uses). Unknown scopes answer 400. # Create an Azure credential Source: https://docs.ankra.ai/api-reference/azure-credentials/create-an-azure-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/azure Bearer PAT authentication; RBAC permission `credentials.write`. The secret is stored in Vault and connection-tested without echoing key material. # Create an Azure credential (browser session) Source: https://docs.ankra.ai/api-reference/azure-credentials/create-an-azure-credential-browser-session https://platform.ankra.app/openapi.json post /org/credentials/azure Browser session authentication; RBAC permission `credentials.write`. The secret is stored in Vault and connection-tested without echoing key material. # Create an SSH key credential Source: https://docs.ankra.ai/api-reference/azure-credentials/create-an-ssh-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/azure/ssh-key Creates an SSH key credential from `name` and either `ssh_public_key` or `generate=true`; a generated RSA-4096 key pair's private key is returned once, in this response. A public key the organisation already stores under another credential is refused with 400. Requires `credentials.write`; answers 201. # Get Azure credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/azure-credentials/get-azure-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/azure/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the Azure API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get Azure credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/azure-credentials/get-azure-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/azure/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the Azure limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # List Azure credentials Source: https://docs.ankra.ai/api-reference/azure-credentials/list-azure-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/azure Bearer PAT authentication; RBAC permission `credentials.read`. Returns credential metadata only; the client secret is never returned. # List Azure credentials (browser session) Source: https://docs.ankra.ai/api-reference/azure-credentials/list-azure-credentials-browser-session https://platform.ankra.app/openapi.json get /org/credentials/azure Browser session authentication; RBAC permission `credentials.read`. Returns credential metadata only; the client secret is never returned. # List SSH key credentials Source: https://docs.ankra.ai/api-reference/azure-credentials/list-ssh-key-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/azure/ssh-keys Lists the organisation's SSH key credentials. The list is the same under every cloud provider. Each item carries the stored public key with the fingerprint (as `ssh-keygen -l` prints it), key type and comment derived from it; a stored value that is not a readable key keeps its row, with a null `fingerprint` and `fingerprint_error` saying why. # Preview restoring an application deployment Source: https://docs.ankra.ai/api-reference/backups/preview-restoring-an-application-deployment https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/deployments/{deployment_id}/restore-points/{restore_point_id}/restore-plan Answers what restoring this deployment in place from this restore point would do if it were confirmed now, without doing any of it: the steps in order with the volume claims they remove, the namespaces whose objects are restored, whether a safety copy of the live stack is taken first, every reason the restore would be refused, and whether the stack has drifted. It opens no run, mints no restore point and writes nothing to the stack. The stack is resolved from the application's own records; a restore point captured from another stack or cluster is not found here. Requires the backups rollout flag and backups.read. # Preview restoring an application deployment (browser session) Source: https://docs.ankra.ai/api-reference/backups/preview-restoring-an-application-deployment-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deployments/{deployment_id}/restore-points/{restore_point_id}/restore-plan Answers what restoring this deployment in place from this restore point would do if it were confirmed now, without doing any of it: the steps in order with the volume claims they remove, the namespaces whose objects are restored, whether a safety copy of the live stack is taken first, every reason the restore would be refused, and whether the stack has drifted. It opens no run, mints no restore point and writes nothing to the stack. The stack is resolved from the application's own records; a restore point captured from another stack or cluster is not found here. Requires the backups rollout flag and backups.read. # Read one application deployment restore point with its receipt Source: https://docs.ankra.ai/api-reference/backups/read-one-application-deployment-restore-point-with-its-receipt https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/deployments/{deployment_id}/restore-points/{restore_point_id} Reads one restore point of one application deployment with its manifest: assets with their measured sizes and per-artifact size and SHA-256 checksums, coverage, what was not carried, and the run that produced it. The stack is resolved from the application's own installation rows; a restore point captured from another stack or cluster is not found here. Requires the backups rollout flag and backups.read. Object keys are never returned. A completed capture is not a verified restore: verification_status is written only by an automated restore verification. # Read one application deployment restore point with its receipt (browser session) Source: https://docs.ankra.ai/api-reference/backups/read-one-application-deployment-restore-point-with-its-receipt-browser-session https://platform.ankra.app/openapi.json get /org/applications/{application_id}/deployments/{deployment_id}/restore-points/{restore_point_id} Reads one restore point of one application deployment with its manifest: assets with their measured sizes and per-artifact size and SHA-256 checksums, coverage, what was not carried, and the run that produced it. The stack is resolved from the application's own installation rows; a restore point captured from another stack or cluster is not found here. Requires the backups rollout flag and backups.read. Object keys are never returned. A completed capture is not a verified restore: verification_status is written only by an automated restore verification. # Restore an application deployment from a restore point Source: https://docs.ankra.ai/api-reference/backups/restore-an-application-deployment-from-a-restore-point https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/deployments/{deployment_id}/restore-points/{restore_point_id}/restore Restores this deployment in place from one of its own restore points. The cluster and stack are resolved from the deployment on the server, and confirm must be that stack's name. A safety restore point of the live stack is taken first and the restore is refused if it cannot be; the workloads are then scaled down, the claims the restore point replaces are removed, the data is restored and the workloads are scaled back up. A refusal answers with the sentence as detail and a stable error_code. force overrides drift and nothing else. Requires the backups rollout flag and backups.operate. # Restore an application deployment from a restore point (browser session) Source: https://docs.ankra.ai/api-reference/backups/restore-an-application-deployment-from-a-restore-point-browser-session https://platform.ankra.app/openapi.json post /org/applications/{application_id}/deployments/{deployment_id}/restore-points/{restore_point_id}/restore Restores this deployment in place from one of its own restore points. The cluster and stack are resolved from the deployment on the server, and confirm must be that stack's name. A safety restore point of the live stack is taken first and the restore is refused if it cannot be; the workloads are then scaled down, the claims the restore point replaces are removed, the data is restored and the workloads are scaled back up. A refusal answers with the sentence as detail and a stable error_code. force overrides drift and nothing else. Requires the backups rollout flag and backups.operate. # Approve usage charges as the organisation owner Source: https://docs.ankra.ai/api-reference/billing/approve-usage-charges-as-the-organisation-owner https://platform.ankra.app/openapi.json post /api/v1/org/billing/activation Requires billing.manage and a live organisation owner assignment. The owner must send accept_charges=true and the exact current terms, and have a verified payment method. The approval, plan switch and audit record commit together. Repeated confirmation is idempotent. # Approve usage charges as the organisation owner (browser session) Source: https://docs.ankra.ai/api-reference/billing/approve-usage-charges-as-the-organisation-owner-browser-session https://platform.ankra.app/openapi.json post /org/billing/activation Requires billing.manage and a live organisation owner assignment. The owner must send accept_charges=true and the exact current terms, and have a verified payment method. The approval, plan switch and audit record commit together. Repeated confirmation is idempotent. # Attach an AWS Marketplace subscription to the organisation Source: https://docs.ankra.ai/api-reference/billing/attach-an-aws-marketplace-subscription-to-the-organisation https://platform.ankra.app/openapi.json post /api/v1/org/billing/aws-marketplace/link Requires billing.manage. Consumes the single-use link token the AWS Marketplace registration redirect carried and attaches that license to the caller's organisation. Once AWS confirms the contract, the organisation moves to the plan its contract dimension maps to (startup_vcpu to Startup, enterprise_vcpu to Enterprise), and its usage is collected by AWS Marketplace: worker vCPU above the 30 free and the contracted quantity is metered to AWS hourly. An organisation holds at most one live AWS Marketplace subscription, and one already paying Ankra on a paid plan or by invoice is refused. Repeating the call from the organisation that already holds the license answers the same outcome. # Read billing, trial allowance and linked Cloud credits Source: https://docs.ankra.ai/api-reference/billing/read-billing-trial-allowance-and-linked-cloud-credits https://platform.ankra.app/openapi.json get /api/v1/org/billing/activation Reads the current organisation only. Requires billing.read. Unknown capacity or Cloud credit observations remain null and saving a card does not authorise paid usage. Once a card is saved, the read grants the welcome credit if the setup webhook has not. # Read billing, trial allowance and linked Cloud credits (browser session) Source: https://docs.ankra.ai/api-reference/billing/read-billing-trial-allowance-and-linked-cloud-credits-browser-session https://platform.ankra.app/openapi.json get /org/billing/activation Reads the current organisation only. Requires billing.read. Unknown capacity or Cloud credit observations remain null and saving a card does not authorise paid usage. Once a card is saved, the read grants the welcome credit if the setup webhook has not. # Save a card without approving paid usage Source: https://docs.ankra.ai/api-reference/billing/save-a-card-without-approving-paid-usage https://platform.ankra.app/openapi.json post /api/v1/org/billing/activation/setup Requires billing.manage and CSRF for browser sessions. Creates a Stripe setup checkout and returns to an allowed Platform origin. This operation never selects a paid plan or removes the trial limit. # Save a card without approving paid usage (browser session) Source: https://docs.ankra.ai/api-reference/billing/save-a-card-without-approving-paid-usage-browser-session https://platform.ankra.app/openapi.json post /org/billing/activation/setup Requires billing.manage and CSRF for browser sessions. Creates a Stripe setup checkout and returns to an allowed Platform origin. This operation never selects a paid plan or removes the trial limit. # Set or clear the organisation's daily AI caps (a key not sent is kept) Source: https://docs.ankra.ai/api-reference/billing/set-or-clear-the-organisations-daily-ai-caps-a-key-not-sent-is-kept https://platform.ankra.app/openapi.json put /api/v1/org/billing/ai-spend-cap Partially updates the organisation's daily AI caps: a key left out of the body is kept, `null` clears it and a value sets it. Requires `billing.manage`; returns 400 for a non-positive cap or a soft token cap above the hard cap. # The organisation's daily AI money and token caps with today's spend Source: https://docs.ankra.ai/api-reference/billing/the-organisations-daily-ai-money-and-token-caps-with-todays-spend https://platform.ankra.app/openapi.json get /api/v1/org/billing/ai-spend-cap Returns the organisation's daily AI limits: its own USD chat spend cap with the plan default and effective cap, the soft and hard daily token caps with the budget state, and today's spend and token usage. Past the soft token cap unattended runs degrade to a quicker model tier; past the hard cap new unattended runs are refused until the UTC day resets. # List canvas datasets Source: https://docs.ankra.ai/api-reference/canvas-datasets/list-canvas-datasets https://platform.ankra.app/openapi.json get /api/v1/org/canvas-datasets Lists the fixed, named datasets a canvas dashboard element can bind to, with the shape each answers and the parameters it takes. The queries behind them are owned by the platform; no query text is ever accepted. # Query a canvas dataset Source: https://docs.ankra.ai/api-reference/canvas-datasets/query-a-canvas-dataset https://platform.ankra.app/openapi.json post /api/v1/org/canvas-datasets/{dataset_id}/query Runs one dataset for a time range, pinned to the caller's organisation and project, and with compare_previous also for the period of the same length just before it. Series are capped at 20 with at most 1000 points each and tables at 500 rows; truncated says when a cap applied. # List published canvas pages Source: https://docs.ankra.ai/api-reference/canvas-pages/list-published-canvas-pages https://platform.ankra.app/openapi.json get /api/v1/org/canvas-publications Lists the organisation's published chat canvas pages, newest first, without their content. Pass `next_cursor` back as `cursor` for the next page. Any organisation member can read them. # Publish a canvas page Source: https://docs.ankra.ai/api-reference/canvas-pages/publish-a-canvas-page https://platform.ankra.app/openapi.json post /api/v1/org/canvas-publications Publishes a chat canvas artifact at a public address on the pages host. The content is sent explicitly, so edited canvases publish as shown. Only `html`, `svg` and `markdown` can be published. The page is a static snapshot served in a sandbox, with a fixed notice naming it as an AI-generated page. Publishing the same `conversation_id` and `artifact_id` again keeps the URL and extends the expiry. A durable canvas is published with `canvas_id` and `revision_id` instead: it has one publication whichever revision is published, the canvas's `published_revision_id` follows it, and a json dashboard revision is published as the html snapshot sent in `content`. Who may publish is the organisation's `public_canvas_publishing` setting (`admins` by default, `members`, or `disabled`); tightening it leaves live pages up. `ttl_hours` defaults to 168 and is capped at 720. # Unpublish a canvas page Source: https://docs.ankra.ai/api-reference/canvas-pages/unpublish-a-canvas-page https://platform.ankra.app/openapi.json delete /api/v1/org/canvas-publications/{publication_id} Takes a published page down. Its address answers 410 from then on; publishing the artifact again restores it at the same address. # Create a canvas Source: https://docs.ankra.ai/api-reference/canvases/create-a-canvas https://platform.ankra.app/openapi.json post /api/v1/org/canvases Saves a new canvas with its first revision as the draft. A canvas is private to its creator until shared. Saving a chat artifact names `source_conversation_id` and `source_artifact_id` together; saving the same artifact again returns the canvas it already made (200) instead of creating another. # Delete a canvas Source: https://docs.ankra.ai/api-reference/canvases/delete-a-canvas https://platform.ankra.app/openapi.json delete /api/v1/org/canvases/{canvas_id} Removes the canvas from the library. Its revisions are purged later. A chat artifact saved as this canvas can be saved again afterwards. Only the owner can delete a canvas. # Get a canvas Source: https://docs.ankra.ai/api-reference/canvases/get-a-canvas https://platform.ankra.app/openapi.json get /api/v1/org/canvases/{canvas_id} Returns a canvas with the content of its draft revision. # Get a canvas revision Source: https://docs.ankra.ai/api-reference/canvases/get-a-canvas-revision https://platform.ankra.app/openapi.json get /api/v1/org/canvases/{canvas_id}/revisions/{revision_id} Returns one revision of a canvas with its content. # List canvas revisions Source: https://docs.ankra.ai/api-reference/canvases/list-canvas-revisions https://platform.ankra.app/openapi.json get /api/v1/org/canvases/{canvas_id}/revisions Lists a canvas's revisions newest first, without content. Pass `next_cursor` back as `cursor` for the next page. # List canvases Source: https://docs.ankra.ai/api-reference/canvases/list-canvases https://platform.ankra.app/openapi.json get /api/v1/org/canvases Lists the canvases the caller can see - their own and every canvas shared with the organisation - most recently updated first, without content. `query` matches the title, `kind` is dashboard or page, and `pinned=true` keeps the caller's pinned canvases. Pass `next_cursor` back as `cursor` for the next page. With `source_conversation_id` and `source_artifact_id` the list is a lookup instead: `items` holds the one canvas saved from that chat artifact when the caller can see it, or is empty, and `next_cursor` is null. # Move a canvas draft Source: https://docs.ankra.ai/api-reference/canvases/move-a-canvas-draft https://platform.ankra.app/openapi.json post /api/v1/org/canvases/{canvas_id}/draft Undo and redo: points the draft at any revision of the canvas without writing a new one, under the same `base_revision_id` check as saving. Only the owner can move the draft. # Pin a canvas Source: https://docs.ankra.ai/api-reference/canvases/pin-a-canvas https://platform.ankra.app/openapi.json put /api/v1/org/canvases/{canvas_id}/pin Adds a canvas the caller can see to their pinned canvases. Pins are per member; pinning twice changes nothing. # Rename or share a canvas Source: https://docs.ankra.ai/api-reference/canvases/rename-or-share-a-canvas https://platform.ankra.app/openapi.json patch /api/v1/org/canvases/{canvas_id} Changes the title, the visibility or both. `private` keeps the canvas to its owner; `organisation` lets every member read it. Only the owner can change a canvas. # Save a canvas revision Source: https://docs.ankra.ai/api-reference/canvases/save-a-canvas-revision https://platform.ankra.app/openapi.json post /api/v1/org/canvases/{canvas_id}/revisions Appends a revision and makes it the draft. `base_revision_id` is the draft the change was made on: when the draft has moved since, nothing is saved and the answer is 409 with the current revision, so a newer change is never overwritten. Only the owner can save. # Unpin a canvas Source: https://docs.ankra.ai/api-reference/canvases/unpin-a-canvas https://platform.ankra.app/openapi.json delete /api/v1/org/canvases/{canvas_id}/pin Removes a canvas from the caller's pinned canvases. Unpinning a canvas that was not pinned changes nothing. # Get detailed information about a specific chart Source: https://docs.ankra.ai/api-reference/charts/get-detailed-information-about-a-specific-chart https://platform.ankra.app/openapi.json post /api/v1/org/stacks/charts/details Returns a chart's icon, repository and available versions, looked up by `chart_name` and `repository_url`. Answers 404 when no such chart exists. # List available Helm charts Source: https://docs.ankra.ai/api-reference/charts/list-available-helm-charts https://platform.ankra.app/openapi.json get /api/v1/org/stacks/charts Lists the Helm charts available to the organisation, from its own registries and the global ones, with each chart's repository, icon, version and registry credential name. Paginated, with `page_size` up to 1500. # Answer the question a durable AI chat session is parked on Source: https://docs.ankra.ai/api-reference/chat/answer-the-question-a-durable-ai-chat-session-is-parked-on https://platform.ankra.app/openapi.json post /api/v1/chat/sessions/{session_id}/answers Answers the question an AI chat session is waiting on, with the `question_id` and at least one of the offered `selected_option_ids`, so the turn can continue. Answers 409 when no pending question matches. # Approve an AI chat plan and execute its steps Source: https://docs.ankra.ai/api-reference/chat/approve-an-ai-chat-plan-and-execute-its-steps https://platform.ankra.app/openapi.json post /api/v1/chat/plans/{plan_id}/approve Approves an AI plan and executes its steps, streaming progress as server-sent events. Requires `ai.execute`. High-risk and irreversible steps must be named in `acknowledged_step_orders`, or the call answers 400 listing them; a plan created before the current plan format answers 410 and has to be proposed again. # Approve an AI chat plan and execute its steps (browser session) Source: https://docs.ankra.ai/api-reference/chat/approve-an-ai-chat-plan-and-execute-its-steps-browser-session https://platform.ankra.app/openapi.json post /chat/plans/{plan_id}/approve Approves an AI plan and executes its steps, streaming progress as server-sent events. Requires `ai.execute`. High-risk and irreversible steps must be named in `acknowledged_step_orders`, or the call answers 400 listing them; a plan created before the current plan format answers 410 and has to be proposed again. # Ask a running AI chat session to answer now Source: https://docs.ankra.ai/api-reference/chat/ask-a-running-ai-chat-session-to-answer-now https://platform.ankra.app/openapi.json post /api/v1/chat/sessions/{session_id}/wrap-up Asks the running turn to stop investigating and answer with what it has; the session keeps running. Repeating the call is safe and returns the original `wrap_up_requested_at`; answers 409 when no turn is running. # Cancel a durable AI chat session Source: https://docs.ankra.ai/api-reference/chat/cancel-a-durable-ai-chat-session https://platform.ankra.app/openapi.json post /api/v1/chat/sessions/{session_id}/cancel Cancels an AI chat session and stops any turn it is running. Answers 409 when the session can no longer be cancelled and 404 when it is not visible to the caller. # Confirm or reject a pending AI action Source: https://docs.ankra.ai/api-reference/chat/confirm-or-reject-a-pending-ai-action https://platform.ankra.app/openapi.json post /api/v1/chat/actions/confirm Approves (`confirmed: true`) or rejects a write the AI proposed in chat, identified by `action_id`, or every write of one proposal batch identified by `batch_id` (with `action_ids` to approve some and reject the rest). Requires `ai.execute`. For one action, answers 404 when it is unknown or expired, 409 when the cluster state drifted since the proposal or the action was superseded, and 410 when its approval window has passed. For a batch, answers 404 when no action carries it and otherwise 200 with one result per action in proposal order (a decided action's result is the single decision's response; `drifted`, `agent_timeout`, `expired`, `superseded` and `skipped` name the actions it could not decide) and a `summary` of `executed`, `failed`, `rejected`, `open`, `skipped` and `deciding` counts. A batch that takes longer than about 90 seconds answers then: the actions not yet decided read `deciding`, are still decided in order afterwards, and report their outcomes as `action_decided` events on the conversation. An `action_decided` event for an action whose execution failed carries its `error` when the decision knew it. # Create a durable AI chat session Source: https://docs.ankra.ai/api-reference/chat/create-a-durable-ai-chat-session https://platform.ankra.app/openapi.json post /api/v1/chat/sessions The bearer twin of the browser session transport - the successor the deprecated POST /api/v1/chat/general points clients at. Token authentication replaces the session cookie and CSRF pair; authorisation is the browser lane's. # Delete a chat conversation Source: https://docs.ankra.ai/api-reference/chat/delete-a-chat-conversation https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/{cluster_id}/kubernetes/chat/history/{conversation_id} Deletes one of the caller's saved chat conversations. Answers 404 when the cluster or the conversation is not found. # Delete a general chat conversation Source: https://docs.ankra.ai/api-reference/chat/delete-a-general-chat-conversation https://platform.ankra.app/openapi.json delete /api/v1/chat/general/history/{conversation_id} Deletes one of the caller's saved chat conversations. Answers 404 when the conversation does not exist or belongs to someone else. # Edit a proposed AI plan step's parameters Source: https://docs.ankra.ai/api-reference/chat/edit-a-proposed-ai-plan-steps-parameters https://platform.ankra.app/openapi.json patch /api/v1/chat/plans/{plan_id}/steps/{step_id} Replaces the `parameters` object of one step in a proposed AI plan and returns the updated plan. Answers 400 when the step cannot be edited. # Extend a proposed AI plan's expiry Source: https://docs.ankra.ai/api-reference/chat/extend-a-proposed-ai-plans-expiry https://platform.ankra.app/openapi.json post /api/v1/chat/plans/{plan_id}/refresh Pushes back the expiry of a proposed AI plan and returns the new `expires_at`. It does not regenerate the plan; answers 409 when the plan is not in a refreshable state. # Get a specific chat conversation Source: https://docs.ankra.ai/api-reference/chat/get-a-specific-chat-conversation https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/kubernetes/chat/history/{conversation_id} Returns one of the caller's saved chat conversations with its messages. Answers 404 when the cluster or the conversation is not found. # Get a specific general chat conversation Source: https://docs.ankra.ai/api-reference/chat/get-a-specific-general-chat-conversation https://platform.ankra.app/openapi.json get /api/v1/chat/general/history/{conversation_id} Returns one of the caller's saved chat conversations with its messages, or the conversation of a platform-dispatched session that a ticket the caller can read links as its work. Answers 404 when it does not exist or neither applies. # Get AI-analysed cluster health Source: https://docs.ankra.ai/api-reference/chat/get-ai-analysed-cluster-health https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/kubernetes/health Returns health insights for the cluster. Set `include_ai_analysis` to true to add an AI analysis; answers 404 when the cluster is not a live cluster of the organisation. # Get one AI plan with its steps Source: https://docs.ankra.ai/api-reference/chat/get-one-ai-plan-with-its-steps https://platform.ankra.app/openapi.json get /api/v1/chat/plans/{plan_id} Returns an AI plan with its steps. Answers 404 for an unknown plan and 403 when the plan belongs to another user. # Get one durable AI chat session Source: https://docs.ankra.ai/api-reference/chat/get-one-durable-ai-chat-session https://platform.ankra.app/openapi.json get /api/v1/chat/sessions/{session_id} Returns one of the caller's AI chat sessions with its current status, or a platform-dispatched session that a ticket the caller can read links as its work. Answers 404 when it does not exist or neither applies. # List chat conversations for a cluster Source: https://docs.ankra.ai/api-reference/chat/list-chat-conversations-for-a-cluster https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/kubernetes/chat/history Lists the caller's saved chat conversations about the cluster, paginated with `limit` (default 50, 1 to 1000) and `offset`. Set `show_all` to true to list the caller's conversations across all clusters. # List general chat conversations Source: https://docs.ankra.ai/api-reference/chat/list-general-chat-conversations https://platform.ankra.app/openapi.json get /api/v1/chat/general/history Lists the caller's saved chat conversations, newest first, paginated with `limit` (default 50, 1 to 1000) and `offset`. `show_all` (default true) includes cluster conversations; set it to false for general conversations only. # List general chat conversations with unseen finished replies Source: https://docs.ankra.ai/api-reference/chat/list-general-chat-conversations-with-unseen-finished-replies https://platform.ankra.app/openapi.json get /api/v1/chat/general/history/unread # List pending AI actions for a conversation Source: https://docs.ankra.ai/api-reference/chat/list-pending-ai-actions-for-a-conversation https://platform.ankra.app/openapi.json get /api/v1/chat/actions/pending Lists the caller's AI-proposed actions still awaiting confirmation in the conversation named by the required `conversation_id` query parameter, with their count. # List the caller's durable AI chat sessions Source: https://docs.ankra.ai/api-reference/chat/list-the-callers-durable-ai-chat-sessions https://platform.ankra.app/openapi.json get /api/v1/chat/sessions Lists the caller's AI chat sessions, optionally filtered by `cluster_id`, `agent_task_id` and `status`. `limit` defaults to 50 and is capped at 200. # List the caller's open AI plans for a conversation Source: https://docs.ankra.ai/api-reference/chat/list-the-callers-open-ai-plans-for-a-conversation https://platform.ankra.app/openapi.json get /api/v1/chat/plans Lists the caller's open AI plans in the conversation named by the required `conversation_id` query parameter, with their count. # Mark a general chat conversation's results as seen Source: https://docs.ankra.ai/api-reference/chat/mark-a-general-chat-conversations-results-as-seen https://platform.ankra.app/openapi.json post /api/v1/chat/general/history/{conversation_id}/seen # Re-check a pending AI action against the live cluster state Source: https://docs.ankra.ai/api-reference/chat/re-check-a-pending-ai-action-against-the-live-cluster-state https://platform.ankra.app/openapi.json get /api/v1/chat/actions/{action_id}/revalidate Re-checks a pending AI action before it is confirmed. Answers 200 when it still applies, 409 when the cluster state drifted or the action was superseded, 410 when it expired, 404 when it is unknown, and 503 when the cluster agent could not be reached. # Read a conversation's decision trace Source: https://docs.ankra.ai/api-reference/chat/read-a-conversations-decision-trace https://platform.ankra.app/openapi.json get /api/v1/chat/conversations/{conversation_id}/trace Returns the team-readable decision trace of a chat conversation, grouped by turn: the model routing decision with its reason, the tool families offered and lazily loaded, every refused tool call with its error code, every post-hoc rewrite of the reply, every budget or loop-guard intervention, how each turn ended, and every pending-action status transition with who decided it. Organisation admins only; a conversation outside the organisation answers 404. # Read a durable AI chat session's event log, as a page or an SSE tail Source: https://docs.ankra.ai/api-reference/chat/read-a-durable-ai-chat-sessions-event-log-as-a-page-or-an-sse-tail https://platform.ankra.app/openapi.json get /api/v1/chat/sessions/{session_id}/events Reads an AI chat session's event log from sequence `since`, as a JSON page of up to `limit` events (default 1000, at most 5000). With `stream=true` it replays the log and then follows new events as server-sent events, resuming from `since` or the `Last-Event-ID` header, and ends when the session finishes. # Record that an approval card for this action was rendered to its owner Source: https://docs.ankra.ai/api-reference/chat/record-that-an-approval-card-for-this-action-was-rendered-to-its-owner https://platform.ankra.app/openapi.json post /api/v1/chat/actions/{action_id}/presented Stamps presented_at for a surface that painted a decidable approval card for the calling user - the Slack confirmation card acknowledges here with the asker's own token. A surface that only mentions the action must not call it. Repeat acknowledgements answer recorded=false and leave the first stamp alone. # Reject a proposed AI plan Source: https://docs.ankra.ai/api-reference/chat/reject-a-proposed-ai-plan https://platform.ankra.app/openapi.json post /api/v1/chat/plans/{plan_id}/reject Rejects a proposed AI plan, with an optional `reason`. A chat session waiting on the plan is resumed with the decision; answers 404 when the plan does not exist or can no longer be rejected. # Reorder the steps of a proposed AI plan Source: https://docs.ankra.ai/api-reference/chat/reorder-the-steps-of-a-proposed-ai-plan https://platform.ankra.app/openapi.json post /api/v1/chat/plans/{plan_id}/reorder Sets a new order for a proposed plan's steps from `entries`, a non-empty list of `step_id` and integer `step_order` pairs, and returns the updated plan. Answers 400 when the new order is not valid. # Restore a skipped AI plan step Source: https://docs.ankra.ai/api-reference/chat/restore-a-skipped-ai-plan-step https://platform.ankra.app/openapi.json post /api/v1/chat/plans/{plan_id}/steps/{step_id}/restore Puts a skipped step of an AI plan back into the plan and returns the updated plan. Answers 400 when the step cannot be restored. # Skip a pending AI plan step Source: https://docs.ankra.ai/api-reference/chat/skip-a-pending-ai-plan-step https://platform.ankra.app/openapi.json post /api/v1/chat/plans/{plan_id}/steps/{step_id}/skip Marks one pending step of an AI plan as skipped, with an optional `reason`, and returns the updated plan. Answers 400 when the step cannot be skipped. # Stream chat responses for cluster troubleshooting Source: https://docs.ankra.ai/api-reference/chat/stream-chat-responses-for-cluster-troubleshooting https://platform.ankra.app/openapi.json post /api/v1/org/clusters/{cluster_id}/kubernetes/chat Runs one AI chat turn about the cluster and streams the reply as server-sent events. Answers 404 when the cluster is not a live cluster of the organisation. Deprecated: responses carry `Deprecation` and `Sunset` headers, and durable chat sessions (`/api/v1/chat/sessions`) are the successor. # Stream general chat responses Source: https://docs.ankra.ai/api-reference/chat/stream-general-chat-responses https://platform.ankra.app/openapi.json post /api/v1/chat/general Runs one general (not cluster-scoped) AI chat turn and streams the reply as server-sent events. Deprecated: responses carry `Deprecation` and `Sunset` headers, and durable chat sessions (`/api/v1/chat/sessions`) are the successor. # Submit a user turn to a durable AI chat session Source: https://docs.ankra.ai/api-reference/chat/submit-a-user-turn-to-a-durable-ai-chat-session https://platform.ankra.app/openapi.json post /api/v1/chat/sessions/{session_id}/turns Scoped tokens are honoured end to end: the token's scope allowlist is persisted with the turn (token_scopes on the user_turn event) and the session runner narrows its ai.execute lane gate and per-tool gate to it. # Summarise a conversation's placeholder title from its first exchange Source: https://docs.ankra.ai/api-reference/chat/summarise-a-conversations-placeholder-title-from-its-first-exchange https://platform.ankra.app/openapi.json post /api/v1/chat/conversations/{conversation_id}/title/regenerate Replaces a conversation's placeholder title with a short AI-generated summary of its first exchange and returns the new `title`. Answers 409 when the title was already set by hand, 422 when the conversation has no exchange yet, and 404 when it is not the caller's. # Approve or decline the Platform connection request Source: https://docs.ankra.ai/api-reference/cloud-delegation/approve-or-decline-the-platform-connection-request https://platform.ankra.app/openapi.json post /oauth/cloud/authorize/decision Requires the same Platform browser, authenticated subject, a single-use CSRF nonce paired with an HttpOnly host-only cookie, and the Platform Origin header. Membership and MFA are rechecked before issuing a 60-second code. Refuses cross-origin submissions. # Exchange an approved Cloud consent code for an organisation grant Source: https://docs.ankra.ai/api-reference/cloud-delegation/exchange-an-approved-cloud-consent-code-for-an-organisation-grant https://platform.ankra.app/openapi.json post /api/v1/cloud/delegation-grants/exchange Authenticates Cloud before reading the body. Atomically consumes a 60-second code, checks S256 PKCE and the exact callback, and rechecks the consenting human and organisation membership. Only the grant hash is stored; Cloud keeps its secret sealed. A directory bearer cannot exchange a code. # Show Platform consent for an Ankra Cloud organisation connection Source: https://docs.ankra.ai/api-reference/cloud-delegation/show-platform-consent-for-an-ankra-cloud-organisation-connection https://platform.ankra.app/openapi.json get /oauth/cloud/authorize Requires the person’s real Platform browser session and its MFA gate. Auth0 subject must match Cloud’s requested subject. The exact callback is deployment configured. Shows explicit consent for the person’s current permissions in one active organisation, for at most twelve hours. No grant is created by GET. # Connect a Cloudflare credential Source: https://docs.ankra.ai/api-reference/cloudflare/connect-a-cloudflare-credential https://platform.ankra.app/openapi.json post /api/v1/org/cloudflare/credentials Verifies a Cloudflare API token against Cloudflare, stores it under `name` and answers 201 with the verification result. Organisation admins only; a token Cloudflare rejects, or one that reaches no domain, answers 400. # Create a Cloudflare DNS record Source: https://docs.ankra.ai/api-reference/cloudflare/create-a-cloudflare-dns-record https://platform.ankra.app/openapi.json post /api/v1/org/cloudflare/domains/{zone_id}/records Creates a DNS record in the Cloudflare domain and answers 201 with it; the TTL defaults to automatic and proxying to off. Organisation admins only; answers 409 when the record already exists. # Delete a Cloudflare DNS record Source: https://docs.ankra.ai/api-reference/cloudflare/delete-a-cloudflare-dns-record https://platform.ankra.app/openapi.json delete /api/v1/org/cloudflare/domains/{zone_id}/records/{record_id} Deletes the record from the Cloudflare domain and answers 204. Organisation admins only. # Get a Cloudflare domain Source: https://docs.ankra.ai/api-reference/cloudflare/get-a-cloudflare-domain https://platform.ankra.app/openapi.json get /api/v1/org/cloudflare/domains/{zone_id} Returns one Cloudflare domain by zone id, with its status and name servers. Answers 404 for a domain the organisation's credential cannot reach. # List Cloudflare credentials Source: https://docs.ankra.ai/api-reference/cloudflare/list-cloudflare-credentials https://platform.ankra.app/openapi.json get /api/v1/org/cloudflare/credentials Lists the Cloudflare credentials connected to the organisation, with the account each one reaches, when it was verified, token expiry and state. The API token itself is never returned. # List Cloudflare DNS records Source: https://docs.ankra.ai/api-reference/cloudflare/list-cloudflare-dns-records https://platform.ankra.app/openapi.json get /api/v1/org/cloudflare/domains/{zone_id}/records Lists the DNS records in a Cloudflare domain, with optional `name`, `record_type` and `search` filters. Answers 404 for a domain the organisation's credential cannot reach. # List Cloudflare domains Source: https://docs.ankra.ai/api-reference/cloudflare/list-cloudflare-domains https://platform.ankra.app/openapi.json get /api/v1/org/cloudflare/domains Lists the Cloudflare domains the organisation's credential reaches, optionally filtered by `name`; pick a credential with `credential_name`. `is_truncated` is true when the list was cut short. # Update a Cloudflare DNS record Source: https://docs.ankra.ai/api-reference/cloudflare/update-a-cloudflare-dns-record https://platform.ankra.app/openapi.json patch /api/v1/org/cloudflare/domains/{zone_id}/records/{record_id} Changes a record's `content`, which is required, and any of `ttl`, `proxied`, `priority` and `comment`: an omitted field keeps its current value and a field sent as null is reset. The name and record type cannot change. Organisation admins only. # Verify a Cloudflare token Source: https://docs.ankra.ai/api-reference/cloudflare/verify-a-cloudflare-token https://platform.ankra.app/openapi.json post /api/v1/org/cloudflare/credentials/verify Checks a Cloudflare API token and reports the account and domains it reaches, without storing it. Organisation admins only. # Create a cluster access grant Source: https://docs.ankra.ai/api-reference/cluster-access/create-a-cluster-access-grant https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/access/grants Grants an active organisation member (by `user_email`) a Kubernetes role (`view`, `edit`, `admin` or `cluster-admin`) on the whole cluster or on one `namespace`, optionally expiring via `expires_in` or `expires_at` and with an audit `reason`. Requires `kube_access.manage` and organisation admin; returns 409 when a matching grant already exists and 400 when the user is not an active member. A caller holding `kube_access.elevate` instead may grant only itself (403 otherwise), and the grant must expire within the policy's elevated_max_ttl_seconds (4 hours when unset) and carry a reason; it may re-time only an elevation it made. Grants outside the organisation policy answer 403 with detail cluster_access_policy_violation, cluster_access_grant_expiry_required, cluster_access_grant_lifetime_exceeded or cluster_access_grant_reason_required, naming the bound. # Delete a cluster access grant Source: https://docs.ankra.ai/api-reference/cluster-access/delete-a-cluster-access-grant https://platform.ankra.app/openapi.json delete /api/v1/clusters/{cluster_id}/access/grants/{grant_id} Revokes one access grant on a cluster; the member loses that access through the kube gateway. Requires `kube_access.manage` and organisation admin; returns 404 when the grant does not exist. A grant that comes from the member's organisation role (source role) answers 409: the role sync would re-create it, so change the member's role, or the role's Kubernetes access, to remove it. A caller holding `kube_access.elevate` instead may end only an elevation it made for itself; any other grant answers 404. # Elevate your own cluster access Source: https://docs.ankra.ai/api-reference/cluster-access/elevate-your-own-cluster-access https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/access/elevate Break-glass addressed to the caller: the body names no member (a `user_email` is refused with 422), and the route grants the authenticated principal itself (a member or a service account, which has no email the grants route could name) a Kubernetes role on the whole cluster or one `namespace`. The grant must expire (`expires_in` or `expires_at`) within the organisation policy's elevated_max_ttl_seconds, or 4 hours when unset, must carry a `reason`, and stays under max_grant_role; re-posting with a new expiry re-times only the caller's own elevation. Requires `kube_access.elevate` (or `kube_access.manage`, held to the same bounds). Refusals answer 403 with detail cluster_access_policy_violation, cluster_access_grant_expiry_required, cluster_access_grant_lifetime_exceeded or cluster_access_grant_reason_required, naming the bound, and 409 when a matching grant exists that is not the caller's own elevation. End it early with DELETE /api/v1/clusters/{cluster_id}/access/grants/{grant_id}. # Generate a kubeconfig Source: https://docs.ankra.ai/api-reference/cluster-access/generate-a-kubeconfig https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubeconfig Returns a ready-to-use kubeconfig document for the cluster that points at the kube gateway and embeds a freshly minted short-lived token, together with the server URL and the token's expiry. Requires `kube_access.use` on the cluster; requests are rate limited per user (429). # Get a cluster access policy proposal Source: https://docs.ankra.ai/api-reference/cluster-access/get-a-cluster-access-policy-proposal https://platform.ankra.app/openapi.json get /api/v1/org/cluster-access-policy/proposals/{proposal_id} One cluster access policy proposal: the proposed policy, the policy it was proposed against (base), the current policy and whether it still matches base, the reason, the proposer and the token that carried the proposal, its status, expiry and decision, and the apply_url. Readable with `kube_access.manage`, `kube_access.policy` or `kube_access.elevate`; 404 when the organisation has no such proposal, naming the organisation searched (organisation_id, organisation_name): a proposal created in another organisation answers 404 here. # Get Cluster Access Policy Source: https://docs.ankra.ai/api-reference/cluster-access/get-cluster-access-policy https://platform.ankra.app/openapi.json get /api/v1/org/cluster-access-policy The organisation cluster access policy: the role the user who creates or imports a cluster receives on it, the highest role any cluster access grant may carry, the longest a grant above view may last, and the role from which grants need a reason. An organisation that has not set one reads the default (creator receives cluster-admin, no ceiling, no bounds) with is_configured false. Readable with `kube_access.manage`, `kube_access.policy` or `kube_access.elevate` (a break-glass holder reads the bounds it is held to). organisation_id and organisation_name name the organisation read: a token reads its own unless X-Ankra-Organisation-Id names another, so check them before reporting another organisation's policy. # List cluster access grants Source: https://docs.ankra.ai/api-reference/cluster-access/list-cluster-access-grants https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/access/grants Lists the access grants on a cluster, newest first, each naming the member, the scope (whole cluster or one namespace), the Kubernetes role and the expiry. Requires `kube_access.manage` and organisation admin; returns 403 otherwise. # List cluster access policy proposals Source: https://docs.ankra.ai/api-reference/cluster-access/list-cluster-access-policy-proposals https://platform.ankra.app/openapi.json get /api/v1/org/cluster-access-policy/proposals The organisation's cluster access policy proposals, newest first (at most 20; is_capped says when more matched). status filters by the status a proposal reads as: a pending proposal past its expiry reads as expired. Readable with `kube_access.manage`, `kube_access.policy` or `kube_access.elevate`, like the policy itself. organisation_id and organisation_name name the organisation listed, so an empty listing still says whose it is. # Mint a Kubernetes access token Source: https://docs.ankra.ai/api-reference/cluster-access/mint-a-kubernetes-access-token https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/k8s-token Mints a short-lived bearer token for the cluster's kube gateway (valid for one hour by default), as used by the CLI's kubectl exec credential plugin. Requires `kube_access.use` on the cluster, and the minted token carries the calling token's restrictions; requests are rate limited per user (429). # Propose a cluster access policy change Source: https://docs.ankra.ai/api-reference/cluster-access/propose-a-cluster-access-policy-change https://platform.ankra.app/openapi.json post /api/v1/org/cluster-access-policy/proposals Propose a change to the organisation cluster access policy. This never changes the policy: it records a proposal that an organisation owner or admin applies, signed in to the Ankra web app, from apply_url. Every field but reason is optional and an omitted one keeps its current value; elevated_max_ttl_seconds and require_reason_from_role may be null to clear the bound. The proposed policy is validated exactly like a policy write, and a proposal identical to the current policy answers 422. A new proposal supersedes the pending one; a proposal expires after 7 days, and applying it answers 409 policy_changed if the policy changed since it was proposed. Requires `kube_access.manage`, which is not human-only: an administrator's CLI token can propose. Applying and dismissing have no token route. # Set Cluster Access Policy Source: https://docs.ankra.ai/api-reference/cluster-access/set-cluster-access-policy https://platform.ankra.app/openapi.json put /api/v1/org/cluster-access-policy Set the organisation cluster access policy. creator_grant_role none seeds no grant for the creator; a creator role above max_grant_role is refused, and so is a creator role above view while elevated_max_ttl_seconds is set (sent or stored), because the creator's grant is standing and would be over policy as soon as it is seeded. A lowered ceiling leaves manual and creator grants above it in place, but they cannot be re-timed; grants that come from organisation roles are lowered to the new ceiling on the next role sync (about a minute), each with an audit row and one organisation notification per sync. New grants above the ceiling answer 403 cluster_access_policy_violation. elevated_max_ttl_seconds and require_reason_from_role are optional: an omitted bound keeps its stored value and null clears it; new and re-timed grants outside them answer 403 cluster_access_grant_expiry_required, cluster_access_grant_lifetime_exceeded or cluster_access_grant_reason_required. Requires `kube_access.policy`, which is human-only: an API token must name it in its scopes, and an unscoped or wildcard token answers 403 permission_denied. organisation_id and organisation_name name the organisation written. # Change cluster labels Source: https://docs.ankra.ai/api-reference/clusters/change-cluster-labels https://platform.ankra.app/openapi.json patch /api/v1/org/clusters/{cluster_id}/labels Sets and removes labels on this cluster (`ankra cluster label set|rm`): `set` adds or overwrites labels, `remove` deletes keys (removing a key the cluster does not carry is not an error). A label moves the cluster into or out of every dynamic cluster group that selects on it, and so changes what members holding a group-scoped role may do on it; the change therefore requires `clusters.write` for the whole organisation, not only on the cluster. Keys are 1-63 lowercase letters, digits, `.`, `_`, `-` or `/`; values are 1-63 letters, digits, `.`, `_` or `-`; a cluster carries at most 32 labels; the `environment` key is reserved. Every change is recorded in the organisation's audit log; a change that leaves the labels as they were records nothing. Answers the labels after the change. # Create the organisation's playground Source: https://docs.ankra.ai/api-reference/clusters/create-the-organisations-playground https://platform.ankra.app/openapi.json post /org/clusters/playground Creates the organisation's playground. With the hosts provider it is a real, writable vcluster on Ankra's shared playground host; with the ankracloud provider it is an Ankra Cloud dev cluster created through the managed-create flow on the organisation's built-in Ankra Cloud credential (the free 24-hour trial for an account with no payment method, a billed dev cluster otherwise; IPv6-only, with proxied records for IPv4 clients, unless the order body carries `public_ipv4: true`, which orders the public IPv4 add-on at create for an account that can spend and on conversion for a trial). Browser session authentication. One live playground per organisation across both providers. # Declare a custom DNS zone on the cluster Source: https://docs.ankra.ai/api-reference/clusters/declare-a-custom-dns-zone-on-the-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/custom-dns-zones The DNS zones this cluster serves with the organisation's own external-dns webhook credential, alongside the delegated zone Ankra serves itself. Ankra's own Avura token is pinned to Ankra's account, so ingress hosts on a zone the customer holds in their own provider account are dropped server-side and publish nothing. Declaring the zone here has Ankra render and own an external-dns deployment for it, pinned to that zone with its own txt owner id. A zone that overlaps the delegated zone Ankra already serves for this cluster is refused with 400: two sync-policy controllers on one zone delete each other's records. # Delete a cluster by name Source: https://docs.ankra.ai/api-reference/clusters/delete-a-cluster-by-name https://platform.ankra.app/openapi.json delete /api/v1/clusters/{cluster_name} Deletes an imported cluster identified by its name and revokes its agent's credentials. Requires `clusters.delete` at organisation scope; cloud clusters are refused with `409` and must be deleted through their provider's route so cloud resources are released. A cluster that hosts managed services that have not been retired is refused with `409` naming them, until they are retired or its agent has been offline for 15 minutes. # Deprovision a cluster Source: https://docs.ankra.ai/api-reference/clusters/deprovision-a-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/deprovision Tears down what Ankra manages on an imported cluster, which must be online: active executions are cancelled, every resource is set to stop, and a reconcile is triggered. Requires `clusters.delete`; cloud clusters are refused with `409` and must be deleted through their provider's route so cloud resources are released. # Disable the cluster's DNS zone Source: https://docs.ankra.ai/api-reference/clusters/disable-the-clusters-dns-zone https://platform.ankra.app/openapi.json delete /api/v1/clusters/{cluster_id}/dns-zone Hands the cluster's DNS zone back for asynchronous teardown and returns its state; the cluster itself is not changed, and the zone is not recreated until it is enabled again. Answers 404 when the cluster has no zone. # Enable the cluster's DNS zone Source: https://docs.ankra.ai/api-reference/clusters/enable-the-clusters-dns-zone https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/dns-zone Creates the cluster's generated public DNS zone under the organisation's root domain and returns its FQDN and state. Idempotent: an existing zone keeps its FQDN and a failed one is retried. The zone is provisioned asynchronously, after which Ankra installs external-dns on the cluster to publish hostnames in it. # Get a cluster's Baseline score Source: https://docs.ankra.ai/api-reference/clusters/get-a-clusters-baseline-score https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/baseline A cluster's Ankra Baseline score: the latest nightly evaluation of the ten Baseline checks (metrics and logs, service-level alerts, backups with a tested restore, requests and limits, probes, PodDisruptionBudgets, pinned images, supported versions, certificate automation, no critical or known-exploited CVEs), each pass, fail or unknown with its evidence, the gaps heaviest first and the unknowns named. The score is passed weight over decided weight; unknown checks leave the denominator and are reported as coverage. A cluster the nightly recorder has not scored answers computed false with every check unknown, not a score of zero. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Get cluster GitOps status Source: https://docs.ankra.ai/api-reference/clusters/get-cluster-gitops-status https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/gitops/status Returns which GitOps repository, branch and credential the cluster syncs from, together with the last synced commit. A cluster without GitOps configured returns a `not_configured` status. # Get control plane self-heal Source: https://docs.ankra.ai/api-reference/clusters/get-control-plane-self-heal https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/control-plane-self-heal Whether Ankra may restart a NotReady control plane of this cluster through its cloud provider (`ankra cluster control-plane-heal status`), and the cluster's latest NotReady episode: which node, what Ankra did, and how it ended. Ankra restarts a control plane only after it has been NotReady for about ten minutes, only when every other control plane is Ready and etcd keeps its quorum without it, and at most twice per node per day. It never stops, deprovisions or scales anything. Automatic restart is on for every cluster until a member turns it off. Readable by any member of the organisation. Bearer-PAT twin of the browser route of the same name under /org. # Get hosted log shipping Source: https://docs.ankra.ai/api-reference/clusters/get-hosted-log-shipping https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/hosted-logs Whether this cluster's container log lines are sent to Ankra's hosted log store (`ankra cluster logs-ship status`), whether that store is live on this platform (`available`), and whether the cluster's agent can obey the switch (`agent_supports_switch`; false means the agent needs an upgrade or has hosted log shipping turned off in its own Helm values). Hosted log shipping is off for every cluster until a member turns it on. Readable by any member of the organisation. Bearer-PAT twin of the browser route of the same name under /org. # Get state snapshot retention Source: https://docs.ankra.ai/api-reference/clusters/get-state-snapshot-retention https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/state-snapshot-retention The cluster's retention window for its scheduled etcd backups (`ankra cluster state-snapshots retention`). Ankra backs up the etcd state of every running cluster it provisions off the control plane, to the organisation's backup vault, every six hours. It keeps the four newest backups, plus the newest backup of each UTC day for `retention_days` days, 7 unless a member sets another window between 1 and 90. Whatever the window, Ankra also keeps the newest backup from before the cluster's last start and its last recovery incidents. Readable by any member of the organisation. Bearer-PAT twin of the browser route of the same name under /org. # Get the cluster agent's sizing Source: https://docs.ankra.ai/api-reference/clusters/get-the-cluster-agents-sizing https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/agent/resources Returns the agent container sizing Ankra holds for the cluster (memory and CPU limits and requests; null where Ankra holds nothing, so the agent chart's default and the agent's live sizing apply) and what it is doing now: apply_state not_set, applied, pending_upgrade or agent_offline, with a message to show verbatim. The bearer twin of `ankra cluster agent resources get`. # Get the cluster's DNS token rotation Source: https://docs.ankra.ai/api-reference/clusters/get-the-clusters-dns-token-rotation https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/dns-zone/token-rotation Reports where the rotation of the cluster's DNS write token stands: whether a rotation is waiting for its replacement token, and whether the token a rotation replaced is still valid and waiting to be revoked. Never returns a token. # Get the cluster's DNS zone Source: https://docs.ankra.ai/api-reference/clusters/get-the-clusters-dns-zone https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/dns-zone Read the cluster's generated public DNS domain without creating one. A cluster that has no zone reports state "none" with an empty fqdn. The domain nests under the organisation's Ankra root domain (dns_root_domain on /api/v1/org/ai-environment; ankra.cc by default). Use POST to create the zone and DELETE to hand it back for teardown. # Import or update a cluster Source: https://docs.ankra.ai/api-reference/clusters/import-or-update-a-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/import Applies an ImportCluster specification: creates the cluster when no cluster with that name exists in your organisation, otherwise updates it, including its stacks and GitOps settings. By default the apply runs in the background and the call answers `202` with `{"status": "accepted", "operation_id": ""}` (a `409` means an apply for the same cluster is already running). Follow the operation on `/api/v1/org/executions/{execution_id}`: it settles `success` or `failed`, and its step result carries the status code and body the apply would have answered synchronously, including the reason it was refused. Pass `wait=true` to run it synchronously and receive the result or the rejection reason directly. # Latest restorable state snapshot of a HPE Morpheus cluster Source: https://docs.ankra.ai/api-reference/clusters/latest-restorable-state-snapshot-of-a-hpe-morpheus-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/morpheus/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the HPE Morpheus cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # Latest restorable state snapshot of a Proxmox VE cluster Source: https://docs.ankra.ai/api-reference/clusters/latest-restorable-state-snapshot-of-a-proxmox-ve-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/proxmox/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the Proxmox VE cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List cluster add-ons Source: https://docs.ankra.ai/api-reference/clusters/list-cluster-add-ons https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/addons Returns a paginated list of the add-ons deployed on the cluster, including their live health where available. Answers `404` when the cluster is not in your organisation. # List cluster labels Source: https://docs.ankra.ai/api-reference/clusters/list-cluster-labels https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/labels The free-form key/value labels on this cluster (`ankra cluster label list`), which dynamic cluster groups select on, beside the cluster's own environment. The `environment` key is reserved: a group selecting on it reads the cluster's environment, so it is never stored as a label. Readable by any member of the organisation. Bearer-PAT twin of the browser route of the same name under /org. # List cluster manifests Source: https://docs.ankra.ai/api-reference/clusters/list-cluster-manifests https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/manifests Returns the manifests deployed on the cluster. A cluster outside your organisation returns an empty list. # List cluster stacks Source: https://docs.ankra.ai/api-reference/clusters/list-cluster-stacks https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/stacks Returns the first page (up to 25) of the stacks on the cluster. Answers `404` when the cluster is not in your organisation; use the imported-cluster stacks listing for search and paging. # List clusters Source: https://docs.ankra.ai/api-reference/clusters/list-clusters https://platform.ankra.app/openapi.json get /api/v1/clusters Returns a paginated list of the clusters in your organisation. Filter by `cluster_id` or by `cluster_name` (a case-insensitive partial match); `page_size` accepts 1 to 100 and defaults to 25. # List CNI options Source: https://docs.ankra.ai/api-reference/clusters/list-cni-options https://platform.ankra.app/openapi.json get /api/v1/clusters/cni-options Returns the container network interface (CNI) plugins you can choose when creating a cluster, with a display label for each and a flag marking the default. # List GitOps conflicts Source: https://docs.ankra.ai/api-reference/clusters/list-gitops-conflicts https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/gitops/conflicts Lists the cluster's open GitOps merge conflicts (`ankra cluster gitops conflicts list`): resources changed both in the GitOps repository and on the platform since they last agreed. GitOps sync stays paused on them until each is resolved. Each entry names the resource, how Git (`git_change_type`) and the platform (`db_change_type`) changed it, and any resolution already recorded that the next sync will apply. Any member of the cluster's organisation can read it. # List hosted log shipping Source: https://docs.ankra.ai/api-reference/clusters/list-hosted-log-shipping https://platform.ankra.app/openapi.json get /api/v1/org/hosted-logs Every live cluster of the organisation with its hosted log shipping switch, ordered by name: whether the cluster's container log lines are sent to Ankra's hosted log store, whether that store is live on this platform (`available`), and whether the cluster's agent can obey the switch (`agent_supports_switch`). Each cluster is answered exactly as GET /api/v1/org/clusters/{cluster_id}/hosted-logs answers it, so this is the one place to see which clusters ship their logs and which could. Readable by any member of the organisation, like the per-cluster read. Bearer-PAT twin of the browser route of the same name under /org. # List K3s versions Source: https://docs.ankra.ai/api-reference/clusters/list-k3s-versions https://platform.ankra.app/openapi.json get /api/v1/clusters/k3s-versions Returns the K3s versions Ankra supports: the newest three minor releases with their two latest patches each. `stable_version` is the recommended version, used when a cluster is created without an explicit version. # List kubeadm versions Source: https://docs.ankra.ai/api-reference/clusters/list-kubeadm-versions https://platform.ankra.app/openapi.json get /api/v1/clusters/kubeadm-versions Returns the Kubernetes versions Ankra supports for kubeadm clusters: the newest three minor releases with their two latest patches each. `stable_version` is the recommended version, used when a cluster is created without an explicit version. # List preserved GitOps paths Source: https://docs.ankra.ai/api-reference/clusters/list-preserved-gitops-paths https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/gitops/preserved-paths Lists the files Git and the platform are divided on: each was edited in Git, so Ankra keeps Git's bytes there instead of overwriting them, and the platform's later changes to it do not reach Git. Every entry carries both sides - withheld, with the reason, for a file that may carry a secret - and any decision already recorded, in path order and capped at 100 entries and a content budget (truncated says when either cap applied). Unlike a GitOps merge conflict a preserved file pauses nothing. Bearer-PAT twin of the browser route of the same name under /org. # List the cluster's custom DNS zones Source: https://docs.ankra.ai/api-reference/clusters/list-the-clusters-custom-dns-zones https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/custom-dns-zones The DNS zones this cluster serves with the organisation's own external-dns webhook credential, alongside the delegated zone Ankra serves itself. Ankra's own Avura token is pinned to Ankra's account, so ingress hosts on a zone the customer holds in their own provider account are dropped server-side and publish nothing. Declaring the zone here has Ankra render and own an external-dns deployment for it, pinned to that zone with its own txt owner id. # Read labels from the selected cluster log store Source: https://docs.ankra.ai/api-reference/clusters/read-labels-from-the-selected-cluster-log-store https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/logs/labels Namespace, pod and container values observed in the time window. Values can be capped and absence does not establish that collection is disabled. Use the same source choice as the history query. # Read the playground provisioning status Source: https://docs.ankra.ai/api-reference/clusters/read-the-playground-provisioning-status https://platform.ankra.app/openapi.json get /org/clusters/playground/{clusterId}/status Reads the provisioning phase of the organisation's playground. A playground belonging to another organisation is not found. # Read what a cluster is for Source: https://docs.ankra.ai/api-reference/clusters/read-what-a-cluster-is-for https://platform.ankra.app/openapi.json get /org/clusters/{cluster_id}/criticality Reports the cluster's criticality, the denominator the insight scanner grades blast radius against. Clusters that have never been classified read as production, which is the column's default and the value the grading falls back to. # (Removed) Get a cluster operation Source: https://docs.ankra.ai/api-reference/clusters/removed-get-a-cluster-operation https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/operations/{operation_id} This endpoint has been removed and always answers 410 Gone. Use `GET /api/v1/org/executions/{execution_id}` instead. # (Removed) List a cluster operation's jobs Source: https://docs.ankra.ai/api-reference/clusters/removed-list-a-cluster-operations-jobs https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/operations/{operation_id}/jobs This endpoint has been removed and always answers 410 Gone. Use `GET /api/v1/org/executions/{execution_id}/steps` and `GET /api/v1/org/executions/{execution_id}/result` instead. # (Removed) List cluster operations Source: https://docs.ankra.ai/api-reference/clusters/removed-list-cluster-operations https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/operations This endpoint has been removed and always answers 410 Gone. Use `GET /api/v1/org/executions?cluster_id={cluster_id}` instead. # Resolve a GitOps conflict Source: https://docs.ankra.ai/api-reference/clusters/resolve-a-gitops-conflict https://platform.ankra.app/openapi.json post /api/v1/org/clusters/{cluster_id}/gitops/conflicts/resolve-resource Records the resolution of one open GitOps conflict, named by its `resource_key` (`ankra cluster gitops conflicts resolve `). `git` keeps the repository's version and overwrites the platform's; `cluster` keeps the platform's version and pushes it back to the repository. The call triggers a sync, which applies the choice and clears the conflict. Without `resolution` the conflict is cleared undecided and the sync re-evaluates it. Requires `clusters.write` on the cluster. # Resolve a preserved GitOps path Source: https://docs.ankra.ai/api-reference/clusters/resolve-a-preserved-gitops-path https://platform.ankra.app/openapi.json post /api/v1/org/clusters/{cluster_id}/gitops/preserved-paths/resolve Records a decision on one preserved file, over the two sides the caller listed: git imports Git's content into the platform, cluster writes Ankra's version into Git and makes the file Ankra's again, none withdraws a decision that has not run yet. The next sync executes a decision; a git decision runs once every preserved file on the cluster has a decision, because the import reads Git at head, and an import the platform refuses (for example one that would lower an add-on's chart version) records why on the file and clears the decision. Nothing is written to Git by this call. Requires `clusters.write` on the cluster. Bearer-PAT twin of the browser route of the same name under /org. # Resolve all GitOps conflicts Source: https://docs.ankra.ai/api-reference/clusters/resolve-all-gitops-conflicts https://platform.ankra.app/openapi.json post /api/v1/org/clusters/{cluster_id}/gitops/conflicts/resolve Records one resolution for every open GitOps conflict on the cluster (`ankra cluster gitops conflicts resolve --all`). `git` keeps the repository's version and overwrites the platform's; `cluster` keeps the platform's version and pushes it back to the repository. The call triggers a sync, which applies the choice and clears the conflicts. Without `resolution` the conflicts are cleared undecided and the sync re-evaluates them, recording again any that still conflict. Requires `clusters.write` on the cluster. # Revoke the cluster's replaced DNS token Source: https://docs.ankra.ai/api-reference/clusters/revoke-the-clusters-replaced-dns-token https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/dns-zone/token-rotation/revoke-previous Asks Ankra to revoke the DNS write token the last rotation replaced. Call it only once the cluster publishes with the new token: a cluster still using the replaced token stops publishing when it is revoked. Idempotent once requested. # Roll a cluster back to a resource version Source: https://docs.ankra.ai/api-reference/clusters/roll-a-cluster-back-to-a-resource-version https://platform.ankra.app/openapi.json post /api/v1/clusters/resources/roll-to Rolls a cluster's resource definitions back to an earlier saved version, identified by `cluster_id` and `version_id` in the body, and moves the cluster's current version to it. Requires `clusters.operate`; answers `{"ok": true}` on success. # Rotate the cluster's DNS token Source: https://docs.ankra.ai/api-reference/clusters/rotate-the-clusters-dns-token https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/dns-zone/token-rotation Asks Ankra to replace the write token the cluster's external-dns uses to publish into its DNS zone. Ankra mints the replacement within about a minute and republishes the external-dns Secret; the replaced token stays valid until it is revoked, so the cluster keeps publishing meanwhile. Restart external-dns on the cluster so it reads the new Secret, confirm records still publish, then revoke the replaced token. Idempotent while a rotation is pending. # Save a stack draft Source: https://docs.ankra.ai/api-reference/clusters/save-a-stack-draft https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/stacks/draft Saves a specification holding exactly one stack as a draft on the cluster without deploying it, and returns the draft id and version. Requires `stacks.write`; validation errors answer `422`. # Search cluster log history in the selected store Source: https://docs.ankra.ai/api-reference/clusters/search-cluster-log-history-in-the-selected-store https://platform.ankra.app/openapi.json post /org/clusters/imported/{cluster_id}/logs/query Session-authenticated historical search. Requires kubernetes.logs on this cluster. An explicit source never replaces a saved connection or changes shipping. Hosted reads remain pinned to the organisation and cluster. # Set control plane self-heal Source: https://docs.ankra.ai/api-reference/clusters/set-control-plane-self-heal https://platform.ankra.app/openapi.json put /api/v1/org/clusters/{cluster_id}/control-plane-self-heal Turns the automatic restart of a NotReady control plane on or off for this cluster (`ankra cluster control-plane-heal enable|disable`). While it is on, Ankra restarts a control plane through the cloud provider when it has been NotReady for about ten minutes, every other control plane is Ready, and etcd keeps its quorum without it; it never stops, deprovisions or scales anything, and shows each restart and its outcome in the cluster's notifications and audit log. Turning it off leaves a NotReady control plane for you to handle. Every change is recorded in the organisation's audit log; re-sending the current value changes nothing. Requires `clusters.write` on the cluster. # Set hosted log shipping Source: https://docs.ankra.ai/api-reference/clusters/set-hosted-log-shipping https://platform.ankra.app/openapi.json put /api/v1/org/clusters/{cluster_id}/hosted-logs Turns hosted log shipping on or off for this cluster (`ankra cluster logs-ship enable|disable`). While it is on and the hosted store is live, the cluster's agent sends the log lines of every running container to Ankra's hosted log store, where members of the organisation can search them; turning it off stops shipping within about a minute. Lines already stored age out with the store's retention. Every change is recorded in the organisation's audit log; re-sending the current value changes nothing. Requires `clusters.write` on the cluster. # Set state snapshot retention Source: https://docs.ankra.ai/api-reference/clusters/set-state-snapshot-retention https://platform.ankra.app/openapi.json put /api/v1/org/clusters/{cluster_id}/state-snapshot-retention Sets the cluster's retention window for its scheduled etcd backups, in days between 1 and 90 (`ankra cluster state-snapshots retention --days N`). From the next retention pass on, Ankra keeps the newest backup of each UTC day within the window, on top of the four newest backups. A shorter window deletes the daily backups that fall outside it from the backup vault; a longer one keeps backups from then on and cannot bring back ones already deleted. The newest backup from before the cluster's last start and its last recovery incidents is kept whatever the window. Every change is recorded in the organisation's audit log; re-sending the current value changes nothing. Requires `clusters.write` on the cluster. # Set the cluster agent's sizing Source: https://docs.ankra.ai/api-reference/clusters/set-the-cluster-agents-sizing https://platform.ankra.app/openapi.json put /api/v1/org/clusters/{cluster_id}/agent/resources Sets the agent container sizing Ankra holds for the cluster. Name any of memory_limit, memory_request, cpu_limit and cpu_request: a Kubernetes quantity holds the member, an empty string stops holding it (which does not change what the agent runs: it keeps its current value, carried through upgrades), and an absent member is unchanged. Limits take memory 512Mi-16Gi and CPU 100m-8; requests are capped at memory 128Mi-2Gi and CPU 50m-2 because the agent's old pod stops before its replacement schedules. Every later agent upgrade and every install command renders the held sizing; an online agent that accepts it re-renders its own release at once and restarts onto it. The bearer twin of `ankra cluster agent resources set`. # Set what a cluster is for Source: https://docs.ankra.ai/api-reference/clusters/set-what-a-cluster-is-for https://platform.ankra.app/openapi.json put /org/clusters/{cluster_id}/criticality Records the cluster's criticality, the denominator the insight scanner grades blast radius against. A production cluster reports exactly as it always has; a lower criticality needs a correspondingly larger blast radius before a finding reaches a severity that opens a ticket. # Start a cluster Source: https://docs.ankra.ai/api-reference/clusters/start-a-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/provision Starts a stopped cluster, retries a start whose control plane never came up (the cluster `offline` or `provider_without_access`), or brings back parked workers with the default scope; any other state answers `409`. Pass `scope=control_plane` to start only the control plane (the default starts everything), and `restore_state=false` to boot an empty etcd instead of restoring a captured cluster state snapshot. By default a cluster whose stop deleted its VMs restores a restore already requested, otherwise the newest snapshot captured after its last start (the stop's own capture, otherwise the newest scheduled backup); an older one is restored only when named with `restore_snapshot_id`. A paused cluster keeps its etcd and gets no new restore, unless `restore_over_kept_etcd=true` is passed with `restore_snapshot_id` to confirm that the kept control-plane machines, and their etcd, are lost. The answer's `state_restore`, `state_snapshot` and `state_restore_message` say which snapshot is restored. Answers `409` when the cluster cannot be started in its current state. # The persistent volumes deleting a Proxmox VE cluster removes Source: https://docs.ankra.ai/api-reference/clusters/the-persistent-volumes-deleting-a-proxmox-ve-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/proxmox/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the disks the Proxmox CSI driver provisioned on the Proxmox storage, which `DELETE /api/v1/clusters/proxmox/{cluster_id}` deletes with the cluster. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. # Validate a cluster specification Source: https://docs.ankra.ai/api-reference/clusters/validate-a-cluster-specification https://platform.ankra.app/openapi.json post /api/v1/clusters/validate Checks an ImportCluster specification without applying it and returns the errors and warnings an apply would hit: resource definitions, unique names, parent references and dependency cycles, chart references, and secret encryption. Pass `cluster_id` to validate against that cluster's current state, exactly as an update would. # Withdraw a custom DNS zone from the cluster Source: https://docs.ankra.ai/api-reference/clusters/withdraw-a-custom-dns-zone-from-the-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/{cluster_id}/custom-dns-zones/{zone} Withdraws the declaration. The scheduler's next pass republishes the cluster's custom-dns stack without it, which removes the external-dns deployment; the zone itself and the records already published into it are untouched. # Change a cost budget Source: https://docs.ankra.ai/api-reference/cost/change-a-cost-budget https://platform.ankra.app/openapi.json put /api/v1/org/cloud-cost/budgets/{budget_id} Changes a budget's name, amount, currency, owner or notify threshold; a field left out keeps its value and owner_user_id null clears the owner. A budget's scope never changes: naming scope_kind or scope_id is refused. Audited as update_cost_budget with what it replaced. Gated on billing.manage; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Change the cost autopilot policy Source: https://docs.ankra.ai/api-reference/cost/change-the-cost-autopilot-policy https://platform.ankra.app/openapi.json put /api/v1/org/cloud-cost/autopilot A partial policy write. defaults names only the environment kinds it changes; quiet_hours and notification_route_id change only when present, and null clears them; a field the policy does not have is refused (400). Needs billing.manage and clusters.write (at the organisation); the token stands in for the browser route's CSRF double-submit. The change and what it replaced are written to the audit log in the same transaction. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. The policy answered lists the clusters of the scope project_id names, as the read does. # Clean up a batch of cloud waste Source: https://docs.ankra.ai/api-reference/cost/clean-up-a-batch-of-cloud-waste https://platform.ankra.app/openapi.json post /api/v1/org/cloud-cost/waste/resolve-batch Starts the cleanup of up to 100 cloud-waste findings at once, one operation per finding, and answers each finding's outcome: started with its operation, refused with the platform's own reason (resolved already, a cleanup running, no consent, not old enough, a kind Ankra does not remove, no clusters.write on a released persistent volume's cluster, or what that volume's live read showed), or failed when the cleanup could not be started. Each finding is decided exactly as the single resolve route decides it; the written consent and minimum age apply to every finding that needs them. Answers 200 when at least one started (partial: true when not all did) and 409, with the same results, when none did. Needs billing.manage. A token pinned to a project, or a member limited to projects, cleans up only the findings their waste read lists; any other is refused as not found. Bearer-PAT twin of the browser route of the same name under /org. # Clean up one cloud-waste finding Source: https://docs.ankra.ai/api-reference/cost/clean-up-one-cloud-waste-finding https://platform.ankra.app/openapi.json post /api/v1/org/cloud-cost/waste/{finding_id}/resolve Starts the cleanup of one open cloud-waste finding and answers the operation that does it (`ankra cost waste resolve`). What happens is decided by the kind of waste, never by the caller: a stopped server gets a snapshot image kept for seven days and is deleted only once the image is available; an unassigned IP or an idle load balancer is released. An unattached volume has no snapshot (the provider offers none), so it is deleted only with written consent in the body (`consent: delete_unattached_volume_acknowledged`, the value consent_by_kind names for the kind, or the generic `no_snapshot_acknowledged`) and only once it has been seen unattached for at least `min_unattached_days` (30, or more if asked): the consent is recorded on the finding and in the audit log, and the job re-reads the volume and refuses one attached again or delete-protected. A released persistent volume (released_pv) keeps its disk because its reclaim policy is Retain, so it is cleaned up the Kubernetes way: with the same written consent and minimum age, and with clusters.write on its cluster, the volume is read live from its cluster and, only when it is still Released and was created by a cloud provider's CSI driver that deletes the disks it provisioned, its reclaim policy is set to Delete (action reclaim_delete); the operation then waits for the volume to leave its cluster before the saving is counted. A pre-provisioned volume, one of another driver, one bound again, one already gone, or one that could not be read live is refused and left unchanged. The body is optional; without one no consent is given. Needs billing.manage. A token pinned to a project, or a member limited to projects, cleans up only the findings their waste read lists (stamped with their project, on a cluster they may read); any other answers 404. Bearer-PAT twin of the browser route of the same name under /org. # Clear a cluster's cost autopilot tier Source: https://docs.ankra.ai/api-reference/cost/clear-a-clusters-cost-autopilot-tier https://platform.ankra.app/openapi.json delete /api/v1/org/cloud-cost/autopilot/clusters/{cluster_id} Returns one cluster to its environment's default tier. Clearing a cluster with no override changes nothing and writes no audit row. The answer is the cluster's tier as it now stands. Needs billing.manage and clusters.write (at the cluster); the token stands in for the browser route's CSRF double-submit. The change and what it replaced are written to the audit log in the same transaction. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Clear a project's cost autopilot tier Source: https://docs.ankra.ai/api-reference/cost/clear-a-projects-cost-autopilot-tier https://platform.ankra.app/openapi.json delete /api/v1/org/cloud-cost/autopilot/projects/{project_id} Returns a project's clusters to the organisation's defaults by removing the project's own autopilot defaults (ADR 0022). Clearing a project that set none changes nothing and writes no audit row. The answer is the project's layer as it now stands (empty). Needs billing.manage and clusters.write; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org. # Clear a rate card Source: https://docs.ankra.ai/api-reference/cost/clear-a-rate-card https://platform.ankra.app/openapi.json delete /api/v1/org/cloud-cost/rate-cards/{credential_id} Removes a stated card so its capacity falls back to the provider default, or becomes unpriced when the provider ships none. Gated on billing.manage. A token pinned to a project, or a member limited to projects, clears the cards of their own project's credentials only: the organisation fallback card and a credential no project owns are refused with 403, another project's credential answers 404; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Create a cost budget Source: https://docs.ankra.ai/api-reference/cost/create-a-cost-budget https://platform.ankra.app/openapi.json post /api/v1/org/cloud-cost/budgets Adds a monthly budget for the organisation, one cluster, one environment label or one application. A scope holds at most one budget (409). Audited as create_cost_budget. Gated on billing.manage; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Delete a cost budget Source: https://docs.ankra.ai/api-reference/cost/delete-a-cost-budget https://platform.ankra.app/openapi.json delete /api/v1/org/cloud-cost/budgets/{budget_id} Removes a budget; its card is no longer raised. Audited as delete_cost_budget. Gated on billing.manage; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Download the provider invoice lines for a month Source: https://docs.ankra.ai/api-reference/cost/download-the-provider-invoice-lines-for-a-month https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/reconciliation.csv Every billed line of the month's counted provider invoices as a CSV download, one row per line, for the same month and project scope as the reconciliation read and behind the same billing.read gate. Amounts stay in the provider's currency, in minor units, credits negative. An invoice two credentials of one provider account both imported is listed once, under the credential the reconciliation counts it under. On a one-project read only the lines placed on the project's clusters are listed. Text cells that would start a spreadsheet formula are prefixed with an apostrophe. Bearer-PAT twin of the browser route of the same name under /org. # Estimate what a change to an application costs Source: https://docs.ankra.ai/api-reference/cost/estimate-what-a-change-to-an-application-costs https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/cost/estimate Prices a proposed change to an application as the monthly change to its cost. A change to one deployment - its replica count, its per-pod CPU and memory requests, parking it, or promoting a build into it - is read from its object cost projection and moved by the metering's own allocation rule; a new environment or a database preset is priced on the cluster it would run on as the share that cluster's latest metering pass would have allocated it, compute only, so a floor. The answer carries the confidence of the snapshot it is read from and how it was reached. A change Ankra cannot price (a namespace another application shares, a cluster with no recent snapshot or none recording its capacity yet, a parked deployment's size, a workload the cluster has no room for) answers priced false with the reason, never zero. Nothing is stored. Bearer-PAT twin of the browser route of the same name under /org. # Every move on the cost line, named by what happened Source: https://docs.ankra.ai/api-reference/cost/every-move-on-the-cost-line-named-by-what-happened https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/events The events that moved the organisation's cost run rate over the last `days` UTC days (`ankra cost events`), newest first, in the caller's display currency: application releases (version changes in the clusters' resource graphs), node-count changes between adjacent priced hours, scheduled and manual stops and starts, executed cost decisions, clusters entering or leaving pricing, and resolved cloud waste. A snapshot-measured event carries window_delta_monthly_cents, how the cluster's run rate moved between the priced hour before it and the first priced hour at least 30 minutes after it; delta_monthly_cents is set only when the event was alone in that window, no other event on the cluster being measured across any of its hours (delta_status isolated). Events whose windows overlap by any span, identical or not, are shared: each carries its window's move and none claims a share of it. A start alone in a window whose run rate fell, or a stop alone in one whose run rate rose, is contradicted: something else moved it, so it carries the window's move and no delta of its own. metering_gap means the cluster was not priced for some hours inside the event's window (a start after a stop it was not metered through, a missed metering hour): the run rate either side of the gap is no move of the event's, so it carries neither delta, and an event whose window overlaps it is still shared. no_snapshots means no priced hour on one side, so the move is unknown, never zero. A coverage event's delta is coverage, not spend; a resolved finding's is its own monthly cost (own_cost), unknown (unpriced), or not_counted with no delta when other organisations also saw the resource and its cost counts in another organisation or in none. sources says which kinds could be read; a kind marked unavailable is missing from events, not quiet. At most 500 events (truncated). Bearer-PAT twin of the browser route of the same name under /org. # Get a cluster's cost estimate Source: https://docs.ankra.ai/api-reference/cost/get-a-clusters-cost-estimate https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/cost A cluster's latest cost snapshot with its daily trend and namespace allocation, in the caller's display currency, or its readiness when no estimate exists yet (`ankra cost cluster`). Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Get a cluster's cost projection Source: https://docs.ankra.ai/api-reference/cost/get-a-clusters-cost-projection https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/cluster/{cluster_id} What one cluster costs: its run rate at the latest snapshot inside the window, its idle share, its share of the fleet, its open waste and 30 days of daily cost. Bearer-PAT twin of the browser route of the same name under /org. # Get a cluster's namespace cost history Source: https://docs.ankra.ai/api-reference/cost/get-a-clusters-namespace-cost-history https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/cost/namespaces/history One cluster's namespace allocations over the window the metering keeps (35 days of hourly snapshots), in day buckets or, for at most 7 days, hour buckets, in the caller's display currency. Each bucket says how many of its hours the metering attributed to namespaces; a namespace's value for a bucket with none is null (unknown), never 0, and a namespace missing from an attributed hour was allocated nothing that hour. Nothing is extrapolated. Bearer-PAT twin of the browser route of the same name under /org. # Get a credential's cost projection Source: https://docs.ankra.ai/api-reference/cost/get-a-credentials-cost-projection https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/credential/{credential_id} What one cloud credential costs: the run rate of the priced clusters it provisioned, resolved the way the fleet summary groups spend by credential, plus the open waste found with it, and the month so far of the clusters it provisioned that exist now, the sum of their own months. A cluster deleted this month is not counted: the month's records name no credential, and only live clusters are resolved to one. Bearer-PAT twin of the browser route of the same name under /org. # Get a namespace's cost projection Source: https://docs.ankra.ai/api-reference/cost/get-a-namespaces-cost-projection https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/namespace/{cluster_id}/{namespace} What one namespace of a cluster costs: its allocation at the cluster's latest attributed metering pass, its share of the fleet and 30 days of daily allocation. A pass that attributed the cluster but not this namespace allocated it nothing; a pass that attributed no namespace leaves it unknown. Bearer-PAT twin of the browser route of the same name under /org. # Get a stack's cost projection Source: https://docs.ankra.ai/api-reference/cost/get-a-stacks-cost-projection https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/stack/{cluster_id}/{stack_name} What one stack of a cluster costs: the allocations of the namespaces its cluster's latest metering pass attributes to it (a namespace two stacks claim is attributed to neither), its share of the fleet and 30 days of daily cost. Bearer-PAT twin of the browser route of the same name under /org. # Get an application's cost projection Source: https://docs.ankra.ai/api-reference/cost/get-an-applications-cost-projection https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/application/{application_id} What one application costs across every namespace it is installed in. A namespace another application also runs in is marked shared and counted whole rather than split on no basis. Bearer-PAT twin of the browser route of the same name under /org. # Get project cost Source: https://docs.ankra.ai/api-reference/cost/get-project-cost https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/project/{project_id} What one project costs: the run rate of the priced clusters it holds now, plus the open waste found on them, and what was spent under the project's stamp this month. A cluster that moved projects keeps what it spent where it was spent, and a cluster deleted this month stays in the month of the project it was in, so the month equals the project's by_project figure in the fleet summary. Bearer-PAT twin of the browser route of the same name under /org. # Get the cloud cost settings Source: https://docs.ankra.ai/api-reference/cost/get-the-cloud-cost-settings https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/settings The organisation's stored cost settings, the display currency effective for the token's user, and the FX table (`ankra cost settings get`). Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Get the cost autopilot policy Source: https://docs.ankra.ai/api-reference/cost/get-the-cost-autopilot-policy https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/autopilot How much Ankra may do about cost unasked (`ankra cost autopilot`): the tier each environment kind defaults to, the quiet hours, the notification route kept with the policy (no notice is sent to it today), the four tiers with what each lets Ankra do, and every live cluster with its tier, whether an override or its environment set it, and the sentence a card shows for it. An organisation that never set a policy reads is_set false and Hands-off for every kind; recommended_defaults is the policy the Autopilot view offers to turn on. Any member can read it. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. The clusters listed are those of one project - the one project_id names, else the caller's current project - that the caller may read; scope says what was read. # Get the organisation's fleet cost rollup Source: https://docs.ankra.ai/api-reference/cost/get-the-organisations-fleet-cost-rollup https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/summary Every cluster whose latest snapshot is inside the staleness window, grouped by provider and by provisioning credential, in the caller's display currency (`ankra cost summary`). Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Get the provider invoice reconciliation for a month Source: https://docs.ankra.ai/api-reference/cost/get-the-provider-invoice-reconciliation-for-a-month https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/reconciliation What each cloud credential's provider billed for a month against what the metering estimated for the same clusters. Lines stay in the provider's currency; the estimate is converted at the stated rate. A credential with no imported document is absent, a failed import says so, a line nothing places on a cluster is listed with its amount, and a difference is stated only when the provider's figures are final and the clusters the metering fully covered hold at least half of the invoice; it is over those clusters, never the whole invoice, and difference_coverage says what share of the invoice and of the hours they hold. Bearer-PAT twin of the browser route of the same name under /org. # List applications' cost projections Source: https://docs.ankra.ai/api-reference/cost/list-applications-cost-projections https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/objects/application Every application of the organisation with its cost projection in one call - the single application read's figures without the trend and the waste - and the fleet's run rate split into the namespaces applications run in (each once), the rest of the attributed clusters, and the clusters whose pass attributed no namespace. Bearer-PAT twin of the browser route of the same name under /org. Reads the applications of one project - the one project_id names, else the caller's current project - over the clusters the caller may read; scope says what was read. # List open cloud waste findings Source: https://docs.ankra.ai/api-reference/cost/list-open-cloud-waste-findings https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/waste The organisation's open waste findings from the latest scan, with the credentials whose sweep failed so an incomplete scan never reads as zero waste. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # List the cost budgets Source: https://docs.ankra.ai/api-reference/cost/list-the-cost-budgets https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/budgets Every budget of the organisation, largest first, each with its month as of the read: spent so far, the projected month end at the current pace, the hour it crosses, and where it lands once the changes already approved or running have landed. Figures are in the budget's own currency; a scope with no priced cluster answers status unknown with null figures rather than zero. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # List the fleet's namespace allocations Source: https://docs.ankra.ai/api-reference/cost/list-the-fleets-namespace-allocations https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/workloads The latest namespace allocation of every analysed cluster, decorated with the stack, addons and applications running in each namespace, ordered by allocated monthly cost. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # List the private-capacity rate cards Source: https://docs.ankra.ai/api-reference/cost/list-the-private-capacity-rate-cards https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/rate-cards The private credentials the caller may read with the rates that price them, the organisation fallback card, the provider defaults, and how many of the private clusters the caller may read nothing prices yet. A token pinned to a project or a member limited to projects reads their project's credentials and clusters only, never another project's or a credential no project owns; a caller narrowed to the clusters they hold clusters.read on counts and prices those clusters only. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Measured outcomes of the organisation's cost decisions Source: https://docs.ankra.ai/api-reference/cost/measured-outcomes-of-the-organisations-cost-decisions https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/ledger The measured outcomes of the organisation's cost decisions (`ankra cost ledger`): every cost proposal that was approved, is running or has run, with what the surface expected it to save, what was measured seven days after it ran (the subject cluster's mean run rate over the week before the change minus its mean over the week after it, from cost snapshots, never an estimate; unmeasured when another measured change on the cluster overlaps it), where the measurement stands and why it could not be made, in the caller's display currency. measured_total_cents sums measured rows only; running_total_cents sums the expectations of approved, running and still-verifying changes, leaving out an approval older than seven days whose change never started (approval_expired on its row). Bearer-PAT twin of the browser route of the same name under /org. # Price a cluster's current configuration for a what-if draft Source: https://docs.ankra.ai/api-reference/cost/price-a-clusters-current-configuration-for-a-what-if-draft https://platform.ankra.app/openapi.json get /api/v1/org/clusters/{cluster_id}/cost/estimate Prices what the cluster runs today - its node groups (from the machines Ankra provisioned, or the synced nodes grouped by pool) and load balancers - from the same price book or rate card the metering loop bills from, in the caller's display currency. The estimate echoes the baseline so a draft can start from it. Bearer-PAT twin of the browser route of the same name under /org. # Price a what-if node-group draft against a cluster Source: https://docs.ankra.ai/api-reference/cost/price-a-what-if-node-group-draft-against-a-cluster https://platform.ankra.app/openapi.json post /api/v1/org/clusters/{cluster_id}/cost/estimate Prices a draft configuration - the full set of node groups the prototype cluster would run (count, vCPU, memory, disk, spot) and optionally its network resources - beside the cluster's current configuration, and predicts the monthly bill as the metered run rate plus the difference. In catalogue mode each group is placed on the cheapest published size that holds it (locked to the current size's CPU architecture); private capacity is priced per vCPU, GB of memory and GB of disk from the rate card. Nothing is stored. Bearer-PAT twin of the browser route of the same name under /org. # Resolved cloud waste Source: https://docs.ankra.ai/api-reference/cost/resolved-cloud-waste https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/waste/resolved Totals what Ankra's cleanup resolved in the last `days` days (default 30, at most 90), by kind, in the caller's display currency: the monthly cost the resolved findings were billing. Only findings Ankra's cleanup resolved count; a finding the scan stopped seeing for another reason is not. A finding whose resource other organisations also saw, and whose cost counts in another organisation or in none, is counted in shared_not_counted_count instead. Unpriced findings are counted in unpriced_count and never summed as zero, and monthly_cost_cents is null when none was priced. Bearer-PAT twin of the browser route of the same name under /org. # Savings model for the organisation Source: https://docs.ankra.ai/api-reference/cost/savings-model-for-the-organisation https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/savings The organisation's savings model: right-size and off-hours recommendations across the biggest priced clusters, the cost breakdowns they were read from, the clusters the fleet cannot price, and the waste scan, in the caller's display currency (`ankra cost savings`). One cluster counts once in the total, at its best lever. Bearer-PAT twin of the browser route of the same name under /org. # Set a cluster's cost autopilot tier Source: https://docs.ankra.ai/api-reference/cost/set-a-clusters-cost-autopilot-tier https://platform.ankra.app/openapi.json put /api/v1/org/cloud-cost/autopilot/clusters/{cluster_id} Puts one cluster in a tier whatever its environment kind, with the reason a card shows (3 to 500 characters), until expires_at (omitted for no expiry; it must be in the future). It replaces the cluster's previous override. The answer is the cluster's tier as it now stands. Needs billing.manage and clusters.write (at the cluster); the token stands in for the browser route's CSRF double-submit. The change and what it replaced are written to the audit log in the same transaction. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Set a project's cost autopilot tier Source: https://docs.ankra.ai/api-reference/cost/set-a-projects-cost-autopilot-tier https://platform.ankra.app/openapi.json put /api/v1/org/cloud-cost/autopilot/projects/{project_id} Sets a project's autopilot tier per environment kind (ADR 0022), a layer between a cluster's override and the organisation's defaults: the body is {"defaults": {kind: tier, ...}} and is the whole layer, a kind it does not name falls through to the organisation. Needs billing.manage and clusters.write; the token stands in for the browser route's CSRF double-submit. The change and what it replaced are written to the audit log in the same transaction. Bearer-PAT twin of the browser route of the same name under /org. # State a rate card Source: https://docs.ankra.ai/api-reference/cost/state-a-rate-card https://platform.ankra.app/openapi.json put /api/v1/org/cloud-cost/rate-cards Writes the monthly rates for one private credential, or the organisation fallback card. Gated on billing.manage. A token pinned to a project, or a member limited to projects, writes the cards of their own project's credentials only: the organisation fallback card and a credential no project owns are refused with 403, another project's credential answers 404; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # The fleet's run rate per day, with coverage drawn apart from spend Source: https://docs.ankra.ai/api-reference/cost/the-fleets-run-rate-per-day-with-coverage-drawn-apart-from-spend https://platform.ankra.app/openapi.json get /api/v1/org/cloud-cost/trend The organisation's cost run rate per UTC day over every priced cluster (`ankra cost trend`), in the caller's display currency. A cost snapshot exists only for an hour a cluster was priced, so a cluster missing from a day was not priced that day - it did not cost nothing. Each point says how many clusters it covers (priced_cluster_count, and fully_priced_cluster_count for those whose run rate is the whole cost, not a floor), and a day no cluster was priced has a null run rate. coverage_changes lists every day the set of priced clusters changed, naming the clusters that entered and left and what that moved the line by: a coverage move, never a spend move. The last point (current) is the summary's own run rate - each cluster's latest snapshot within 26 hours, deleted clusters out - so the line ends where GET /api/v1/org/cloud-cost/summary does; earlier points are each cluster's mean run rate over its snapshots of the UTC day, not its last hour. clusters carries each priced cluster's span over the window, when it was first priced (first_priced_exact false means the earliest priced hour still known, not necessarily the first), when it was created and deleted, its own run rate per day index-aligned with points (daily_monthly_cost_estimate_cents, null on a day it was not priced), so the line can be stacked by cluster, and what its current estimate leaves out (estimate_gaps). Bearer-PAT twin of the browser route of the same name under /org. # Update the cloud cost settings Source: https://docs.ankra.ai/api-reference/cost/update-the-cloud-cost-settings https://platform.ankra.app/openapi.json put /api/v1/org/cloud-cost/settings Sets the effective discount and the organisation default currency (`ankra cost settings set`). Gated on billing.manage. The settings are the organisation's, every project's, so a token pinned to a project, or a member limited to projects, is refused with 403 whatever organisation role they also hold; the token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Create a registry project Source: https://docs.ankra.ai/api-reference/credentials/create-a-registry-project https://platform.ankra.app/openapi.json post /api/v1/org/registry-projects Create an extra registry project for the organisation, private and under the same scan policy, retention policy and storage quota as its own. A robot account is bound to one project, so an extra project is how a login is given access to less than everything the organisation publishes. An organisation can hold up to extra_project_limit extra projects. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Create a registry robot Source: https://docs.ankra.ai/api-reference/credentials/create-a-registry-robot https://platform.ankra.app/openapi.json post /api/v1/org/registry-robots Create a robot account on the organisation's Ankra registry project, with a preset scope (push, which pushes and pulls, or pull) or its own list of permissions, and optionally an expiry in days. The robot is a project-level robot: it reaches the organisation's project and no other. Stating permissions that reach past pull and push additionally needs the credentials.reveal permission. The secret is returned once, in this response, together with the docker login command that uses it; the login is stored as a platform-managed registry credential named ankra-harbor-robot-. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Delete a credential Source: https://docs.ankra.ai/api-reference/credentials/delete-a-credential https://platform.ankra.app/openapi.json delete /api/v1/org/credentials/{credential_id} Deletes a credential. A credential a cluster still uses is refused with 409, and a Git credential a cluster's GitOps binding still names can be refused unless `replacement_credential_id` re-points those bindings in the same delete. Requires `credentials.delete`. # Delete a registry project Source: https://docs.ankra.ai/api-reference/credentials/delete-a-registry-project https://platform.ankra.app/openapi.json delete /api/v1/org/registry-projects/{project_name} Delete an extra registry project. Nothing else is deleted on the way: a project that still holds repositories, or that robot accounts are still bound to, is refused with 409, and so is the organisation's own project. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Discover a registry connection Source: https://docs.ankra.ai/api-reference/credentials/discover-a-registry-connection https://platform.ankra.app/openapi.json post /api/v1/org/registry-connections/discover Discover what an admin credential sees on a registry before any registry entry points at it: Ankra acts as the credential, over the same SSRF-guarded client the robot lane uses, and lists the projects (Harbor, up to 500 by name, truncated true past that) or the Docker repositories (Google Artifact Registry; only the named one when the URL names a repository) robots and registry entries can be made for. Nothing is written or minted. A connection that fails - the credential refused, the registry unreachable, a credential that cannot be used there, a Google URL naming no project, or a registry Ankra cannot make robots on - answers 200 with ok false, no projects and a sentence saying why. A credential the platform manages is refused (422). Requires credentials.write and credentials.reveal. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication, CSRF-guarded). # Get a credential's rotation status Source: https://docs.ankra.ai/api-reference/credentials/get-a-credentials-rotation-status https://platform.ankra.app/openapi.json get /api/v1/org/credentials/{credential_id}/propagation Reports where the credential's latest recorded rotation has got to in each cluster linked to it, and whether the old value is safe to revoke: safe_to_revoke is true once every running cluster whose copy Ankra renders serves the new value, and blocking_clusters names the rest. Stopped, not_covered and platform_only clusters do not block it and are still listed; a not_covered cluster carries the command that updates its copy by hand. A credential with no recorded rotation is safe to revoke only when no cluster is linked to it. The answer carries no secret material. Requires `credentials.read`. # Get credential details Source: https://docs.ankra.ai/api-reference/credentials/get-credential-details https://platform.ankra.app/openapi.json get /api/v1/org/credentials/{credential_id} Returns one credential including its decrypted secret material, and records the read in the audit log. Requires `credentials.reveal`; answers 404 when the credential does not exist. An SSH-key credential is the exception: it stores only a public key, which is not a secret, so its detail (the public key with its fingerprint, key type and comment, and the clusters it is attached to) answers on `credentials.read` and records no reveal. # Get paid registry storage Source: https://docs.ankra.ai/api-reference/credentials/get-paid-registry-storage https://platform.ankra.app/openapi.json get /api/v1/org/registry-storage/addon Read the organisation's paid registry storage: the storage blocks it holds on top of its base limit, the price of a block, the limit they make up, and whether more can be bought (available, billing_ready, billing_blocker). Requires credentials.read or billing.manage. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Get registry retention Source: https://docs.ankra.ai/api-reference/credentials/get-registry-retention https://platform.ankra.app/openapi.json get /api/v1/org/registry-storage/retention Read which images the organisation keeps: its project-wide retention policy (or the platform default), its per-repository rules, the protections every organisation-set policy carries, when retention runs and when deleted images stop counting against storage. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Get registry storage Source: https://docs.ankra.ai/api-reference/credentials/get-registry-storage https://platform.ankra.app/openapi.json get /api/v1/org/registry-storage Read the organisation's registry storage: the storage limit each of its registry projects carries, whether that limit is the platform default or one set for the organisation (and why), and how much each project holds. usage_status says whether the figures are complete, a lower bound (partial), unknown (the registry could not be read; used_bytes is then absent, never zero) or not_provisioned (no registry project yet). A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List a Bitbucket Cloud repository's branches Source: https://docs.ankra.ai/api-reference/credentials/list-a-bitbucket-cloud-repositorys-branches https://platform.ankra.app/openapi.json get /api/v1/org/bitbucket-cloud/{credential_id}/branches Lists the branches of a Bitbucket Cloud repository through a stored Bitbucket Cloud credential, for the repository and branch pickers. The credential must belong to the caller's organisation, and a personal credential must be the caller's own; any other id answers 404. Every page Bitbucket returns is followed, and default_branch is the repository's main branch, or null for an empty repository. A listing that cannot be read whole is refused with a 422 rather than answered partially, including a repository with more than 5000 branches. A 422 whose remedy is reconnecting the credential carries detail {message, error_code: bitbucket_cloud_credential_invalid}; any other failure Bitbucket reports is a plain-string detail. # List a Bitbucket Cloud repository's branches (browser session) Source: https://docs.ankra.ai/api-reference/credentials/list-a-bitbucket-cloud-repositorys-branches-browser-session https://platform.ankra.app/openapi.json get /org/bitbucket-cloud/{credential_id}/branches Lists the branches of a Bitbucket Cloud repository through a stored Bitbucket Cloud credential, for the repository and branch pickers. The credential must belong to the caller's organisation, and a personal credential must be the caller's own; any other id answers 404. Every page Bitbucket returns is followed, and default_branch is the repository's main branch, or null for an empty repository. A listing that cannot be read whole is refused with a 422 rather than answered partially, including a repository with more than 5000 branches. A 422 whose remedy is reconnecting the credential carries detail {message, error_code: bitbucket_cloud_credential_invalid}; any other failure Bitbucket reports is a plain-string detail. # List credentials Source: https://docs.ankra.ai/api-reference/credentials/list-credentials https://platform.ankra.app/openapi.json get /api/v1/org/credentials Lists the organisation's credentials of every kind with their metadata, optionally filtered by `provider`. Requires `credentials.read`. # List registry projects Source: https://docs.ankra.ai/api-reference/credentials/list-registry-projects https://platform.ankra.app/openapi.json get /api/v1/org/registry-projects List the organisation's registry projects: its own first (named default), then the extra projects it created, each with how many repositories it holds and how many robot accounts are bound to it. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List registry providers Source: https://docs.ankra.ai/api-reference/credentials/list-registry-providers https://platform.ankra.app/openapi.json get /api/v1/org/registry-providers List the registry providers robots can be made on and what each supports: Harbor (the Ankra registry and any integrated Harbor), Google Artifact Registry (pkg.dev and gcr.io hosts), and any other OCI registry, on which no robot can be made. Each states whether its robots can expire, whether custom permissions can be picked, the permission catalogue a create picks from, and what the admin credential's username looks like. Requires credentials.read. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List registry repositories by size Source: https://docs.ankra.ai/api-reference/credentials/list-registry-repositories-by-size https://platform.ankra.app/openapi.json get /api/v1/org/registry-storage/repositories List every repository of the organisation's registry projects, largest first, with its image count, approximate size and last push, and the retention rule that governs it. CI build images live under ankra-ci/. Sizes count layers shared between images once per image, so they rank repositories but do not add up to the storage figure. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List the organisation's registry robots Source: https://docs.ankra.ai/api-reference/credentials/list-the-organisations-registry-robots https://platform.ankra.app/openapi.json get /api/v1/org/registry-robots List every robot login the organisation holds: the robot accounts its members created on its Ankra registry project, then the ones Ankra manages (the organisation's ci and pull robots and one push robot per application, marked managed and read-only here). The answer also carries the registry project robots are minted on and the permissions a robot may be created with. Secrets are never listed. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List the repositories a credential covers Source: https://docs.ankra.ai/api-reference/credentials/list-the-repositories-a-credential-covers https://platform.ankra.app/openapi.json get /api/v1/org/credentials/{credential_id}/repositories For a GitHub credential, lists the repositories its installation can reach, the repositories Ankra needs it to reach, and a verdict on the difference; other credential kinds answer 409. Requires `credentials.read`. # Read a registry robot Source: https://docs.ankra.ai/api-reference/credentials/read-a-registry-robot https://platform.ankra.app/openapi.json get /api/v1/org/registry-robots/{robot_name} Read one robot's record: its registry login, the projects it reaches, its permissions, when it was created and last rotated, and when it expires. A member's robot is read by its name; a robot Ankra manages by ci, pull, or app-. The secret is never returned here. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Revoke a registry robot Source: https://docs.ankra.ai/api-reference/credentials/revoke-a-registry-robot https://platform.ankra.app/openapi.json delete /api/v1/org/registry-robots/{robot_name} Delete the robot on the registry and the credential holding its login. Anything still using the login stops working, which is the point of a revoke. A robot Ankra manages is refused with 409. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Rotate a credential's secret in place Source: https://docs.ankra.ai/api-reference/credentials/rotate-a-credentials-secret-in-place https://platform.ankra.app/openapi.json put /api/v1/org/credentials/{credential_id} Replaces the credential's secret without recreating it, so clusters and other resources that reference it keep working. The supplied fields are probed against the provider first: a refused probe answers 200 with `rotated: false` and the probe's code, message and hints, and the stored secret is left unchanged. The new value is then read against every cluster that uses the credential (its network, then its servers): a cluster it cannot see refuses the rotation with code `foreign_project`, one that could not be checked with `cluster_unreachable`, both listed in `unreachable_clusters`; set `force` to save anyway. Connection settings cannot be rotated. Requires `credentials.write`; a credential pinned to another project answers 404. # Rotate a registry robot's secret Source: https://docs.ankra.ai/api-reference/credentials/rotate-a-registry-robots-secret https://platform.ankra.app/openapi.json post /api/v1/org/registry-robots/{robot_name}/rotate Mint a new secret for the robot and return it once. The previous secret stops working. A robot the registry no longer has is minted again under the same name with its recorded permissions and the rest of its lifetime. A rotation never extends a robot: an expired one, and a robot Ankra manages, are refused with 409. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Run registry retention now Source: https://docs.ankra.ai/api-reference/credentials/run-registry-retention-now https://platform.ankra.app/openapi.json post /api/v1/org/registry-storage/retention/run Start the retention of every registry project of the organisation now instead of at its daily time. Deleted images stop counting against storage at the registry's next garbage collection. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Set or clear a credential display name Source: https://docs.ankra.ai/api-reference/credentials/set-or-clear-a-credential-display-name https://platform.ankra.app/openapi.json patch /api/v1/org/credentials/{credential_id} Sets the credential's human label, or clears it with `null` or an empty string so the credential name shows again. The credential name, which other resources reference, never changes. An omitted `display_name` is refused with 422. A credential the platform manages is refused with 409. Requires `credentials.write`; a credential pinned to another project answers 404. # Set paid registry storage Source: https://docs.ankra.ai/api-reference/credentials/set-paid-registry-storage https://platform.ankra.app/openapi.json put /api/v1/org/registry-storage/addon Set how many paid storage blocks the organisation holds and apply the new limit to the registry at once. Each block adds block_gib GiB to every registry project's limit and is billed on the monthly invoice, prorated by the days held. Buying more needs billing that can pay (402 with a sentence saying what to fix on the Billing page; 503 when billing could not be checked). More than max_blocks, a decrease below what the registry holds, or a purchase while sales are paused answer 422 with the figures. A stale expected_version answers 409. Requires billing.manage. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication, CSRF header). # Update registry retention Source: https://docs.ankra.ai/api-reference/credentials/update-registry-retention https://platform.ankra.app/openapi.json put /api/v1/org/registry-storage/retention Change the organisation's project-wide retention policy (null resets it to the platform default), its repository rules (the list replaces the stored one; an empty list removes them all), or both, and apply the result to the registry at once. The newest image of every repository is always kept. harbor_applied false means the change is saved and applies on the next provisioning pass, within about five minutes. Send the version you read as expected_version: if someone changed the retention since, the save answers 409 and writes nothing. A change that needs more retention rules than the registry runs for one project (rule_slots in the read) answers 422 saying how many it needs, and writes nothing. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Validate credential name Source: https://docs.ankra.ai/api-reference/credentials/validate-credential-name https://platform.ankra.app/openapi.json get /api/v1/org/credentials/validate Checks whether `credential_name` is valid and still free in the organisation, returning `valid`, a message, the name's RFC 1035 form and, when the conflicting credential is one you can see, its ID. # Verify an integrated registry connection Source: https://docs.ankra.ai/api-reference/credentials/verify-an-integrated-registry-connection https://platform.ankra.app/openapi.json post /api/v1/org/registry-connections/verify Check that an admin credential can manage robots on an integrated registry entry, before a robot is made there: Ankra acts as the credential and lists the projects (Harbor) or Docker repositories (Google Artifact Registry) robots can be bound to. Nothing is minted. A connection that fails - the credential refused, the registry unreachable, a credential that cannot be used there, or a registry Ankra cannot make robots on - answers 200 with ok false and a sentence saying why. Requires credentials.write and credentials.reveal. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication, CSRF-guarded). # Create data source credential Source: https://docs.ankra.ai/api-reference/data-source-credentials/create-data-source-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/datasource Bearer token authentication; RBAC permission `credentials.write`. Creates the org-scoped credential the metrics and log data sources reference by credential_name; the secret is stored in Vault and never echoed. # Create data source credential (browser session) Source: https://docs.ankra.ai/api-reference/data-source-credentials/create-data-source-credential-browser-session https://platform.ankra.app/openapi.json post /org/credentials/datasource Browser session authentication; RBAC permission `credentials.write`. Creates the org-scoped credential the metrics and log data sources reference by credential_name; the secret is stored in Vault and never echoed. # List data source credentials Source: https://docs.ankra.ai/api-reference/data-source-credentials/list-data-source-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/datasource Bearer PAT authentication; RBAC permission `credentials.read`. Returns credential metadata only; secret material is never returned. # List data source credentials (browser session) Source: https://docs.ankra.ai/api-reference/data-source-credentials/list-data-source-credentials-browser-session https://platform.ankra.app/openapi.json get /org/credentials/datasource Browser session authentication; RBAC permission `credentials.read`. Returns credential metadata only; secret material is never returned. # A decision proposal's activity feed Source: https://docs.ankra.ai/api-reference/decisions/a-decision-proposals-activity-feed https://platform.ankra.app/openapi.json get /api/v1/org/decisions/{decision_id}/activity Everything that happened to the proposal, newest first: proposed, reopened, approved, set_aside, superseded, execution_started, dispatched, succeeded, failed, each with its actor, note and detail. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Approve a decision proposal Source: https://docs.ankra.ai/api-reference/decisions/approve-a-decision-proposal https://platform.ankra.app/openapi.json post /api/v1/org/decisions/{decision_id}/approve Records the caller's approval of the plan they read, with an optional note. The body's plan_hash names that plan (the plan_hash the read answered): when the proposal holds another plan by now, because a surface re-posted it since, nothing is approved and the answer is 409 with error_code PLAN_DRIFTED and the proposal as it stands. An approval without plan_hash approves the plan the proposal holds when the request is read; it is accepted for clients released before plan_hash existed and will become required. Allowed from proposed, set_aside (re-opens it) and failed (approving again is the retry); a new decision clears the previous run's receipt. Gated on the area's permission: billing.manage for cost, security.manage for security. The token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Check decision proposals without filing them Source: https://docs.ankra.ai/api-reference/decisions/check-decision-proposals-without-filing-them https://platform.ankra.app/openapi.json post /api/v1/org/decisions/check Answers, for each proposal, what POST /api/v1/org/decisions would answer it with now, and writes nothing: no proposal or activity event is created or changed, and no row or lock is held past the request. The proposals are judged as the caller, by the same validation and refusal logic the upsert runs, against the read replica, so a result can trail a write made a moment before. Each result is ok (the upsert would take it), refused with exactly the status code and body the upsert answers, or unknown when the check could not be evaluated; a client must not read unknown as ok. A check takes at most 24 proposals; a larger one is refused with 422 and nothing is checked. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Hold a decision Source: https://docs.ankra.ai/api-reference/decisions/hold-a-decision https://platform.ankra.app/openapi.json post /api/v1/org/decisions/{decision_id}/hold Holds an approved proposal before it ran, with the caller's reason: a held proposal is run by nothing - neither the cost autopilot nor execute - until it is released or set aside. Allowed from approved. A cost approval older than seven days is refused with 409 (error_code approval_expired) and returned to proposed; a hold does not restart the seven days. The cost autopilot's pre-notice links here (ankra-cozgu.2.2.1). Gated on the area's permission: billing.manage for cost, security.manage for security. The token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # List the organisation's decision proposals Source: https://docs.ankra.ai/api-reference/decisions/list-the-organisations-decision-proposals https://platform.ankra.app/openapi.json get /api/v1/org/decisions The organisation's Security and Cost proposals inside the caller's reach, newest first, narrowed by ?area= (security, cost) and ?status= (proposed, approved, set_aside, running, succeeded, failed, superseded); ?limit= caps the page (default 200, at most 500), and truncated says whether the cap cut it. Running proposals are settled against their platform operation before they are answered, and a cost approval whose change has not started within seven days of it, held or not, is returned to proposed (event approval_expired); a ladder between its waves counts from the wave it last verified. The reach (ADR 0022): a proposal is scoped through the project of the cluster its plan acts on (a waste_cleanup through its cloud waste finding's own project and cluster); a token pinned to a project and a member limited to projects reach their own project only; a caller whose clusters are narrowed by clusters.read (always a member limited to projects, anyone else where RBAC is enforced) reaches those clusters only; and a proposal anchored on no cluster of the organisation - a security proposal, or one whose plan and subject name different clusters - is reached only by a caller who reaches the whole organisation. Every other route answers a proposal outside the reach with 404, as if the organisation did not hold it. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Read one decision proposal Source: https://docs.ankra.ai/api-reference/decisions/read-one-decision-proposal https://platform.ankra.app/openapi.json get /api/v1/org/decisions/{decision_id} One proposal with its plan, evidence, decision and receipt. A running proposal is settled first: the terminal status of its platform operation becomes succeeded or failed, with the outcome appended to the receipt. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Record a computed proposal (upsert by dedupe key) Source: https://docs.ankra.ai/api-reference/decisions/record-a-computed-proposal-upsert-by-dedupe-key https://platform.ankra.app/openapi.json post /api/v1/org/decisions Records what a surface computed. The dedupe key is the proposal's identity within the organisation: a re-post refreshes a proposal nobody has decided and leaves a decided, running or finished one exactly as it was decided, so an approval is always of the plan the person saw; the one exception is a cost approval whose change has not started within seven days, which the re-post returns to proposed with the new computation (event approval_expired). An off_hours_schedule is also keyed by its subject.cluster_id, whichever dedupe key a surface files it under: filing one withdraws an undecided proposal of the same change under another key, or one whose approval expired (superseded, naming the new proposal), and is refused with 409 while one approved within the last seven days, one somebody held, or one that is running stands for it. Proposals of one change are filed one at a time, so two surfaces filing it at once never both stand. Only the right-size verification files a right_size_rollback: a proposal of that kind, or under a dedupe key starting with rollback: (the verification's rollback namespace), is refused with 400, so a re-post can neither file nor change one; a proposal the verification did not file that it finds under a right-size's rollback key is moved to :displaced: (event dedupe_key_displaced) and the verification files its own. status "superseded" withdraws the proposal the dedupe key names instead; withdrawing one somebody approved or set aside erases that decision and takes the area's permission (403 otherwise). Any member who may write may propose; a scoped token needs the area's permission. Answers 201 when the dedupe key was new and 200 when it named an existing proposal. A caller confined to part of the organisation (see GET /api/v1/org/decisions) files, refreshes and withdraws only inside its reach: what it posts, the proposal its dedupe key names and every twin the filing would withdraw must be inside it, and any one outside answers 404, the same answer as a dedupe key the organisation does not hold. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Release a held decision Source: https://docs.ankra.ai/api-reference/decisions/release-a-held-decision https://platform.ankra.app/openapi.json post /api/v1/org/decisions/{decision_id}/release Returns a held proposal to approved; one the cost autopilot filed runs on its next pass once its run_after has passed. Allowed from held. A cost proposal whose approval is older than seven days is refused with 409 (error_code approval_expired) and returned to proposed: a hold does not restart the seven days, so it runs only on a fresh approval against today's figures. A ladder between its waves counts from the wave it last verified. Gated on the area's permission: billing.manage for cost, security.manage for security. The token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Run an approved decision proposal Source: https://docs.ankra.ai/api-reference/decisions/run-an-approved-decision-proposal https://platform.ankra.app/openapi.json post /api/v1/org/decisions/{decision_id}/execute Dispatches the platform operation the proposal's kind maps to (addon_upgrade rewrites the add-on's chart version in its stack; off_hours_schedule creates the stop and start power schedules; waste_cleanup files the platform cleanup job of the cloud waste finding, which snapshots a stopped server before deleting it, releases an unassigned IP or idle load balancer, deletes an unattached volume, and sets a released persistent volume to Delete (after reading it live, and with clusters.write on its cluster), both only with the written consent this request carries; right_size_ladder files its next wave as a right_size child proposal, approved by the caller, and runs it, staying running until that wave's verification concludes; how many nodes of a group one wave moves is the server's split of the wave's count, a plan's own canary split is taken only when it is no coarser, and the group is read before each wave so a count that is not the number of nodes it runs is refused) as the caller, and records the receipt. The proposal is answered as the run left it: running with its operation_id when there is a platform execution to wait on, succeeded when the operation finished inside the call, or failed with the refusal on its receipt. Only an approved proposal runs, and only once: a concurrent execute finds it running. A cost approval older than seven days whose change never started is refused with 409 (error_code approval_expired) and returned to proposed, so it runs only on a fresh approval. Gated on the area's permission: billing.manage for cost, security.manage for security. The token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. A plan marked consent_required (at its top or under plan.parameters) runs only when the body carries consent "no_snapshot_acknowledged" or, for a waste_cleanup, the value naming its finding's kind (consent_by_kind on the waste consent policy); without it the answer is 422 with error_code consent_required and the proposal stays approved. # Set a decision proposal aside Source: https://docs.ankra.ai/api-reference/decisions/set-a-decision-proposal-aside https://platform.ankra.app/openapi.json post /api/v1/org/decisions/{decision_id}/set-aside Records that the caller declined the proposal, with their reason. Allowed from proposed, approved or held (withdraws the approval before anything ran) and failed. Gated on the area's permission: billing.manage for cost, security.manage for security. The token stands in for the browser route's CSRF double-submit. Bearer-PAT twin of the browser route of the same name under /org; the console's session cookie is not accepted here and the browser route answers a token with a login redirect. # Get an application's deploy provenance mode Source: https://docs.ankra.ai/api-reference/deploy-provenance/get-an-applications-deploy-provenance-mode https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/provenance-policy The deploy provenance mode the application's deploys use and where it came from: the application's own choice, the organisation's default, or Ankra's default. Requires pipelines.read. # Get the organisation's deploy provenance default Source: https://docs.ankra.ai/api-reference/deploy-provenance/get-the-organisations-deploy-provenance-default https://platform.ankra.app/openapi.json get /api/v1/org/provenance/policy The deploy provenance mode applications follow when they choose none. Null means the organisation chose no default, so Ankra's applies: off for an application created before verified provenance shipped, warn for one created since. Requires pipelines.read. # List the organisation's provenance signing keys Source: https://docs.ankra.ai/api-reference/deploy-provenance/list-the-organisations-provenance-signing-keys https://platform.ankra.app/openapi.json get /api/v1/org/provenance/keys Every generation of the key the organisation's pipelines sign provenance with, oldest first: the active one signs, trusted ones still verify, revoked ones verify nothing. The public keys verify an attestation by hand with cosign verify-attestation --type predicate_type. Listing never creates a key. Requires pipelines.read. # Revoke a provenance signing key Source: https://docs.ankra.ai/api-reference/deploy-provenance/revoke-a-provenance-signing-key https://platform.ankra.app/openapi.json post /api/v1/org/provenance/keys/revoke Revokes one key generation: nothing it signed verifies from then on, so an enforcing application refuses every image it attested until it is published again. Revoking the active key makes the next attestation create a successor. Revoking a revoked key changes nothing. Recorded in the audit log. Requires security.manage. # Rotate the organisation's provenance signing key Source: https://docs.ankra.ai/api-reference/deploy-provenance/rotate-the-organisations-provenance-signing-key https://platform.ankra.app/openapi.json post /api/v1/org/provenance/keys/rotate Creates the next key generation, which signs every attestation from then on. Older generations keep verifying what they signed until they are revoked, so images published before the rotation still deploy. Recorded in the audit log. Requires security.manage. # Set an application's deploy provenance mode Source: https://docs.ankra.ai/api-reference/deploy-provenance/set-an-applications-deploy-provenance-mode https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/provenance-policy Stores the application's own deploy provenance mode: off, warn (deploy and file a finding for an image without verified provenance) or enforce (refuse such a deploy and pin verified images to their digest). A mode of null clears the choice, so the application follows the organisation's default. Recorded in the audit log. Requires security.manage. # Set the organisation's deploy provenance default Source: https://docs.ankra.ai/api-reference/deploy-provenance/set-the-organisations-deploy-provenance-default https://platform.ankra.app/openapi.json put /api/v1/org/provenance/policy Stores the deploy provenance mode applications follow when they choose none: off, warn or enforce. A mode of null clears the default. Recorded in the audit log. Requires security.manage. # Verify images against an application's provenance policy Source: https://docs.ankra.ai/api-reference/deploy-provenance/verify-images-against-an-applications-provenance-policy https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/provenance/verify A dry-run: checks each image exactly as the application's deploy would - signed provenance from this organisation's pipeline, a trusted key, this application, a gate that passed the digest - and answers what a deploy under the application's mode would do. Nothing is deployed, no finding is filed and no key is created. Images outside the application's registry are reported, never judged. Requires pipelines.read. # Create a host join token Source: https://docs.ankra.ai/api-reference/deploy-targets/create-a-host-join-token https://platform.ankra.app/openapi.json post /api/v1/org/environments/{environment_name}/host-join-tokens Mints a single-use token a host registers with (`ankra targets register`), creating the environment when the organisation has none of that name. The token is in this answer exactly once and only its hash is stored. Requires pipelines.manage. # Get a deployment Source: https://docs.ankra.ai/api-reference/deploy-targets/get-a-deployment https://platform.ankra.app/openapi.json get /api/v1/org/deployments/{deployment_id} One deployment with the outcome on each host target it went to. Requires pipelines.read. # Get a host target Source: https://docs.ankra.ai/api-reference/deploy-targets/get-a-host-target https://platform.ankra.app/openapi.json get /api/v1/org/host-targets/{host_target_id} One host target with its derived status and what the host last reported about itself. Requires pipelines.read. # Get the release signing key Source: https://docs.ankra.ai/api-reference/deploy-targets/get-the-release-signing-key https://platform.ankra.app/openapi.json get /api/v1/org/release-signing-key The public key the organisation's releases are signed with, which a host pins at registration. Answers configured false while the organisation has no key; a key that could not be read is a 500, never that answer. Requires pipelines.read. # List deployments Source: https://docs.ankra.ai/api-reference/deploy-targets/list-deployments https://platform.ankra.app/openapi.json get /api/v1/org/deployments The releases a pipeline deploy stage sent to host targets, newest first by keyset, optionally narrowed to an environment or a pipeline repository. Requires pipelines.read. # List environments Source: https://docs.ankra.ai/api-reference/deploy-targets/list-environments https://platform.ankra.app/openapi.json get /api/v1/org/environments The organisation's deploy environments by name. An environment is created by the first host join token minted for it. Requires pipelines.read. # List host targets Source: https://docs.ankra.ai/api-reference/deploy-targets/list-host-targets https://platform.ankra.app/openapi.json get /api/v1/org/host-targets The organisation's host targets, active ones first and revoked ones after, optionally narrowed to one environment. Requires pipelines.read. # Revoke a host target Source: https://docs.ankra.ai/api-reference/deploy-targets/revoke-a-host-target https://platform.ankra.app/openapi.json post /api/v1/org/host-targets/{host_target_id}/revoke Revokes a host target: its identity token and every session stop working, it is offered no further deploy jobs, and its name is free for a replacement host. Releases already on the host keep running. Revoking a revoked target changes nothing. Requires pipelines.manage. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/vms Adds a standalone VM with the given `name` and `instance_type` on the cluster network; its location defaults to the bastion's. On a running cluster its creation starts at once and the result carries the `operation_id`; on a stopped cluster it is created at the next start. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Add node group Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/node-groups Adds a worker node group with the given `name`, `instance_type` and `count` (default 1), plus optional labels, taints and autoscaling settings. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Create a DigitalOcean cluster Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/create-a-digitalocean-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean Creates a DigitalOcean cluster from the request and starts provisioning it in the background, answering 201 with the new cluster's `cluster_id` and `name`. A configuration that fails validation answers 400 with the reason, and an organisation whose billing is restricted is refused with 402. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/digitalocean/{cluster_id}/vms/{vm_id} Deletes a standalone VM: it is handed to teardown and its server is removed at the provider. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Delete node group Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/digitalocean/{cluster_id}/node-groups/{group_name} Removes a node group by draining and deleting its workers one at a time through their PodDisruptionBudgets; a worker whose drain is refused stays in service and the operation fails with the reason. Pass `force_drain=true` to take the whole group down regardless. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. Requires `clusters.delete`. # Deprovision a DigitalOcean cluster Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/deprovision-a-digitalocean-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/digitalocean/{cluster_id} Deletes the cluster and its cloud resources. A delete that would remove persistent volumes, or cannot tell whether it does, is refused with 409 unless `accept_volume_data_loss=true` is passed (see the deprovision-volumes read); `force=true` tolerates infrastructure that can no longer be reached. Active operations on the cluster are cancelled and the response carries the teardown's `operation_id`. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. # Diagnose node Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `digitalocean_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get bastion health Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. # Get bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/get-bastion-ssh-allowlist https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.read`. Returns the cluster's stored bastion SSH source allowlist, as the update on the same path stored it. `configured` tells a cleared list (true, empty) from one that was never set (false, empty); both leave SSH open from anywhere. # Get current Kubernetes version Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/get-current-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/k8s-version Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Get current worker count Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/get-current-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/worker-count Returns the cluster's current worker count and the minimum and maximum it can be scaled to. # Get disabled K3s components Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/get-disabled-k3s-components https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/k3s-disabled-components Read the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), on top of the provider shape's own defaults. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states (a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider) answer 409 with a detail message. # Latest restorable state snapshot of a DigitalOcean cluster Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/latest-restorable-state-snapshot-of-a-digitalocean-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the DigitalOcean cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List available DigitalOcean droplet sizes Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/list-available-digitalocean-droplet-sizes https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/sizes Lists DigitalOcean droplet sizes for the credential in `credential_id` with vCPUs, memory, disk, monthly price and regions; with `region` set, sizes not offered there are marked unavailable. # List available DigitalOcean regions Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/list-available-digitalocean-regions https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/regions Lists the DigitalOcean regions visible to the DigitalOcean credential given in `credential_id`, with each region's availability and the droplet sizes it offers. # List node groups Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/node-groups Lists the cluster's worker node groups with each group's instance type, node count, autoscaling bounds, labels and taints. # List nodes Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/nodes Lists the cluster's machines with each one's role, node group, instance type, location, state, private and public IPs, the kubelet version it reports and, where recorded, the provider's own status. # List standalone VMs Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/vms Lists the cluster's standalone VMs (servers on the cluster network that are not Kubernetes nodes) with their instance type, location, state, addresses and provider ID. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/control-plane Returns the control-plane count, the counts the cluster supports, the control planes' instance type, and whether a count or instance-type change is possible right now, with the reason when it is not. # Read node Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/nodes/{node_id} Returns one machine of the cluster with its definition, its latest recorded info and data, and its dependencies and relationships. Answers 404 when the node is not part of the cluster. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `digitalocean_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Read private cluster access Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/access-info Returns what you need to reach the cluster's machines over SSH: the bastion's public IP, the control-plane IPs and the cluster name. # Read SSH keys Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/ssh-keys Returns the SSH key credentials authorised on the cluster's servers (`ssh_key_credential_ids`) and the organisation's SSH key credentials that can be attached. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/vms/{vm_id}/instance-type Resizes a standalone VM in place to the given `instance_type`. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/control-plane/instance-type Changes the instance type of every control plane. On a running cluster the control planes are resized one at a time behind an etcd quorum check and a drain, and the response carries the `operation_id` to follow (clusters with fewer than three control planes are refused); on a stopped cluster the new type is applied at the next start and no operation is returned. # Resize node group Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/node-groups/{group_name}/instance-type Changes the instance type of every worker in the node group. Each node is power-cycled, so the workers are drained and resized in bounded batches; `force_drain` bypasses PodDisruptionBudgets for that drain. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/ssh-keys/resync Re-applies the cluster's configured SSH keys to its servers and returns the IDs of the resources queued for the update. # Scale DigitalOcean cluster workers Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/scale-digitalocean-cluster-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/scale-workers Sets the cluster's worker count to `worker_count` and returns the previous and new counts. A scale-down drains the removed workers through their PodDisruptionBudgets unless `force_drain` is true. Requires `clusters.operate`. # Scale node group Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/node-groups/{group_name}/scale Sets the node group's node count to `count`. A scale-down drains the removed workers through their PodDisruptionBudgets; `force_drain` bypasses them for this request. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Start a DigitalOcean cluster Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/start-a-digitalocean-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/start Recreate the DigitalOcean cloud resources for a stopped cluster and reconcile it back to a running state. # Stop a DigitalOcean cluster Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/stop-a-digitalocean-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/stop Initiate async deletion of DigitalOcean cloud resources (droplets, VPCs, firewalls, SSH keys) without removing the cluster from the database. # The persistent volumes deleting a DigitalOcean cluster removes Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/the-persistent-volumes-deleting-a-digitalocean-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/digitalocean/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/digitalocean/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. # Update bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/update-bastion-ssh-allowlist https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.write`. Replaces the cluster's bastion SSH source allowlist and dispatches the bastion update that applies it: the bastion's SSH port then accepts only the listed IPv4 sources, the platform's own egress addresses and private network sources, and drops everything else. An empty list clears the allowlist. Clusters that never set one keep SSH open from anywhere. # Update control-plane count Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/control-plane Changes the number of control planes to `count`. On a running cluster the control plane can only grow, and the new control planes join live (`mode` `rolling`, with an `operation_id` to follow); a reduction is applied offline to a stopped cluster and takes effect at its next start. # Update disabled K3s components Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/update-disabled-k3s-components https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/k3s-disabled-components Replace the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), for example ["traefik"] on a cluster that brings its own ingress. k3s distribution only. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states answer 409 with a detail message: a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider, a list that drops a stored entry (re-enabling a bundled component is not supported yet, so the list only grows), a list that adds coredns (load-bearing for in-cluster DNS), and any change while a node update, version upgrade or node restart is in progress on the cluster. # Update node-group labels Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/node-groups/{group_name}/labels Replaces the Kubernetes labels on every node in the node group with the given `labels` map. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update node-group taints Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/node-groups/{group_name}/taints Replaces the taints on every node in the node group with the given `taints` list (each a `key`, an optional `value` and an `effect` of `NoSchedule`, `PreferNoSchedule` or `NoExecute`). By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update SSH keys Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/digitalocean/{cluster_id}/ssh-keys Replaces the SSH key credentials authorised on the cluster's servers with `ssh_key_credential_ids` and starts rolling the keys out. An unknown or repeated credential, or two credentials carrying the same public key, is refused with 409. # Upgrade Kubernetes version Source: https://docs.ankra.ai/api-reference/digitalocean-clusters/upgrade-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/digitalocean/{cluster_id}/upgrade-k8s-version Upgrades the cluster to `target_version`, which must be in the supported version catalogue and must not downgrade or skip a minor version. The cluster must be online with no change in progress; the nodes are upgraded as a rolling operation whose `operation_id` is returned, and `force` continues past a node drain that PodDisruptionBudgets refuse. # Create a DigitalOcean API credential Source: https://docs.ankra.ai/api-reference/digitalocean-credentials/create-a-digitalocean-api-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/digitalocean Creates a DigitalOcean API credential from `name` and `api_token`. The token is checked against the DigitalOcean API before it is stored in Vault; an invalid token answers 400. Requires `credentials.write`; answers 201. # Create an SSH key credential Source: https://docs.ankra.ai/api-reference/digitalocean-credentials/create-an-ssh-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/digitalocean/ssh-key Creates an SSH key credential from `name` and either `ssh_public_key` or `generate=true`; a generated RSA-4096 key pair's private key is returned once, in this response. A public key the organisation already stores under another credential is refused with 400. Requires `credentials.write`; answers 201. # Get DigitalOcean credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/digitalocean-credentials/get-digitalocean-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/digitalocean/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the DigitalOcean API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get DigitalOcean credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/digitalocean-credentials/get-digitalocean-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/digitalocean/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the DigitalOcean limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # List DigitalOcean API credentials Source: https://docs.ankra.ai/api-reference/digitalocean-credentials/list-digitalocean-api-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/digitalocean Lists the organisation's DigitalOcean API credentials with their metadata; the secret material is never returned. # List SSH key credentials Source: https://docs.ankra.ai/api-reference/digitalocean-credentials/list-ssh-key-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/digitalocean/ssh-keys Lists the organisation's SSH key credentials. The list is the same under every cloud provider. Each item carries the stored public key with the fingerprint (as `ssh-keygen -l` prints it), key type and comment derived from it; a stored value that is not a readable key keeps its row, with a null `fingerprint` and `fingerprint_error` saying why. # Create a DNS credential Source: https://docs.ankra.ai/api-reference/dns-credentials/create-a-dns-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/dns Bearer token authentication; RBAC permission `credentials.write`. Creates the org-scoped credential a cluster's custom DNS zones reference by credential_name. The webhook provider url carries the DNS provider's token, so it is stored in Vault and never echoed. # List DNS credentials Source: https://docs.ankra.ai/api-reference/dns-credentials/list-dns-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/dns Bearer PAT authentication; RBAC permission `credentials.read`. Returns credential metadata only; the webhook provider url is never returned. # Create an organisation DNS record Source: https://docs.ankra.ai/api-reference/dns/create-an-organisation-dns-record https://platform.ankra.app/openapi.json post /api/v1/org/dns/records Adds a CNAME, A, AAAA or TXT record under the organisation's DNS zone, where `name` is the label in front of the zone. An AAAA record takes an IPv6 address and needs the zone on Ankra Cloud. Organisation admins only. The record is published asynchronously and starts in the `pending` state; answers 400 when the zone is not active yet or the record conflicts with an existing one. # Declare a custom DNS zone for every cluster in the organisation Source: https://docs.ankra.ai/api-reference/dns/declare-a-custom-dns-zone-for-every-cluster-in-the-organisation https://platform.ankra.app/openapi.json post /api/v1/org/custom-dns-zones The DNS zones every cluster in the organisation serves with the organisation's own external-dns webhook credential, alongside the delegated zone Ankra serves itself. A zone declared here behaves like the generated domain: each cluster - the ones that exist and the ones created later - gets its own external-dns deployment for it, pinned to that zone with its own cluster-derived txt owner id. A cluster's own declaration of the same zone (the per-cluster custom-dns-zones route) takes precedence over the organisation-wide one on that cluster. A zone that overlaps the zone Ankra already serves for the organisation - its delegated zone on the shared platform root, or the whole domain when a custom Ankra domain is registered - is refused with 400. # Delete an organisation DNS record Source: https://docs.ankra.ai/api-reference/dns/delete-an-organisation-dns-record https://platform.ankra.app/openapi.json delete /api/v1/org/dns/records/{record_id} Marks the record for deletion and answers 204; it is removed from DNS asynchronously. Organisation admins only. # Get the organisation's DNS zone Source: https://docs.ankra.ai/api-reference/dns/get-the-organisations-dns-zone https://platform.ankra.app/openapi.json get /api/v1/org/dns/zone Returns the organisation's own delegated DNS zone, its FQDN and provisioning state; the state is `none` when the organisation has no zone yet. Record names created through this API hang off this zone. # List Cloud DNS zones in the authenticated organisation Source: https://docs.ankra.ai/api-reference/dns/list-cloud-dns-zones-in-the-authenticated-organisation https://platform.ankra.app/openapi.json get /api/v1/org/cloud-dns/zones Lists zones in the Cloud account linked to the authenticated Platform organisation. Requires members.read. # List organisation DNS records Source: https://docs.ankra.ai/api-reference/dns/list-organisation-dns-records https://platform.ankra.app/openapi.json get /api/v1/org/dns/records Lists the CNAME, A, AAAA and TXT records the organisation manages under its own DNS zone, with each record's publishing state. Any organisation member can read them. # List the organisation's cluster DNS zones Source: https://docs.ankra.ai/api-reference/dns/list-the-organisations-cluster-dns-zones https://platform.ankra.app/openapi.json get /api/v1/org/dns/cluster-zones List every cluster-level DNS domain in the organisation with its reconciliation state - the inventory an administrator clears before changing the organisation's dns_root_domain. # List the organisation's custom DNS zones Source: https://docs.ankra.ai/api-reference/dns/list-the-organisations-custom-dns-zones https://platform.ankra.app/openapi.json get /api/v1/org/custom-dns-zones The DNS zones every cluster in the organisation serves with the organisation's own external-dns webhook credential, alongside the delegated zone Ankra serves itself. A zone declared here behaves like the generated domain: each cluster - the ones that exist and the ones created later - gets its own external-dns deployment for it, pinned to that zone with its own cluster-derived txt owner id. A cluster's own declaration of the same zone (the per-cluster custom-dns-zones route) takes precedence over the organisation-wide one on that cluster. # Read a proxied zone access policy Source: https://docs.ankra.ai/api-reference/dns/read-a-proxied-zone-access-policy https://platform.ankra.app/openapi.json get /api/v1/org/cloud-dns/zones/{zone_id}/access Reads the effective policy for a zone owned by the authenticated organisation's Cloud account. Requires members.read. # Set a proxied zone access policy Source: https://docs.ankra.ai/api-reference/dns/set-a-proxied-zone-access-policy https://platform.ankra.app/openapi.json put /api/v1/org/cloud-dns/zones/{zone_id}/access Sets public or organisation-only access for a Cloud proxied zone. Optional roles restrict access to matching active members. Requires members.manage; Cloud checks account ownership and DNS bypass rules. # Update an organisation DNS record Source: https://docs.ankra.ai/api-reference/dns/update-an-organisation-dns-record https://platform.ankra.app/openapi.json patch /api/v1/org/dns/records/{record_id} Changes a record's `content` and, when given, its `ttl`; the name and record type cannot change. Organisation admins only. The record returns to `pending` until the change is published. # Verify the organisation's custom DNS zones Source: https://docs.ankra.ai/api-reference/dns/verify-the-organisations-custom-dns-zones https://platform.ankra.app/openapi.json get /api/v1/org/custom-dns-zones/verification Probes what public DNS answers today for every custom DNS zone the organisation declares - organisation-wide and bound to single clusters - and reports an honest three-state verdict per zone: verified, misconfigured with the observed-vs-expected facts, or unknown when a probe could not be completed. Read-only end to end: it verifies what the customer's DNS resolves and names the record they would need to create themselves; it never writes DNS records anywhere. # Withdraw an organisation-wide custom DNS zone Source: https://docs.ankra.ai/api-reference/dns/withdraw-an-organisation-wide-custom-dns-zone https://platform.ankra.app/openapi.json delete /api/v1/org/custom-dns-zones/{zone} Withdraws the organisation-wide declaration. The scheduler's next pass republishes every affected cluster's custom-dns stack without it, which removes those external-dns deployments; clusters that declared the zone themselves keep it, and the zone and the records already published into it are untouched. # Cancel a batch of executions Source: https://docs.ankra.ai/api-reference/executions/cancel-a-batch-of-executions https://platform.ankra.app/openapi.json post /api/v1/org/executions/cancel Cancels each execution in `execution_ids` that is still running, and reports which ids were cancelled, which were not found in the organisation and which were not running. # Cancel a step within an execution Source: https://docs.ankra.ai/api-reference/executions/cancel-a-step-within-an-execution https://platform.ankra.app/openapi.json post /api/v1/org/executions/{execution_id}/steps/{step_id}/cancel Cancels one pending, blocked or running step of an execution; the step moves to `cancelling`. Answers 409 when the step is in any other state. # Cancel an execution Source: https://docs.ankra.ai/api-reference/executions/cancel-an-execution https://platform.ankra.app/openapi.json post /api/v1/org/executions/{execution_id}/cancel Cancels the execution if it is still running and returns its resulting status; an execution that already finished is left unchanged. Answers 404 when the execution does not belong to the organisation. # Get execution detail Source: https://docs.ankra.ai/api-reference/executions/get-execution-detail https://platform.ankra.app/openapi.json get /api/v1/org/executions/{execution_id} Returns one execution with its steps and their status. Answers 404 when the execution does not belong to the organisation. # Get execution status events Source: https://docs.ankra.ai/api-reference/executions/get-execution-status-events https://platform.ankra.app/openapi.json get /api/v1/org/executions/{execution_id}/events Returns the execution's step status changes in chronological order, each with the step id, status and time. # Get execution step results Source: https://docs.ankra.ai/api-reference/executions/get-execution-step-results https://platform.ankra.app/openapi.json get /api/v1/org/executions/{execution_id}/result Returns the result payload recorded by each of the execution's steps, oldest first. # List execution steps Source: https://docs.ankra.ai/api-reference/executions/list-execution-steps https://platform.ankra.app/openapi.json get /api/v1/org/executions/{execution_id}/steps Returns the execution's steps as a list, each with its status. Answers 404 when the execution does not belong to the organisation. # List executions Source: https://docs.ankra.ai/api-reference/executions/list-executions https://platform.ankra.app/openapi.json get /api/v1/org/executions Returns a page of the organisation's executions, newest first, filterable by cluster, scope, status, target resource and attention state. Internal executions and silent reconciles are left out unless `include_internal_executions` or `include_silent_reconciles` is set; an unknown `cluster_id` answers 404. # Retry a terminal execution Source: https://docs.ankra.ai/api-reference/executions/retry-a-terminal-execution https://platform.ankra.app/openapi.json post /api/v1/org/executions/{execution_id}/retry Starts a new execution that repeats a failed, cancelled or critical execution's steps, and returns the new execution. Answers 409 when the execution is in any other state, when none of its steps has a target resource left to act on, when one of its steps is still running or cancelling, or while any execution in its retry tree (the execution it was retried from, every retry of that one, and their retries) is still live: one retry tree has at most one live execution. # Create a Helm registry Source: https://docs.ankra.ai/api-reference/helm/create-a-helm-registry https://platform.ankra.app/openapi.json post /api/v1/org/helm/registries Adds an HTTP or OCI Helm registry to the organisation, optionally authenticated with an existing registry credential. Naming a credential the organisation holds also requires credentials.reveal, because the registry is then reached with that credential at the URL the caller chose; without it the create answers 403 "Using a stored credential for a registry needs permission to reveal credentials." Validation problems (duplicate name or URL, unknown credential) come back in the `errors` list with a 200 status. # Create a registry credential Source: https://docs.ankra.ai/api-reference/helm/create-a-registry-credential https://platform.ankra.app/openapi.json post /api/v1/org/helm/credentials Stores a username and password as a named credential that a Helm registry can reference by `credential_name`. Validation problems, such as a duplicate name, come back as `success: false` with an `errors` list rather than an error status. # Delete a Helm registry Source: https://docs.ankra.ai/api-reference/helm/delete-a-helm-registry https://platform.ankra.app/openapi.json delete /api/v1/org/helm/registries/{registry_name} Marks the registry and the charts indexed from it for deletion and returns `marked_for_deletion_at`. # Delete a registry credential Source: https://docs.ankra.ai/api-reference/helm/delete-a-registry-credential https://platform.ankra.app/openapi.json delete /api/v1/org/helm/credentials/{credential_name} Deletes the named registry credential. Credentials the platform manages for the organisation cannot be deleted and answer 409. # Get a file from a Helm chart version Source: https://docs.ankra.ai/api-reference/helm/get-a-file-from-a-helm-chart-version https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts/chart/{repository_name}/{chart_name}/{chart_version}/file Returns one file from the chart version's archive, base64-encoded, selected by the required `path` query parameter. Answers 404 when the version has not been indexed and 400 for an invalid path. # Get a Helm chart version Source: https://docs.ankra.ai/api-reference/helm/get-a-helm-chart-version https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts/chart/{repository_name}/{chart_name}/{chart_version} Returns the details of one chart version. Set `values=true` or `readme=true` to also receive the chart's default values or README, base64-encoded. # Get a Helm registry Source: https://docs.ankra.ai/api-reference/helm/get-a-helm-registry https://platform.ankra.app/openapi.json get /api/v1/org/helm/registries/{registry_name} Returns the registry with a page of its indexed charts, its indexing state, next scheduled sync, recent sync jobs and last sync error. Answers 404 for an unknown registry. # Get a registry credential Source: https://docs.ankra.ai/api-reference/helm/get-a-registry-credential https://platform.ankra.app/openapi.json get /api/v1/org/helm/credentials/{credential_name} Returns the credential's name, username and provider; the password is never returned. Requires the `credentials.reveal` permission. # Get Helm chart default values Source: https://docs.ankra.ai/api-reference/helm/get-helm-chart-default-values https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts/chart/{repository_name}/{chart_name}/{chart_version}/values Returns the chart version's default values file, base64-encoded in `values_base64`: the equivalent of `helm show values`. # List clusters using a Helm chart Source: https://docs.ankra.ai/api-reference/helm/list-clusters-using-a-helm-chart https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts/clusters/{repository_name}/{chart_name} Lists the organisation's clusters that run the chart as an add-on, with the installed version and owning stack for each, paginated. The response also carries the chart's latest indexed version. # List files in a Helm chart version Source: https://docs.ankra.ai/api-reference/helm/list-files-in-a-helm-chart-version https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts/chart/{repository_name}/{chart_name}/{chart_version}/files Lists the files contained in the chart version's archive. Answers 404 when the version has not been indexed and 413 when the archive is too large to open. # List Helm chart releases Source: https://docs.ankra.ai/api-reference/helm/list-helm-chart-releases https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts/releases/{repository_name}/{chart_name} Lists the chart's upstream release notes (version, notes, authors, link), newest first, paginated. # List Helm charts Source: https://docs.ankra.ai/api-reference/helm/list-helm-charts https://platform.ankra.app/openapi.json get /api/v1/org/helm/charts Lists the Helm chart versions indexed from the registries available to the organisation, paginated, with optional `search` and `connected` filters. Each chart carries the catalogue add-on it matches, when there is one. # List Helm registries Source: https://docs.ankra.ai/api-reference/helm/list-helm-registries https://platform.ankra.app/openapi.json get /api/v1/org/helm/registries Lists the Helm registries available to the organisation, paginated, with search and sorting. # List registry credentials Source: https://docs.ankra.ai/api-reference/helm/list-registry-credentials https://platform.ankra.app/openapi.json get /api/v1/org/helm/credentials Lists the organisation's Helm registry credentials. Usernames and passwords are never returned. # List registry sync jobs Source: https://docs.ankra.ai/api-reference/helm/list-registry-sync-jobs https://platform.ankra.app/openapi.json get /api/v1/org/helm/registries/{registry_name}/sync-jobs Lists the registry's sync jobs, paginated, with filters for status, job type and search. Answers 404 for an unknown registry. # Render a chart version's manifests with helm template Source: https://docs.ankra.ai/api-reference/helm/render-a-chart-versions-manifests-with-helm-template https://platform.ankra.app/openapi.json post /api/v1/org/helm/charts/template Renders a chart version's manifests server-side, like `helm template`, with optional base64-encoded values, release name and namespace; no cluster is contacted. A values problem answers 422 with Helm's error text. # Sync a Helm registry Source: https://docs.ankra.ai/api-reference/helm/sync-a-helm-registry https://platform.ankra.app/openapi.json post /api/v1/org/helm/registries/{registry_name}/sync Queues a re-index of the registry and returns the number of jobs created. Answers 409 when a sync is already running and 400 when the registry is not in a state that can be synced. # Update a Helm registry Source: https://docs.ankra.ai/api-reference/helm/update-a-helm-registry https://platform.ankra.app/openapi.json put /api/v1/org/helm/registries/{registry_name} Changes the registry's `read_job_interval`, the only setting that can be updated. # Update a registry credential Source: https://docs.ankra.ai/api-reference/helm/update-a-registry-credential https://platform.ankra.app/openapi.json put /api/v1/org/helm/credentials/{credential_name} Replaces the credential's username and password. Credentials the platform manages for the organisation cannot be changed and answer 409. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/hetzner-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/vms Adds a standalone VM with the given `name` and `instance_type` on the cluster network; its location defaults to the bastion's. On a running cluster its creation starts at once and the result carries the `operation_id`; on a stopped cluster it is created at the next start. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Add node group Source: https://docs.ankra.ai/api-reference/hetzner-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/node-groups Adds a worker node group with the given `name`, `instance_type` and `count` (default 1), plus optional labels, taints and autoscaling settings. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/hetzner-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Converge Hetzner external cloud provider Source: https://docs.ankra.ai/api-reference/hetzner-clusters/converge-hetzner-external-cloud-provider https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/external-cloud-provider/converge Store external_cloud_provider=true on every Kubernetes node definition of a Hetzner k3s cluster whose definitions say false while its nodes already run for the hcloud cloud controller manager (a cluster created by the Python lane). The flag is an instruction to the day-2 lanes, so it is only written when EVERY node, read from the platform's node census (the k8s-sync mirror, nodes seen in the last 30 minutes), already carries the external shape: the kubelet argument cloud-provider=external in its k3s.io/node-args annotation, on a k3s server also --disable-cloud-controller and servicelb disabled, and an hcloud:// providerID. Only the stored definitions change: no node is restarted, re-tainted or re-registered and no job is dispatched. Anything else answers 409 with a detail message naming each node to remediate first and why; a node the census cannot vouch for (no fresh node at all, a node missing from the census, a node without a readable k3s.io/node-args annotation, a Kubernetes node resource that is not up or has no stored definition) is refused the same way rather than assumed wired. The other invalid states (a cluster of another provider or distribution, no Kubernetes node resources, a node update, version upgrade or node restart in progress) also answer 409. A cluster whose definitions already all say true answers 200 without reading the census. # Create a Hetzner cluster Source: https://docs.ankra.ai/api-reference/hetzner-clusters/create-a-hetzner-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner Creates a Hetzner cluster from the request, after checking server-type availability in the chosen location with Hetzner, and starts provisioning it in the background, answering 201 with the new cluster's `cluster_id` and `name`. A configuration that fails validation answers 400 with the reason, and an organisation whose billing is restricted is refused with 402. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/hetzner-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/hetzner/{cluster_id}/vms/{vm_id} Deletes a standalone VM: it is handed to teardown and its server is removed at the provider. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Delete node group Source: https://docs.ankra.ai/api-reference/hetzner-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/hetzner/{cluster_id}/node-groups/{group_name} Removes a node group by draining and deleting its workers one at a time through their PodDisruptionBudgets; a worker whose drain is refused stays in service and the operation fails with the reason. Pass `force_drain=true` to take the whole group down regardless. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. Requires `clusters.delete`. # Deprovision a Hetzner cluster Source: https://docs.ankra.ai/api-reference/hetzner-clusters/deprovision-a-hetzner-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/hetzner/{cluster_id} Deletes the cluster and its cloud resources. A delete that would remove persistent volumes, or cannot tell whether it does, is refused with 409 unless `accept_volume_data_loss=true` is passed (see the deprovision-volumes read); `force=true` tolerates infrastructure that can no longer be reached. Active operations on the cluster are cancelled and the response carries the teardown's `operation_id`. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/hetzner-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. # Diagnose node Source: https://docs.ankra.ai/api-reference/hetzner-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `hetzner_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get bastion health Source: https://docs.ankra.ai/api-reference/hetzner-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. # Get bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/hetzner-clusters/get-bastion-ssh-allowlist https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.read`. Returns the cluster's stored bastion SSH source allowlist, as the update on the same path stored it. `configured` tells a cleared list (true, empty) from one that was never set (false, empty); both leave SSH open from anywhere. # Get current Kubernetes version Source: https://docs.ankra.ai/api-reference/hetzner-clusters/get-current-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/k8s-version Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Get current worker count Source: https://docs.ankra.ai/api-reference/hetzner-clusters/get-current-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/worker-count Returns the cluster's current worker count and the minimum and maximum it can be scaled to. # Get disabled K3s components Source: https://docs.ankra.ai/api-reference/hetzner-clusters/get-disabled-k3s-components https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/k3s-disabled-components Read the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), on top of the provider shape's own defaults. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states (a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider) answer 409 with a detail message. # Latest restorable state snapshot of a Hetzner cluster Source: https://docs.ankra.ai/api-reference/hetzner-clusters/latest-restorable-state-snapshot-of-a-hetzner-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the Hetzner cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List available Hetzner locations Source: https://docs.ankra.ai/api-reference/hetzner-clusters/list-available-hetzner-locations https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/locations Lists the Hetzner Cloud locations visible to the Hetzner credential given in `credential_id`. # List available Hetzner server types Source: https://docs.ankra.ai/api-reference/hetzner-clusters/list-available-hetzner-server-types https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/server-types Lists Hetzner Cloud server types for the credential in `credential_id` with cores, memory, disk, architecture, CPU type and monthly price (null when Hetzner lists none). `available` says whether the type can be ordered in the optional `location`, or in any location when none is given; deprecated types are never available. # List node groups Source: https://docs.ankra.ai/api-reference/hetzner-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/node-groups Lists the cluster's worker node groups with each group's instance type, node count, autoscaling bounds, labels and taints. # List nodes Source: https://docs.ankra.ai/api-reference/hetzner-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/nodes Lists the cluster's machines with each one's role, node group, instance type, location, state, private and public IPs, the kubelet version it reports and, where recorded, the provider's own status. # List standalone VMs Source: https://docs.ankra.ai/api-reference/hetzner-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/vms Lists the cluster's standalone VMs (servers on the cluster network that are not Kubernetes nodes) with their instance type, location, state, addresses and provider ID. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/hetzner-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/control-plane Returns the control-plane count, the counts the cluster supports, the control planes' instance type, and whether a count or instance-type change is possible right now, with the reason when it is not. # Read node Source: https://docs.ankra.ai/api-reference/hetzner-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/nodes/{node_id} Returns one machine of the cluster with its definition, its latest recorded info and data, and its dependencies and relationships. Answers 404 when the node is not part of the cluster. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/hetzner-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `hetzner_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Read private cluster access Source: https://docs.ankra.ai/api-reference/hetzner-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/access-info Returns what you need to reach the cluster's machines over SSH: the bastion's public IP, the control-plane IPs and the cluster name. # Read SSH keys Source: https://docs.ankra.ai/api-reference/hetzner-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/ssh-keys Returns the SSH key credentials authorised on the cluster's servers (`ssh_key_credential_ids`) and the organisation's SSH key credentials that can be attached. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/hetzner-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/vms/{vm_id}/instance-type Resizes a standalone VM in place to the given `instance_type`. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/hetzner-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/control-plane/instance-type Changes the instance type of every control plane. On a running cluster the control planes are resized one at a time behind an etcd quorum check and a drain, and the response carries the `operation_id` to follow (clusters with fewer than three control planes are refused); on a stopped cluster the new type is applied at the next start and no operation is returned. # Resize node group Source: https://docs.ankra.ai/api-reference/hetzner-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/node-groups/{group_name}/instance-type Changes the instance type of every worker in the node group. Each node is power-cycled, so the workers are drained and resized in bounded batches; `force_drain` bypasses PodDisruptionBudgets for that drain. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/hetzner-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/ssh-keys/resync Re-applies the cluster's configured SSH keys to its servers and returns the IDs of the resources queued for the update. # Scale Hetzner cluster workers Source: https://docs.ankra.ai/api-reference/hetzner-clusters/scale-hetzner-cluster-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/scale-workers Sets the cluster's worker count to `worker_count` and returns the previous and new counts. A scale-down drains the removed workers through their PodDisruptionBudgets unless `force_drain` is true. Requires `clusters.operate`. # Scale node group Source: https://docs.ankra.ai/api-reference/hetzner-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/node-groups/{group_name}/scale Sets the node group's node count to `count`. A scale-down drains the removed workers through their PodDisruptionBudgets; `force_drain` bypasses them for this request. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Start a Hetzner cluster Source: https://docs.ankra.ai/api-reference/hetzner-clusters/start-a-hetzner-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/start Recreate the Hetzner cloud resources for a stopped cluster and reconcile it back to a running state. # Stop a Hetzner cluster Source: https://docs.ankra.ai/api-reference/hetzner-clusters/stop-a-hetzner-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/stop Initiate async deletion of Hetzner cloud resources (servers, networks, SSH keys) without removing the cluster from the database. # The persistent volumes deleting a Hetzner cluster removes Source: https://docs.ankra.ai/api-reference/hetzner-clusters/the-persistent-volumes-deleting-a-hetzner-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/hetzner/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/hetzner/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. # Update bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/hetzner-clusters/update-bastion-ssh-allowlist https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.write`. Replaces the cluster's bastion SSH source allowlist and dispatches the bastion update that applies it: the bastion's SSH port then accepts only the listed IPv4 sources, the platform's own egress addresses and private network sources, and drops everything else. An empty list clears the allowlist. Clusters that never set one keep SSH open from anywhere. # Update control-plane count Source: https://docs.ankra.ai/api-reference/hetzner-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/control-plane Changes the number of control planes to `count`. On a running cluster the control plane can only grow, and the new control planes join live (`mode` `rolling`, with an `operation_id` to follow); a reduction is applied offline to a stopped cluster and takes effect at its next start. # Update disabled K3s components Source: https://docs.ankra.ai/api-reference/hetzner-clusters/update-disabled-k3s-components https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/k3s-disabled-components Replace the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), for example ["traefik"] on a cluster that brings its own ingress. k3s distribution only. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states answer 409 with a detail message: a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider, a list that drops a stored entry (re-enabling a bundled component is not supported yet, so the list only grows), a list that adds coredns (load-bearing for in-cluster DNS), and any change while a node update, version upgrade or node restart is in progress on the cluster. # Update node-group labels Source: https://docs.ankra.ai/api-reference/hetzner-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/node-groups/{group_name}/labels Replaces the Kubernetes labels on every node in the node group with the given `labels` map. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update node-group taints Source: https://docs.ankra.ai/api-reference/hetzner-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/node-groups/{group_name}/taints Replaces the taints on every node in the node group with the given `taints` list (each a `key`, an optional `value` and an `effect` of `NoSchedule`, `PreferNoSchedule` or `NoExecute`). By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update SSH keys Source: https://docs.ankra.ai/api-reference/hetzner-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/hetzner/{cluster_id}/ssh-keys Replaces the SSH key credentials authorised on the cluster's servers with `ssh_key_credential_ids` and starts rolling the keys out. An unknown or repeated credential, or two credentials carrying the same public key, is refused with 409. # Upgrade Kubernetes version Source: https://docs.ankra.ai/api-reference/hetzner-clusters/upgrade-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/hetzner/{cluster_id}/upgrade-k8s-version Upgrades the cluster to `target_version`, which must be in the supported version catalogue and must not downgrade or skip a minor version. The cluster must be online with no change in progress; the nodes are upgraded as a rolling operation whose `operation_id` is returned, and `force` continues past a node drain that PodDisruptionBudgets refuse. # Create a Hetzner API credential Source: https://docs.ankra.ai/api-reference/hetzner-credentials/create-a-hetzner-api-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/hetzner Creates a Hetzner API credential from `name` and `api_token`. The token is checked against the Hetzner API before it is stored in Vault; an invalid token answers 400. Requires `credentials.write`; answers 201. # Create an SSH key credential Source: https://docs.ankra.ai/api-reference/hetzner-credentials/create-an-ssh-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/hetzner/ssh-key Creates an SSH key credential from `name` and either `ssh_public_key` or `generate=true`; a generated RSA-4096 key pair's private key is returned once, in this response. A public key the organisation already stores under another credential is refused with 400. Requires `credentials.write`; answers 201. # Get Hetzner credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/hetzner-credentials/get-hetzner-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/hetzner/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the Hetzner API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get Hetzner credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/hetzner-credentials/get-hetzner-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/hetzner/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the Hetzner limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # List Hetzner API credentials Source: https://docs.ankra.ai/api-reference/hetzner-credentials/list-hetzner-api-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/hetzner Lists the organisation's Hetzner API credentials with their metadata; the secret material is never returned. # List SSH key credentials Source: https://docs.ankra.ai/api-reference/hetzner-credentials/list-ssh-key-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/hetzner/ssh-keys Lists the organisation's SSH key credentials. The list is the same under every cloud provider. Each item carries the stored public key with the fingerprint (as `ssh-keygen -l` prints it), key type and comment derived from it; a stored value that is not a readable key keeps its row, with a null `fingerprint` and `fingerprint_error` saying why. # Get HPE Morpheus credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/hpe-morpheus-credentials/get-hpe-morpheus-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/morpheus/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the HPE Morpheus API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get HPE Morpheus credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/hpe-morpheus-credentials/get-hpe-morpheus-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/morpheus/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the HPE Morpheus limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Adopt a stack into a stack profile Source: https://docs.ankra.ai/api-reference/imported-clusters/adopt-a-stack-into-a-stack-profile https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks/adopt-profile Records a stack the cluster already runs as a deployment of a stack profile version, so later profile rollouts can track it. Only the profile link is written: nothing is deployed or changed on the cluster. Requires `stacks.write`; the response lists the add-ons and manifests that differ between the live stack and the profile version. # Change an add-on's chart version by rewriting it in its owning stack Source: https://docs.ankra.ai/api-reference/imported-clusters/change-an-add-ons-chart-version-by-rewriting-it-in-its-owning-stack https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_name}/upgrade Moves one installed add-on to the given chart version. The owning stack is read as the specification that reproduces it, the member's chart_version is changed, and only that member is written back, so nothing else in the stack is touched. A lower version is a downgrade and is refused with CHART_VERSION_DOWNGRADE unless confirmed_chart_downgrades names that exact move. Answers the stack write payload (operation_id to watch). 404 when the cluster or add-on is unknown, 409 when no Ankra stack owns the add-on, it already runs that version, or its chart version kept changing while the upgrade was being written (read the add-on again and retry). # Clone a stack to this cluster Source: https://docs.ankra.ai/api-reference/imported-clusters/clone-a-stack-to-this-cluster https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks/clone Copies a stack from a source cluster onto this cluster, optionally under a new name, optionally including its add-on configurations, and deploys it when `deploy_after_clone` is set. Requires `stacks.read` on the source and `stacks.write` resolved in the destination organisation on the destination cluster. Source organisation permissions do not grant destination access. Project-pinned and service-account tokens cannot clone across organisations; token permission scopes still apply. The destination write gate follows body validation so it can resolve the target organisation; cloning with `include_data` also copies the stack's data through a restore point and additionally requires `backups.operate`. # Create a cluster power schedule Source: https://docs.ankra.ai/api-reference/imported-clusters/create-a-cluster-power-schedule https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/power-schedules The bearer twin of the portal's power-schedule create (`ankra cluster power-schedules create`). Files a one-off (`schedule_kind` once, fires at `run_at`) or repeated (`schedule_kind` cron, fires per `cron_expression` in `timezone`) stop or start of a provider-managed cluster. The body is decoded and validated exactly like the portal route's, and the response is the cluster's refreshed schedule listing. # Create a cluster stack Source: https://docs.ankra.ai/api-reference/imported-clusters/create-a-cluster-stack https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks Creates a stack on the cluster from a specification holding exactly one stack, deploys it, and commits it to the cluster's GitOps repository when one is connected. The response carries validation errors and warnings, the commit, and the `operation_id` of the deployment; a failed Git push answers `422`. On a sandbox cluster the deployment is simulated and settled before the response, so the operation has already succeeded. Requires `stacks.write` on the cluster; a caller without it is refused with `403`. # Create a stack from a stack profile Source: https://docs.ankra.ai/api-reference/imported-clusters/create-a-stack-from-a-stack-profile https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks/from-profile Instantiates a stack profile version on the cluster with the parameter values you supply, creating a stack draft and deploying it when `deploy` is set. Requires `stacks.write`; missing or invalid parameters, unresolvable variables and draft errors answer `400`, and a deprecated version answers `409`. # Delete a cluster power schedule Source: https://docs.ankra.ai/api-reference/imported-clusters/delete-a-cluster-power-schedule https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/imported/{cluster_id}/power-schedules/{schedule_id} The bearer twin of the portal's power-schedule delete (`ankra cluster power-schedules delete`). Soft-deletes one schedule. # Delete a cluster stack Source: https://docs.ankra.ai/api-reference/imported-clusters/delete-a-cluster-stack https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name} Marks the stack for deletion so its resources are removed from the cluster, and commits the change to the cluster's GitOps repository when one is connected. Returns when the stack was marked and the commit; on a sandbox cluster the removal is simulated and settled before the response. A stack a managed service owns is refused with `409` until the service is retired. Requires `stacks.delete` on the cluster; a caller without it is refused with `403`. # Discard a stack draft Source: https://docs.ankra.ai/api-reference/imported-clusters/discard-a-stack-draft https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/imported/{cluster_id}/drafts/stacks/{draft_id} Discards one stack draft on the cluster without deploying it; the deployed stack, if any, is left untouched. A draft an Ankra AI chat is still bound to, or one the AI wrote moments ago, is protected: the answer is `200` with `already_discarded` true and `reason` `protected`, and nothing is written, unless `force` is true. Discarding a draft that is already gone answers `200` with `already_discarded` true and the original reason. Answers `404` with code `DRAFT_UNKNOWN` for a draft that never existed on the cluster, and `409` for the installation draft of a managed service, which is removed by retiring the service. # Disconnect a manifest from a stack Source: https://docs.ankra.ai/api-reference/imported-clusters/disconnect-a-manifest-from-a-stack https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/manifests/{manifest_name}/disconnect Disconnects (removes) a manifest from a stack. Mirrors the web UI disconnect endpoint but is exposed on the bearer-token CLI API so `ankra cluster manifests delete` can remove a manifest's resources from the cluster. Requires `stacks.delete` on the cluster; a caller without it is refused with `403`. # Export the cluster specification Source: https://docs.ankra.ai/api-reference/imported-clusters/export-the-cluster-specification https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/iac Returns the full ImportCluster YAML (base64-encoded) for a cluster. Mirrors the web UI endpoint but uses bearer-token auth for CLI access. Returns 404 when the cluster has no resources to generate IaC from. # Generate a cluster agent token Source: https://docs.ankra.ai/api-reference/imported-clusters/generate-a-cluster-agent-token https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/cluster-agent/token Mints a new identity token for the cluster agent, registering the agent first if it has none, and returns it with the Helm install command. Requires `agents.manage`; sandbox clusters answer `400`. # Get add-on settings Source: https://docs.ankra.ai/api-reference/imported-clusters/get-add-on-settings https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_name}/settings Returns the add-on's deployment settings (sync policy, retries, sync windows and similar), with defaults filled in, plus the predefined settings profiles you can apply. # Get add-on values Source: https://docs.ankra.ai/api-reference/imported-clusters/get-add-on-values https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_name}/configuration Returns the addon's DB-stored values (plaintext, base64-encoded). Unlike the web UI v2 endpoint this never queries the cluster agent or Argo CD; the CLI only needs the values for Helm-style --set mutation. # Get manifest content Source: https://docs.ankra.ai/api-reference/imported-clusters/get-manifest-content https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/manifests/{manifest_name}/configuration Returns the manifest's base64-encoded content. Used by `ankra cluster manifests upgrade` to preserve manifest_base64 when the user supplies only metadata changes (e.g. --namespace). # Get stack history Source: https://docs.ankra.ai/api-reference/imported-clusters/get-stack-history https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/history Returns the version history of each add-on and manifest in the stack, up to `max_versions` entries per resource (1 to 100, default 50). Filter to one kind with `resource_type` (`addon` or `manifest`). # Get the cluster agent Source: https://docs.ankra.ai/api-reference/imported-clusters/get-the-cluster-agent https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/agent Returns the cluster agent's version, whether it is online, when it last checked in, and its upgrade state, including whether automatic upgrades are turned off. Answers `404` when no agent is registered. # Get the cluster agent token Source: https://docs.ankra.ai/api-reference/imported-clusters/get-the-cluster-agent-token https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/cluster-agent/token Returns the cluster agent's identity token together with the Helm install command that uses it. Answers `404` when no token exists for the cluster. # Get the cluster metrics source Source: https://docs.ankra.ai/api-reference/imported-clusters/get-the-cluster-metrics-source https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/metrics-source Returns the cluster's registered metrics source: provider, endpoint, state, flavor, health, the credential name it resolves to (back-filled from the cluster's only prometheus_credential when the source never named one), and that credential's name, state and username. Mirrors the web UI endpoint but uses bearer-token auth for CLI access. The credential's password and token are never returned. Returns 404 when the cluster has no metrics source; 400 with the detail "Unknown metrics provider, please check the metrics source configuration." when the stored source resolves to no provider (today: a definition without an endpoint); and 502 with a detail naming the credential when the named credential or its secret cannot be resolved. # List available add-ons Source: https://docs.ankra.ai/api-reference/imported-clusters/list-available-add-ons https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/addons/available Returns the Helm charts available to your organisation for installing as add-ons, each with its latest version and description. Answers `404` when the cluster is not in your organisation. # List cluster stacks Source: https://docs.ankra.ai/api-reference/imported-clusters/list-cluster-stacks https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks Returns a paginated list of the stacks on the cluster, optionally filtered by `search`. `page_size` accepts 1 to 100 and defaults to 25; answers `404` when the cluster is not in your organisation. # Move resources between stacks Source: https://docs.ankra.ai/api-reference/imported-clusters/move-resources-between-stacks https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/move Moves manifests/addons from one stack to another on the same cluster. Mirrors the web UI move endpoint but is exposed on the bearer-token CLI API so `ankra cluster stacks move` can re-home deployed resources without uninstalling them. # Read a shared stack draft Source: https://docs.ankra.ai/api-reference/imported-clusters/read-a-shared-stack-draft https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/drafts/stacks/{draft_id} Requires stack read access to this organisation and cluster. Draft author evidence is additive and reads never backfill attribution. # Read a stack draft Source: https://docs.ankra.ai/api-reference/imported-clusters/read-a-stack-draft https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/drafts/stacks/{draft_id} Returns one stack draft on the cluster with the full content of the members it holds, including the manifest bodies and add-on values the stack listing elides. Use it to review a draft, or to promote the draft of an already-deployed stack through `PATCH /stacks/{stack_name}` with its `draft_id`. Answers `404` with code `DRAFT_UNKNOWN` for a draft that never existed on the cluster, and `410` with code `DRAFT_TOMBSTONED` for one that was discarded or deployed. # Read shared stack draft changes Source: https://docs.ankra.ai/api-reference/imported-clusters/read-shared-stack-draft-changes https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/drafts/stacks/{draft_id}/diff Requires stack read access to this organisation and cluster. Draft author evidence is additive and reads never backfill attribution. # Reconcile a cluster Source: https://docs.ankra.ai/api-reference/imported-clusters/reconcile-a-cluster https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/reconcile Plans and starts the work needed to bring the cluster back in line with its stored specification. Answers `404` when the cluster is not in your organisation. Requires `clusters.operate`. # Remove one stack variable Source: https://docs.ankra.ai/api-reference/imported-clusters/remove-one-stack-variable https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables/{variable_name} Removes one stack variable. Removing a variable the stack does not hold answers 200 with changed false. The edit is merged into the deployed variables under the cluster lock, so keys it does not name are never overwritten, including a key the application image-tag lane moved a moment earlier. It is a live write: a pending draft of the stack is neither read nor published, and the stack's description, deploy wave and members are kept. A pending draft of the stack picks this write up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Requires `stacks.write` on the cluster. # Remove one stack variable (browser session) Source: https://docs.ankra.ai/api-reference/imported-clusters/remove-one-stack-variable-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables/{variable_name} Removes one stack variable. Removing a variable the stack does not hold answers 200 with changed false. The edit is merged into the deployed variables under the cluster lock, so keys it does not name are never overwritten, including a key the application image-tag lane moved a moment earlier. It is a live write: a pending draft of the stack is neither read nor published, and the stack's description, deploy wave and members are kept. A pending draft of the stack picks this write up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Requires `stacks.write` on the cluster. Browser-session twin of the /api/v1 route; a write also requires the X-Ankra-CSRF header. # Rename a cluster stack Source: https://docs.ankra.ai/api-reference/imported-clusters/rename-a-cluster-stack https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/rename-stack Renames a stack on the cluster and commits the change to the cluster's GitOps repository when one is connected. Answers `409` while the stack has unpublished drafts, when the new name is taken, or while a managed service that has not been retired owns the stack. Requires `stacks.write` on the cluster; a caller without it is refused with `403`. # Replace a cluster power schedule Source: https://docs.ankra.ai/api-reference/imported-clusters/replace-a-cluster-power-schedule https://platform.ankra.app/openapi.json put /api/v1/org/clusters/imported/{cluster_id}/power-schedules/{schedule_id} The bearer twin of the portal's power-schedule update (`ankra cluster power-schedules update`). A full replace, not a patch: `enabled` must always be sent, `stop_mode` must be restated for a stop schedule, and `timezone` for a cron schedule. The response is the cluster's refreshed schedule listing. # Set and remove stack variables Source: https://docs.ankra.ai/api-reference/imported-clusters/set-and-remove-stack-variables https://platform.ankra.app/openapi.json patch /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables Sets and removes several stack variables in one atomic write. The edit is merged into the deployed variables under the cluster lock, so keys it does not name are never overwritten, including a key the application image-tag lane moved a moment earlier. It is a live write: a pending draft of the stack is neither read nor published, and the stack's description, deploy wave and members are kept. A pending draft of the stack picks this write up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Requires `stacks.write` on the cluster. # Set and remove stack variables (browser session) Source: https://docs.ankra.ai/api-reference/imported-clusters/set-and-remove-stack-variables-browser-session https://platform.ankra.app/openapi.json patch /org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables Sets and removes several stack variables in one atomic write. The edit is merged into the deployed variables under the cluster lock, so keys it does not name are never overwritten, including a key the application image-tag lane moved a moment earlier. It is a live write: a pending draft of the stack is neither read nor published, and the stack's description, deploy wave and members are kept. A pending draft of the stack picks this write up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Requires `stacks.write` on the cluster. Browser-session twin of the /api/v1 route; a write also requires the X-Ankra-CSRF header. # Set one stack variable Source: https://docs.ankra.ai/api-reference/imported-clusters/set-one-stack-variable https://platform.ankra.app/openapi.json put /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables/{variable_name} Sets one stack variable. The edit is merged into the deployed variables under the cluster lock, so keys it does not name are never overwritten, including a key the application image-tag lane moved a moment earlier. It is a live write: a pending draft of the stack is neither read nor published, and the stack's description, deploy wave and members are kept. A pending draft of the stack picks this write up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Requires `stacks.write` on the cluster. # Set one stack variable (browser session) Source: https://docs.ankra.ai/api-reference/imported-clusters/set-one-stack-variable-browser-session https://platform.ankra.app/openapi.json put /org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables/{variable_name} Sets one stack variable. The edit is merged into the deployed variables under the cluster lock, so keys it does not name are never overwritten, including a key the application image-tag lane moved a moment earlier. It is a live write: a pending draft of the stack is neither read nor published, and the stack's description, deploy wave and members are kept. A pending draft of the stack picks this write up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Requires `stacks.write` on the cluster. Browser-session twin of the /api/v1 route; a write also requires the X-Ankra-CSRF header. # Set what a cluster is called and what it is for Source: https://docs.ankra.ai/api-reference/imported-clusters/set-what-a-cluster-is-called-and-what-it-is-for https://platform.ankra.app/openapi.json patch /api/v1/org/clusters/imported/{cluster_id}/environment The bearer twin of the portal's environment control (`ankra cluster update --environment`): sets the environment label and, when the body carries one, the criticality that decides how loudly the cluster reports, in a single transaction. A body without a criticality leaves the stored one untouched and answers with the environment alone. An ImportCluster file's metadata.environment is applied only when the import creates the cluster, so this is how a cluster moves between environments afterwards. A person's own token records the label as that person's choice of what the cluster is for (`environment_source` person). A service account's token records it as automation, which confirms nothing, leaves the recorded source alone when the label does not change, and may not take production away from the cluster: moving it off a production label or lowering a production criticality answers 403. Requires `clusters.write` at the cluster. # Set what a cluster is called and what it is for (browser session) Source: https://docs.ankra.ai/api-reference/imported-clusters/set-what-a-cluster-is-called-and-what-it-is-for-browser-session https://platform.ankra.app/openapi.json patch /org/clusters/imported/{cluster_id}/environment Sets the environment label and, when the body carries one, the criticality that decides how loudly the cluster reports. Both are written in a single transaction because to the operator they are one decision: picking an environment without a matching criticality is how a cluster ends up labelled for testing while the insight scanner still grades every finding on it at production weight. A body without a criticality leaves the stored one untouched and answers with the environment alone, which is the shape every caller written before the two settings were merged sends and expects. Requires `clusters.write`. # The cluster's node-local storage for a scale_to_zero stop Source: https://docs.ankra.ai/api-reference/imported-clusters/the-clusters-node-local-storage-for-a-scale_to_zero-stop https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/power-schedules/node-local-storage The bearer twin of the portal's node-local storage read (`ankra cluster power-schedules create|update` reads it before a scale_to_zero stop). Answers which persistent volume claims keep their data on a worker's own disk, which a scale_to_zero stop deletes at every stop. A failed or empty inventory read is unknown, never none. # The cluster's power schedules Source: https://docs.ankra.ai/api-reference/imported-clusters/the-clusters-power-schedules https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/power-schedules The bearer twin of the portal's power-schedule list (`ankra cluster power-schedules list`). Answers the cluster's scheduled stop/start entries, newest first. # The stack's deployed variables Source: https://docs.ankra.ai/api-reference/imported-clusters/the-stacks-deployed-variables https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables The stack's deployed variables, deploy wave and description, read from the live stack only. Unlike the stack listing, a pending draft never contributes to this read; `has_pending_draft` says whether one exists. A pending draft picks live variable writes up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. # The stack's deployed variables (browser session) Source: https://docs.ankra.ai/api-reference/imported-clusters/the-stacks-deployed-variables-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/stacks/{stack_name}/variables The stack's deployed variables, deploy wave and description, read from the live stack only. Unlike the stack listing, a pending draft never contributes to this read; `has_pending_draft` says whether one exists. A pending draft picks live variable writes up when it is next reconciled against the deployed stack, and publishing the draft merges its variables with the live stack key by key: only a variable the draft itself changed keeps the draft's value. A variable added live after the draft last reconciled is one the draft never saw, so it is kept: publishing the draft cannot remove it. Browser-session twin of the /api/v1 route; a write also requires the X-Ankra-CSRF header. # Uninstall an add-on Source: https://docs.ankra.ai/api-reference/imported-clusters/uninstall-an-add-on https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_resource_id} Uninstalls an add-on from the cluster: in-flight changes to it are cancelled and it is marked for removal. Pass `delete=true` to remove it permanently and `force=true` to force-delete an add-on that is stuck uninstalling. On a stopped cluster a permanent, unforced removal is recorded and pushed to Git at once, and the add-on is uninstalled once the cluster is running or online (`pending_cluster_start` is true). # Update a cluster stack Source: https://docs.ankra.ai/api-reference/imported-clusters/update-a-cluster-stack https://platform.ankra.app/openapi.json patch /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name} Surgical partial update of a stack's addons/manifests. Accepts the same body as the web UI endpoint. Callers must set partial_stack=true to merge-update (vs replace) the named stack. Requires `stacks.write` on the cluster; a caller without it is refused with `403`. # Update add-on settings Source: https://docs.ankra.ai/api-reference/imported-clusters/update-add-on-settings https://platform.ankra.app/openapi.json put /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_name}/settings Updates the add-on's deployment settings with the members you supply and commits the change to the cluster's GitOps repository when one is connected. Requires `stacks.write`; invalid settings answer `400`. # Update cluster agent settings Source: https://docs.ankra.ai/api-reference/imported-clusters/update-cluster-agent-settings https://platform.ankra.app/openapi.json patch /api/v1/org/clusters/imported/{cluster_id}/agent/settings Turn the cluster agent's automatic upgrades on or off. With them on (the default) the platform's fleet rollout applies each new agent release as soon as the cluster has no write execution running; with them off the rollout skips this agent until they are turned back on, which is how a team fences a cluster off from agent releases for a freeze window. The bearer twin of the portal's cluster settings switch (ankra cluster agent auto-upgrade enable|disable, MCP set_cluster_agent_auto_upgrade). POST .../agent/upgrade still applies a release on demand whichever way the switch sits. Every change writes a set_agent_auto_upgrade audit row naming who made it, the previous and new value and the reason when one is given. # Upgrade the cluster agent Source: https://docs.ankra.ai/api-reference/imported-clusters/upgrade-the-cluster-agent https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/agent/upgrade Sends the cluster agent an upgrade command and returns its acknowledgement. Without a body it installs the latest stable release, or re-renders the agent at the release it runs when that is newer, so a canary on a release candidate is never walked back. `version` names an exact release; one older than the running release answers `409` unless `allow_downgrade` is set, and a release that is neither published nor running answers `422`. Requires `agents.manage`; answers `503` when the cluster is offline or has no agent and `504` when the agent does not respond in time. # Withdraw a pending add-on removal Source: https://docs.ankra.ai/api-reference/imported-clusters/withdraw-a-pending-add-on-removal https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_resource_id}/withdraw-removal Takes back the removal of an add-on that was removed while its cluster was stopped and is still waiting for the cluster to start. The add-on is declared in the cluster's Git repository again and comes back with the rest of the stack when the cluster starts. # Introduction Source: https://docs.ankra.ai/api-reference/introduction Automate cluster operations, integrate with CI/CD, and build on the Ankra platform via REST. Automate cluster operations, integrate with your CI/CD, and build on top of the Ankra platform. The Ankra REST API lets you programmatically manage Ankra resources - such as clusters and add-ons - run operations, and retrieve statuses from your own tools, scripts, and pipelines. *** ## Quickstart ### 1) Generate an API token 1. Click your profile avatar (bottom-left) in the Ankra dashboard. 2. **Profile → API Tokens → Create**. 3. Name the token (choose MCP access only if an MCP client will use it) and **copy it somewhere safe**. > You won’t be able to view the token again after closing the dialog. > [!NOTE] > > That is a **personal** token: it acts as you, and it stops working if you leave the organisation. For anything that runs without a person behind it - CI/CD, Terraform, an agent - create a [service token](/guides/service-tokens) instead. It is owned by the organisation, carries a role you pick rather than all of your authority, and survives offboarding. ### 2) Make your first request **Bash (cURL)** ```bash theme={null} export ANKRA_API_TOKEN="" curl -sS \ -H "Authorization: Bearer $ANKRA_API_TOKEN" \ -H "Accept: application/json" \ https://platform.ankra.app/api/v1/clusters ``` **Python** ```python theme={null} import requests headers = {"Authorization": "Bearer "} resp = requests.get("https://platform.ankra.app/api/v1/clusters", headers=headers) resp.raise_for_status() print(resp.json()) ``` **Node.js** ```javascript theme={null} const res = await fetch("https://platform.ankra.app/api/v1/clusters", { headers: { Authorization: "Bearer " } }); if (!res.ok) throw new Error(await res.text()); console.log(await res.json()); ``` *** ## Authentication Use an API token in the `Authorization` header: ```http theme={null} Authorization: Bearer ``` * Tokens are created in the dashboard or with `ankra tokens create`. A token acts with its owner's role (a service token with the role it was given); scopes only add [MCP server](/platform/mcp-server) access. * Revoke a personal token anytime from **Profile → API Tokens**, and a [service token](/guides/service-tokens) from **Organisation → Service tokens**. * Prefer separate tokens for different use cases (e.g., CLI vs. CI/CD). * A service token is pinned to its organisation: the `X-Ankra-Organisation-Id` override that `ankra --org` uses is refused for it. ### Security best practices * Treat tokens like passwords - never commit them to source control. * Grant only the minimum required permissions. * Rotate/revoke tokens regularly and immediately if you suspect compromise. * Store tokens as secrets in your CI/CD system (e.g., GitHub Actions, GitLab CI, Jenkins). * Use a [service token](/guides/service-tokens) for automation, so the credential is not tied to one person's membership and can be reviewed and revoked by any administrator. **CLI example (authenticate once):** ```bash theme={null} export ANKRA_API_TOKEN="" ``` **GitHub Actions example:** ```yaml theme={null} env: ANKRA_API_TOKEN: ${{ secrets.ANKRA_API_TOKEN }} ``` *** ## Errors The API uses standard HTTP status codes. Error responses include a machine-readable body to help diagnose issues. | Code | Meaning | | - | - | | 200 | Success | | 400 | Bad request / validation | | 401 | Missing or invalid token | | 403 | Insufficient permissions | | 404 | Resource not found | | 429 | Too many requests (rate limit) | | 5xx | Server errors | *** ## Pagination & filtering Endpoints that return collections may support pagination and filters. Refer to the endpoint’s page for available query parameters and response shapes. *** ## Versioning This reference covers the **v1** API (`/api/v1`). Breaking changes are introduced only in new major versions; minor, non-breaking updates may be added to v1. *** ## Help & support * Check the endpoint pages for required parameters and examples. * Review token permissions if you receive 401/403 errors. * If you’re stuck, [contact support](/platform/support). # Apply a Kubernetes resource Source: https://docs.ankra.ai/api-reference/kubernetes/apply-a-kubernetes-resource https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/resources/apply Applies a Kubernetes manifest to the cluster through the cluster agent, optionally as a dry run. Requires `kubernetes.write` on the cluster. A dry-run preview of a Secret carries value digests, never the values. # Delete a Kubernetes resource Source: https://docs.ankra.ai/api-reference/kubernetes/delete-a-kubernetes-resource https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/resources/delete Deletes one Kubernetes resource, identified by `kind`, `name` and (for namespaced kinds) `namespace`, through the cluster agent; supports `dry_run` and `grace_period_seconds`. Requires `kubernetes.write` on the cluster. A dry-run preview of a Secret carries value digests, never the values. # Delete a Kubernetes resource with identity preconditions (browser session) Source: https://docs.ankra.ai/api-reference/kubernetes/delete-a-kubernetes-resource-with-identity-preconditions-browser-session https://platform.ankra.app/openapi.json post /org/clusters/{cluster_id}/kubernetes/resources/delete-guarded Requires a supporting agent. Older agents refuse the guarded command rather than performing an unguarded deletion. # Get a Helm release Source: https://docs.ankra.ai/api-reference/kubernetes/get-a-helm-release https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/kubernetes/helm/releases/{namespace}/{name} Returns the details of one Helm release on the cluster, read live through the cluster agent. Requires `kubernetes.read` on the cluster; answers 404 when the release does not exist. Credential-shaped values, Secret values in the rendered manifests and credentials in the notes are withheld unless `reveal_values` is set and the caller holds `kubernetes.secrets_reveal`; `secret_values` says which. # Get Helm release history Source: https://docs.ankra.ai/api-reference/kubernetes/get-helm-release-history https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/kubernetes/helm/releases/{namespace}/{name}/history Returns the revision history of a Helm release, up to `limit` revisions (default 25, maximum 200), read through the cluster agent. Requires `kubernetes.read` on the cluster. # Get Kubernetes resources Source: https://docs.ankra.ai/api-reference/kubernetes/get-kubernetes-resources https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/resources/get Reads one or more batches of Kubernetes resources by kind, with optional name, namespace, label and field selectors and pagination. Requires `kubernetes.read`; results may be served from Ankra's cache, including stale cached data when the cluster is unreachable. Secret values come back as digests unless `reveal_secret_values` is set on a read of one Secret by name and the caller holds `kubernetes.secrets_reveal`; each item's `secret_values` says which. # List Helm releases Source: https://docs.ankra.ai/api-reference/kubernetes/list-helm-releases https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/resources/helm/get Lists Helm releases on the cluster through the cluster agent, per namespace or across all namespaces, with optional name filter and pagination. Requires `kubernetes.read` on the cluster. # List pods Source: https://docs.ankra.ai/api-reference/kubernetes/list-pods https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/kubernetes/pods Returns a page of the cluster's pods from Ankra's synchronised cache, with filters for namespace, name, node and phase, plus sorting. Requires `kubernetes.read`; the response reports how stale the cached data is and lists the cluster's namespaces. # Patch a Kubernetes resource Source: https://docs.ankra.ai/api-reference/kubernetes/patch-a-kubernetes-resource https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/resources/patch Patches one Kubernetes resource through the cluster agent, using a strategic merge patch by default (`patch_type` selects another), optionally as a dry run. Requires `kubernetes.write` on the cluster. A dry-run preview of a Secret carries value digests, never the values. # Roll back a Helm release Source: https://docs.ankra.ai/api-reference/kubernetes/roll-back-a-helm-release https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/helm/releases/{namespace}/{name}/rollback Rolls a Helm release back to the given `revision` through the cluster agent, waiting for resources to be ready by default (`timeout_seconds` defaults to 600, maximum 3600). Requires `kubernetes.write`; a release managed by an Ankra add-on is refused with 409 and must be rolled back through the add-on. # Run a Prometheus range query Source: https://docs.ankra.ai/api-reference/kubernetes/run-a-prometheus-range-query https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/metrics/query_range Runs a PromQL range query against the cluster's configured metrics source, over either a relative `range` or explicit `start`/`end` timestamps. Requires `kubernetes.read`; answers 404 when the cluster has no metrics source, and queries are briefly cached and rate limited per organisation (429 with a retry hint). # Run an instant Prometheus query Source: https://docs.ankra.ai/api-reference/kubernetes/run-an-instant-prometheus-query https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/metrics/query Runs a PromQL instant query against the cluster's configured metrics source and returns the result series. Requires `kubernetes.read`; answers 404 when the cluster has no metrics source, and identical queries are briefly cached and rate limited per organisation (429 with a retry hint). # Stream pod logs Source: https://docs.ankra.ai/api-reference/kubernetes/stream-pod-logs https://platform.ankra.app/openapi.json get /api/v1/clusters/{cluster_id}/kubernetes/pod/logs Streams a pod's container logs as server-sent events through the cluster agent, following new lines by default (`follow=false` returns the current log and ends). Requires `kubernetes.read` and `kubernetes.logs` on the cluster; the viewer role does not hold `kubernetes.logs`. `previous=true` reads the last terminated container and never follows. # Uninstall a Helm release Source: https://docs.ankra.ai/api-reference/kubernetes/uninstall-a-helm-release https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/resources/helm/uninstall Uninstalls the named Helm release from the given namespace through the cluster agent. Requires `kubernetes.write` on the cluster. # Upgrade a Helm release Source: https://docs.ankra.ai/api-reference/kubernetes/upgrade-a-helm-release https://platform.ankra.app/openapi.json post /api/v1/clusters/{cluster_id}/kubernetes/helm/releases/{namespace}/{name}/upgrade Upgrades a Helm release to `chart_ref` with exactly the supplied `values_yaml`, which replaces the release's current values (an omitted `chart_version` keeps the installed chart version). Requires `kubernetes.write`; a release managed by an Ankra add-on is refused with 409 and must be changed through the add-on. By default the request waits for the agent's answer; with `async` true it answers 202 with an `operation_id` and the upgrade's verdict is recorded on that operation. # Add a managed node pool Source: https://docs.ankra.ai/api-reference/managed-clusters/add-a-managed-node-pool https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools Bearer PAT authentication; RBAC permission `clusters.write`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Add a managed node pool (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/add-a-managed-node-pool-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/node-pools Browser session authentication; RBAC permission `clusters.write`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Create a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/create-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider} Bearer PAT authentication; RBAC permission `clusters.create`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Create a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/create-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider} Browser session authentication; RBAC permission `clusters.create`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Create a provider network Source: https://docs.ankra.ai/api-reference/managed-clusters/create-a-provider-network https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/networks Provision a provider network with the scoping credential so the create wizard can offer an inline create-network flow. GKE creates an auto subnet-mode VPC (a subnetwork in every region, like the GCP default network) and waits for the compute operation to finish; other providers answer 400. The GCP service account needs the compute.networks.create permission (for example via the Compute Network Admin role, roles/compute.networkAdmin); a credential without it answers 400 with the provider's permission message. The response is the created network in the options catalog's private_networks shape. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Create a provider network (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/create-a-provider-network-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/networks Provision a provider network with the scoping credential so the create wizard can offer an inline create-network flow. GKE creates an auto subnet-mode VPC (a subnetwork in every region, like the GCP default network) and waits for the compute operation to finish; other providers answer 400. The GCP service account needs the compute.networks.create permission (for example via the Compute Network Admin role, roles/compute.networkAdmin); a credential without it answers 400 with the provider's permission message. The response is the created network in the options catalog's private_networks shape. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Delete a managed node pool Source: https://docs.ankra.ai/api-reference/managed-clusters/delete-a-managed-node-pool https://platform.ankra.app/openapi.json delete /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name} Bearer PAT authentication; RBAC permission `clusters.write`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Delete a managed node pool (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/delete-a-managed-node-pool-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name} Browser session authentication; RBAC permission `clusters.write`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Delete the provider cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/delete-the-provider-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/delete-provider-cluster Bearer PAT authentication; RBAC permission `clusters.delete`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Delete the provider cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/delete-the-provider-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/delete-provider-cluster Browser session authentication; RBAC permission `clusters.delete`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Deprovision a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/deprovision-a-managed-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/managed/{provider}/{cluster_id} Bearer PAT authentication; RBAC permission `clusters.delete`. For Kapsule this legacy DELETE is disconnect-only; provider deletion requires the explicit delete-provider-cluster endpoint. provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. # Deprovision a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/deprovision-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/managed/{provider}/{cluster_id} Browser session authentication; RBAC permission `clusters.delete`. For Kapsule this legacy DELETE is disconnect-only; provider deletion requires the explicit delete-provider-cluster endpoint. provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. # Disconnect a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/disconnect-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/disconnect Bearer PAT authentication; RBAC permission `clusters.delete`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Disconnect a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/disconnect-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/disconnect Browser session authentication; RBAC permission `clusters.delete`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Discover managed clusters Source: https://docs.ankra.ai/api-reference/managed-clusters/discover-managed-clusters https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/discover Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Discover managed clusters (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/discover-managed-clusters-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/discover Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Import a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/import-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/import Bearer PAT authentication; RBAC permission `clusters.create`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Import a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/import-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/import Browser session authentication; RBAC permission `clusters.create`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # List live node-pool catalog for a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/list-live-node-pool-catalog-for-a-managed-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools/catalog Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List live node-pool catalog for a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/list-live-node-pool-catalog-for-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/{cluster_id}/node-pools/catalog Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List managed-cluster upgrades Source: https://docs.ankra.ai/api-reference/managed-clusters/list-managed-cluster-upgrades https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/{cluster_id}/upgrades Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List managed-cluster upgrades (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/list-managed-cluster-upgrades-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/{cluster_id}/upgrades Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List managed node pools Source: https://docs.ankra.ai/api-reference/managed-clusters/list-managed-node-pools https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List managed node pools (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/list-managed-node-pools-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/{cluster_id}/node-pools Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List managed-provider options Source: https://docs.ankra.ai/api-reference/managed-clusters/list-managed-provider-options https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/options Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # List managed-provider options (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/list-managed-provider-options-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/options Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # List options using the cluster's stored provider context Source: https://docs.ankra.ai/api-reference/managed-clusters/list-options-using-the-clusters-stored-provider-context https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/{cluster_id}/options Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # List options using the cluster's stored provider context (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/list-options-using-the-clusters-stored-provider-context-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/{cluster_id}/options Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Preflight a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/preflight-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/preflight Bearer PAT authentication; RBAC permission `clusters.create`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Preflight a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/preflight-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/preflight Browser session authentication; RBAC permission `clusters.create`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. For `ankracloud_k8s` with no `credential_id`, the organisation's built-in Ankra Cloud credential is used: the request answers 404 while the organisation's `ankra_cloud_provider` feature flag is off, and 503 with Retry-After when the built-in credential cannot be created or renewed right now. # Read managed-cluster status Source: https://docs.ankra.ai/api-reference/managed-clusters/read-managed-cluster-status https://platform.ankra.app/openapi.json get /api/v1/clusters/managed/{provider}/{cluster_id}/status Bearer PAT authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Read managed-cluster status (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/read-managed-cluster-status-browser-session https://platform.ankra.app/openapi.json get /org/clusters/managed/{provider}/{cluster_id}/status Browser session authentication; RBAC permission `clusters.read`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Replace a managed node pool with a new size Source: https://docs.ankra.ai/api-reference/managed-clusters/replace-a-managed-node-pool-with-a-new-size https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name}/replace Bearer PAT authentication; RBAC permission `clusters.write`. Requests a blue/green replacement of the node pool with a new instance size: a scheduler job creates a replacement pool with the requested size (same count, labels, taints, zone, and autoscaling), waits for it to become ready, deletes the replaced pool, and rebinds the stored definition to the replacement pool, named by `replacement_pool_name` when the request gives one and deterministically from the pool and size otherwise; the answer carries the name. No managed provider supports in-place pool resizing, so the 202 answer only accepts the intent. # Replace a managed node pool with a new size (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/replace-a-managed-node-pool-with-a-new-size-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name}/replace Browser session authentication; RBAC permission `clusters.write`. Requests a blue/green replacement of the node pool with a new instance size: a scheduler job creates a replacement pool with the requested size (same count, labels, taints, zone, and autoscaling), waits for it to become ready, deletes the replaced pool, and rebinds the stored definition to the replacement pool, named by `replacement_pool_name` when the request gives one and deterministically from the pool and size otherwise; the answer carries the name. No managed provider supports in-place pool resizing, so the 202 answer only accepts the intent. # Scale a managed node pool Source: https://docs.ankra.ai/api-reference/managed-clusters/scale-a-managed-node-pool https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name}/scale Bearer PAT authentication; RBAC permission `clusters.operate`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Scale a managed node pool (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/scale-a-managed-node-pool-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name}/scale Browser session authentication; RBAC permission `clusters.operate`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Start a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/start-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/start Bearer PAT authentication; RBAC permission `clusters.operate`. Requests the provider-native cluster start; only providers advertising `supports_stop_start` in capabilities (AKS today) support it, others answer 400. The provider completes the transition asynchronously and subsequent status reads converge the resulting power state. # Start a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/start-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/start Browser session authentication; RBAC permission `clusters.operate`. Requests the provider-native cluster start; only providers advertising `supports_stop_start` in capabilities (AKS today) support it, others answer 400. The provider completes the transition asynchronously and subsequent status reads converge the resulting power state. # Stop a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/stop-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/stop Bearer PAT authentication; RBAC permission `clusters.operate`. Requests the provider-native cluster stop; only providers advertising `supports_stop_start` in capabilities (AKS today) support it, others answer 400. The provider completes the transition asynchronously and subsequent status reads converge the resulting power state. # Stop a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/stop-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/stop Browser session authentication; RBAC permission `clusters.operate`. Requests the provider-native cluster stop; only providers advertising `supports_stop_start` in capabilities (AKS today) support it, others answer 400. The provider completes the transition asynchronously and subsequent status reads converge the resulting power state. # Update managed node-pool autoscaling Source: https://docs.ankra.ai/api-reference/managed-clusters/update-managed-node-pool-autoscaling https://platform.ankra.app/openapi.json patch /api/v1/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name} Bearer PAT authentication; RBAC permission `clusters.write`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Update managed node-pool autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/update-managed-node-pool-autoscaling-browser-session https://platform.ankra.app/openapi.json patch /org/clusters/managed/{provider}/{cluster_id}/node-pools/{node_pool_name} Browser session authentication; RBAC permission `clusters.write`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Upgrade a managed cluster Source: https://docs.ankra.ai/api-reference/managed-clusters/upgrade-a-managed-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/managed/{provider}/{cluster_id}/upgrade Bearer PAT authentication; RBAC permission `clusters.operate`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Upgrade a managed cluster (browser session) Source: https://docs.ankra.ai/api-reference/managed-clusters/upgrade-a-managed-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/managed/{provider}/{cluster_id}/upgrade Browser session authentication; RBAC permission `clusters.operate`. For Kapsule, provider_cluster_id is the opaque region-qualified `regions/{region}/clusters/{id}` reference. CNI is immutable. Imported/unknown provenance is disconnect-only; provider deletion is allowed by default only for created clusters. # Check MCP server health Source: https://docs.ankra.ai/api-reference/mcp-servers/check-mcp-server-health https://platform.ankra.app/openapi.json get /api/v1/org/mcp-servers/{server_id}/health Connects to the server now and reports whether it answered, with the names of the allowed tools it offers. A server that cannot be reached is still a 200, with status error and the reason. Only http and sse servers can be probed. # Delete an MCP server Source: https://docs.ankra.ai/api-reference/mcp-servers/delete-an-mcp-server https://platform.ankra.app/openapi.json delete /api/v1/org/mcp-servers/{server_id} Removes the server from the organisation, so AI agents can no longer call its tools. Requires the ai.manage permission. # Disable an MCP server Source: https://docs.ankra.ai/api-reference/mcp-servers/disable-an-mcp-server https://platform.ankra.app/openapi.json post /api/v1/org/mcp-servers/{server_id}/disable Turns the server off without deleting it: AI agents stop calling its tools until it is enabled again. Requires the ai.manage permission. # Enable an MCP server Source: https://docs.ankra.ai/api-reference/mcp-servers/enable-an-mcp-server https://platform.ankra.app/openapi.json post /api/v1/org/mcp-servers/{server_id}/enable Turns the server on, so AI agents can call its allowed tools again. Requires the ai.manage permission. # Get an MCP server Source: https://docs.ankra.ai/api-reference/mcp-servers/get-an-mcp-server https://platform.ankra.app/openapi.json get /api/v1/org/mcp-servers/{server_id} Returns one registered MCP server: its URL, transport, env key names, allowed tools, permission tier, adapter and cluster scope. Env values are never returned. # Grant an MCP tool to a role Source: https://docs.ankra.ai/api-reference/mcp-servers/grant-an-mcp-tool-to-a-role https://platform.ankra.app/openapi.json post /api/v1/org/mcp-servers/{server_id}/grants Lets members with the given role use one of the server's tools. The role defaults to member. Requires the ai.manage permission. # List curated MCP adapters Source: https://docs.ankra.ai/api-reference/mcp-servers/list-curated-mcp-adapters https://platform.ankra.app/openapi.json get /api/v1/org/mcp-servers/catalog Returns the curated MCP integrations Ankra knows how to connect: each adapter's transport, default or example URL, the credential headers it needs, and the tools it recommends. The list is built into Ankra and the same for every organisation. # List MCP server tools Source: https://docs.ankra.ai/api-reference/mcp-servers/list-mcp-server-tools https://platform.ankra.app/openapi.json get /api/v1/org/mcp-servers/{server_id}/tools Connects to the server now and returns every tool it offers, including tools outside allowed_tools, with Ankra's read or write verdict, whether the tool is allowed and the roles it is granted to. Use it to widen a narrowed allowed_tools list. A server that cannot be reached is still a 200, with status error and the reason. # List MCP servers Source: https://docs.ankra.ai/api-reference/mcp-servers/list-mcp-servers https://platform.ankra.app/openapi.json get /api/v1/org/mcp-servers Lists the MCP servers registered in the organisation, sorted by name, with their status and how many tool grants each one has. Any member can read it. # List MCP tool grants Source: https://docs.ankra.ai/api-reference/mcp-servers/list-mcp-tool-grants https://platform.ankra.app/openapi.json get /api/v1/org/mcp-servers/{server_id}/grants Lists which roles may use which of the server's tools, sorted by tool name and role. # Register an MCP server Source: https://docs.ankra.ai/api-reference/mcp-servers/register-an-mcp-server https://platform.ankra.app/openapi.json post /api/v1/org/mcp-servers Registers an MCP server for the organisation's AI agents. A remote server (http or sse) must have a public http(s) URL. An env key that looks like a credential must reference a secret by marker (${SECRET_SLOT:}, or ${VAULT:} naming one of the organisation's own secret slots): a plain value, or a ${VAULT:} outside the organisation's secret slots, is refused, so create a secret slot first and pass its sentinel. Env values are never returned. Requires the ai.manage permission. # Revoke an MCP tool grant Source: https://docs.ankra.ai/api-reference/mcp-servers/revoke-an-mcp-tool-grant https://platform.ankra.app/openapi.json delete /api/v1/org/mcp-servers/{server_id}/grants/{tool_name}/{role} Removes one tool grant, so members with that role can no longer use the tool. Requires the ai.manage permission. # Update an MCP server Source: https://docs.ankra.ai/api-reference/mcp-servers/update-an-mcp-server https://platform.ankra.app/openapi.json put /api/v1/org/mcp-servers/{server_id} Changes the fields the body names and keeps the rest. env replaces every env entry, while env_patch changes only the keys it names. An explicit null clears allowed_tools, adapter_key or cluster_allow_list. The name and transport cannot change: register a new server for that. The same URL and secret-marker rules as registration apply. Requires the ai.manage permission. # Create a notification route Source: https://docs.ankra.ai/api-reference/notifications/create-a-notification-route https://platform.ankra.app/openapi.json post /api/v1/org/notifications/routes Creates a routing rule that sends matching notifications to a destination: an alert integration (webhook or channel) or a push audience of organisation members. Answers 404 when the destination, a named member or `cluster_id` does not belong to the organisation. # Delete a notification route Source: https://docs.ankra.ai/api-reference/notifications/delete-a-notification-route https://platform.ankra.app/openapi.json delete /api/v1/org/notifications/routes/{route_id} Deletes a routing rule and answers 204. Answers 404 when the route does not exist in the organisation. # List notification routes Source: https://docs.ankra.ai/api-reference/notifications/list-notification-routes https://platform.ankra.app/openapi.json get /api/v1/org/notifications/routes Lists the organisation's notification routing rules: which notification kinds, severities and clusters each rule matches, the destination it delivers to, its priority and whether it is enabled. # Preview notification routing Source: https://docs.ankra.ai/api-reference/notifications/preview-notification-routing https://platform.ankra.app/openapi.json post /api/v1/org/notifications/routes/preview Shows which destinations a hypothetical notification would reach, and why, without sending anything. `kind` and `severity` are required; `cluster_id`, `source_id` and `alert_id` narrow the notification being previewed. # Test a notification route Source: https://docs.ankra.ai/api-reference/notifications/test-a-notification-route https://platform.ankra.app/openapi.json post /api/v1/org/notifications/routes/{route_id}/test Queues a sample delivery through the route and answers 202 with the `delivery_id`. A push route is tested against the caller's own registered mobile devices (one delivery each, in `delivery_ids`) and answers 400 when the caller has none. # Update a notification route Source: https://docs.ankra.ai/api-reference/notifications/update-a-notification-route https://platform.ankra.app/openapi.json patch /api/v1/org/notifications/routes/{route_id} Updates the fields sent in the body of a routing rule and leaves the rest unchanged. A new destination or cluster is checked against the organisation first; answers 404 when the route does not exist. # Create an object storage bucket Source: https://docs.ankra.ai/api-reference/object-storage-buckets/create-an-object-storage-bucket https://platform.ankra.app/openapi.json post /api/v1/org/object-storage-buckets Bearer PAT authentication; RBAC permission `credentials.write`. Creates a bucket on one of the organisation's provider credentials (Hetzner, Scaleway, DigitalOcean or UpCloud). Answers 202 with the bucket in `provisioning`; the platform creates it and the bucket moves to `ready` or `error`. Hetzner needs an Object Storage key pair, passed here or already stored on the Hetzner credential; every other provider mints its keys from the credential and refuses a supplied pair. # Delete an object storage bucket Source: https://docs.ankra.ai/api-reference/object-storage-buckets/delete-an-object-storage-bucket https://platform.ankra.app/openapi.json delete /api/v1/org/object-storage-buckets/{bucket_id} Bearer PAT authentication; RBAC permission `credentials.delete`. An ordinary delete forgets the bucket and drops the keys Ankra holds for it, leaving the bucket and its objects on the provider. `destroy_provider_resources=true` also empties and deletes the bucket when Ankra recorded creating it and removes what Ankra minted for it; a bucket still provisioning is always torn down. A bucket that already existed under the requested name is refused at provisioning and never emptied or deleted, and a bucket provisioned before Ankra recorded its creations is kept as well. A teardown that fails brings the bucket back in `error` so it can be retried. # Get an object storage bucket Source: https://docs.ankra.ai/api-reference/object-storage-buckets/get-an-object-storage-bucket https://platform.ankra.app/openapi.json get /api/v1/org/object-storage-buckets/{bucket_id} Bearer PAT authentication; RBAC permission `credentials.read` (read-class). Key material is never returned. # List object storage buckets Source: https://docs.ankra.ai/api-reference/object-storage-buckets/list-object-storage-buckets https://platform.ankra.app/openapi.json get /api/v1/org/object-storage-buckets Bearer PAT authentication; RBAC permission `credentials.read` (read-class). Lists the organisation's live object storage buckets, newest first. Key material and the Vault path it lives under are never returned. # Generate an onboarding website Source: https://docs.ankra.ai/api-reference/onboarding/generate-an-onboarding-website https://platform.ankra.app/openapi.json post /org/onboarding/websites Generate a website from a short prompt on the organisation's website_generation AI lane and deploy it to the cluster: served from a stack (target starter) or committed to a new GitHub repository and deployed as an application (target repository). Answers 202 at once; poll the website for its state. Browser-session route; requires the X-Ankra-CSRF header. # Get an onboarding website Source: https://docs.ankra.ai/api-reference/onboarding/get-an-onboarding-website https://platform.ankra.app/openapi.json get /org/onboarding/websites/{website_id} One generated website and its progress. Browser-session route. # List onboarding websites Source: https://docs.ankra.ai/api-reference/onboarding/list-onboarding-websites https://platform.ankra.app/openapi.json get /org/onboarding/websites The organisation's generated websites on one cluster, latest first. Browser-session route. # List the onboarding website starters Source: https://docs.ankra.ai/api-reference/onboarding/list-the-onboarding-website-starters https://platform.ankra.app/openapi.json get /org/onboarding/website-templates The website starters the onboarding wizard offers, the order of their categories, and the themes any of them can wear. The same for every organisation. Browser-session route. # (Removed) Cancel a job within an operation Source: https://docs.ankra.ai/api-reference/operations/removed-cancel-a-job-within-an-operation https://platform.ankra.app/openapi.json post /api/v1/org/operations/{operation_id}/jobs/{job_id}/cancel This endpoint has been removed and always answers 410 Gone. Use `POST /api/v1/org/executions/{execution_id}/steps/{step_id}/cancel` instead. # (Removed) Cancel an operation Source: https://docs.ankra.ai/api-reference/operations/removed-cancel-an-operation https://platform.ankra.app/openapi.json post /api/v1/org/operations/{operation_id}/cancel This endpoint has been removed and always answers 410 Gone. Use `POST /api/v1/org/executions/{execution_id}/cancel` instead. # Create a custom role (members.manage) Source: https://docs.ankra.ai/api-reference/organisation-iam/create-a-custom-role-membersmanage https://platform.ankra.app/openapi.json post /api/v1/org/organisation/roles Creates a custom role in the organisation from a `slug`, `name`, optional `description`, a list of `permissions` and optional Kubernetes `kube_grants`. Requires `members.manage`. # Create a GitHub Actions trust policy Source: https://docs.ankra.ai/api-reference/organisation-iam/create-a-github-actions-trust-policy https://platform.ankra.app/openapi.json post /api/v1/org/organisation/oidc-trust-policies Requires members.manage in the authenticated organisation; service accounts are refused. Creating a policy provisions its service account and refuses role escalation. Removing a policy revokes tokens issued by it. Browser mutations require the session CSRF token. # Create a GitHub Actions trust policy for a browser session Source: https://docs.ankra.ai/api-reference/organisation-iam/create-a-github-actions-trust-policy-for-a-browser-session https://platform.ankra.app/openapi.json post /org/organisation/oidc-trust-policies Requires members.manage in the authenticated organisation; service accounts are refused. Creating a policy provisions its service account and refuses role escalation. Removing a policy revokes tokens issued by it. Browser mutations require the session CSRF token. # Grant a scoped role to a member (members.manage) Source: https://docs.ankra.ai/api-reference/organisation-iam/grant-a-scoped-role-to-a-member-membersmanage https://platform.ankra.app/openapi.json post /api/v1/org/organisation/assignments Assigns a built-in role (`role_slug`) or custom role (`role_id`) to a member at `scope_type` `organisation`, `cluster` or `cluster_group` (with `scope_id` for the latter two). Requires `members.manage`; returns 409 when the same assignment already exists. # List GitHub Actions trust policies Source: https://docs.ankra.ai/api-reference/organisation-iam/list-github-actions-trust-policies https://platform.ankra.app/openapi.json get /api/v1/org/organisation/oidc-trust-policies Requires members.manage in the authenticated organisation; service accounts are refused. Creating a policy provisions its service account and refuses role escalation. Removing a policy revokes tokens issued by it. Browser mutations require the session CSRF token. # List GitHub Actions trust policies for a browser session Source: https://docs.ankra.ai/api-reference/organisation-iam/list-github-actions-trust-policies-for-a-browser-session https://platform.ankra.app/openapi.json get /org/organisation/oidc-trust-policies Requires members.manage in the authenticated organisation; service accounts are refused. Creating a policy provisions its service account and refuses role escalation. Removing a policy revokes tokens issued by it. Browser mutations require the session CSRF token. # List member role assignments (members.read) Source: https://docs.ankra.ai/api-reference/organisation-iam/list-member-role-assignments-membersread https://platform.ankra.app/openapi.json get /api/v1/org/organisation/members/{ankra_user_id}/assignments Lists the role assignments of one organisation member, each with its role and scope. Requires `members.read`. # List organisation role assignments (members.read) Source: https://docs.ankra.ai/api-reference/organisation-iam/list-organisation-role-assignments-membersread https://platform.ankra.app/openapi.json get /api/v1/org/organisation/assignments Lists the live role assignments of every active member of the organisation in one read, at organisation, cluster and cluster-group scope, each with the member's `ankra_user_id` and `user_id`. Ordered by member, then age. At most 5000 assignments are returned (`limit`); `truncated` is true when the organisation holds more, and the per-member listing stays complete for one person. Requires `members.read`. # List organisation roles (members.read) Source: https://docs.ankra.ai/api-reference/organisation-iam/list-organisation-roles-membersread https://platform.ankra.app/openapi.json get /api/v1/org/organisation/roles Lists the built-in role definitions together with the organisation's custom roles. Requires `members.read`. # List the organisations of the person signed in to Ankra Cloud Source: https://docs.ankra.ai/api-reference/organisation-iam/list-the-organisations-of-the-person-signed-in-to-ankra-cloud https://platform.ankra.app/openapi.json get /api/v1/cloud/me/organisations Read-only organisation directory for Ankra Cloud. The credential is the identity provider's access token of the person, issued for the dedicated directory audience to an authorised Ankra Cloud client; personal access tokens and browser sessions are not accepted. Answers the organisations the person is an active member of, and an empty list for a person with none. No session is created and no organisation is selected. # Reconcile a Cloud workspace organisation and membership Source: https://docs.ankra.ai/api-reference/organisation-iam/reconcile-a-cloud-workspace-organisation-and-membership https://platform.ankra.app/openapi.json post /api/v1/cloud/workspaces/sync Dedicated Cloud machine authentication. Target organisations are created once or adopted through server-managed configuration. Complete monotonic snapshots own member names, roles, and revocations; no organisation ID is accepted from the caller. # Remove a GitHub Actions trust policy Source: https://docs.ankra.ai/api-reference/organisation-iam/remove-a-github-actions-trust-policy https://platform.ankra.app/openapi.json delete /api/v1/org/organisation/oidc-trust-policies/{policy_id} Requires members.manage in the authenticated organisation; service accounts are refused. Creating a policy provisions its service account and refuses role escalation. Removing a policy revokes tokens issued by it. Browser mutations require the session CSRF token. # Remove a GitHub Actions trust policy for a browser session Source: https://docs.ankra.ai/api-reference/organisation-iam/remove-a-github-actions-trust-policy-for-a-browser-session https://platform.ankra.app/openapi.json delete /org/organisation/oidc-trust-policies/{policy_id} Requires members.manage in the authenticated organisation; service accounts are refused. Creating a policy provisions its service account and refuses role escalation. Removing a policy revokes tokens issued by it. Browser mutations require the session CSRF token. # Replace custom role permissions (members.manage) Source: https://docs.ankra.ai/api-reference/organisation-iam/replace-custom-role-permissions-membersmanage https://platform.ankra.app/openapi.json patch /api/v1/org/organisation/roles/{role_id} Replaces a custom role's name, description, permissions and Kubernetes grants with the values sent; members holding the role get the new permissions. Requires `members.manage`. # Retire a custom role and its assignments (members.manage) Source: https://docs.ankra.ai/api-reference/organisation-iam/retire-a-custom-role-and-its-assignments-membersmanage https://platform.ankra.app/openapi.json delete /api/v1/org/organisation/roles/{role_id} Deletes a custom role that no active member holds. While anyone still holds it the delete is refused with 409 `{"detail": " still hold . Give them another role first.", "reason": "role_in_use"}`: give them another role first. Requires `members.manage`; returns 404 when the role does not exist. # Revoke a scoped role assignment (members.manage) Source: https://docs.ankra.ai/api-reference/organisation-iam/revoke-a-scoped-role-assignment-membersmanage https://platform.ankra.app/openapi.json delete /api/v1/org/organisation/assignments/{assignment_id} Revokes one role assignment. Requires `members.manage`; the last organisation-wide owner assignment cannot be removed. # Claim an email domain for single sign-on Source: https://docs.ankra.ai/api-reference/organisation/claim-an-email-domain-for-single-sign-on https://platform.ankra.app/openapi.json post /org/organisation/{organisation_id}/sso/domains Claims an email domain for the connection and answers the TXT record (`_ankra-challenge.`) that proves the organisation controls it. Public email providers cannot be claimed, and a domain another organisation has already verified is refused. # Configure the organisation's identity provider Source: https://docs.ankra.ai/api-reference/organisation/configure-the-organisations-identity-provider https://platform.ankra.app/openapi.json put /org/organisation/{organisation_id}/sso Connects the organisation's own OpenID Connect identity provider, or replaces its issuer and client. The client secret is forwarded to the identity broker and stored nowhere in Ankra, so it is required on the first save and whenever the issuer or client ID changes. The configuring administrator becomes the connection's first break-glass member. # Create a new organisation Source: https://docs.ankra.ai/api-reference/organisation/create-a-new-organisation https://platform.ankra.app/openapi.json post /api/v1/org/organisation Creates a new organisation on the free plan with the calling user as its owner, from a `name` and optional `country`. Returns 201 with the new organisation. # Delete organisation logo Source: https://docs.ankra.ai/api-reference/organisation/delete-organisation-logo https://platform.ankra.app/openapi.json delete /org/organisation/{organisation_id}/logo Removes the organisation's logo. Requires `organisation.manage`. # Delete user from organisation Source: https://docs.ankra.ai/api-reference/organisation/delete-user-from-organisation https://platform.ankra.app/openapi.json delete /api/v1/org/organisation/user Removes a user (`user_id`) from an organisation (`organisation_id`). Users may remove themselves; removing someone else requires `members.manage`, and the last user of an organisation cannot be removed. # Ensure the cluster repository webhook Source: https://docs.ankra.ai/api-reference/organisation/ensure-the-cluster-repository-webhook https://platform.ankra.app/openapi.json post /org/clusters/{cluster_id}/webhook/ensure Browser session authentication; ensures the cluster's connected GitOps repository carries the platform webhook, creating or repairing it (the repository-settings 'ensure webhook' action). Rate limited per cluster: a burst answers 429 with Retry-After. A cluster with no connected repository answers 404 with the reason in detail. Browser route only; there is no bearer-PAT twin yet (the route census's twin-gap ratchet lists it). # Export the organisation audit log Source: https://docs.ankra.ai/api-reference/organisation/export-the-organisation-audit-log https://platform.ankra.app/openapi.json get /org/organisation/audit-logs/export Downloads the organisation audit log as a CSV (default) or JSON attachment (`format=csv|json`), using the same filters as the listing. Requires `audit.read`; at most 100,000 rows are exported, and a truncated CSV is flagged with the `X-Ankra-Export-Truncated: true` header while JSON carries a `truncated` field. # Export the organisation audit log with an API token Source: https://docs.ankra.ai/api-reference/organisation/export-the-organisation-audit-log-with-an-api-token https://platform.ankra.app/openapi.json get /api/v1/org/organisation/audit-logs/export Bearer twin of GET /org/organisation/audit-logs/export for scheduled jobs such as SIEM forwarding. Accepts a personal or service-account API token; the token (its scopes and its role) must hold audit.read in the organisation. Same filters, formats, 100000-row cap and organisation scope as the session export. The organisation is the token's own, or the one named by the X-Ankra-Organisation-Id header when the token's user is a member of it; a service-account token cannot leave its organisation. # Get organisation details Source: https://docs.ankra.ai/api-reference/organisation/get-organisation-details https://platform.ankra.app/openapi.json get /api/v1/org/organisation/{organisation_id} Returns one organisation the calling user belongs to, with its members. Returns 404 when the organisation does not exist or the caller is not a member. # Get organisation logo Source: https://docs.ankra.ai/api-reference/organisation/get-organisation-logo https://platform.ankra.app/openapi.json get /api/v1/org/organisation/{organisation_id}/logo Returns the organisation's logo image with its stored content type, or 404 when none is set. The logo is readable by active members of the organisation, and by any caller while the organisation publishes a stack profile that caller can see (public, or shared with the caller's organisation), which is how stack profile cards show the publisher's logo. Anyone else gets the same 404 as an organisation without a logo. Responses carry an `ETag` and honour `If-None-Match` (304); adding a `v` query parameter makes the response cacheable for a year. # Get organisation logo (browser session) Source: https://docs.ankra.ai/api-reference/organisation/get-organisation-logo-browser-session https://platform.ankra.app/openapi.json get /org/organisation/{organisation_id}/logo Returns the organisation's logo image with its stored content type, or 404 when none is set. The logo is readable by active members of the organisation, and by any caller while the organisation publishes a stack profile that caller can see (public, or shared with the caller's organisation), which is how stack profile cards show the publisher's logo. Anyone else gets the same 404 as an organisation without a logo. Responses carry an `ETag` and honour `If-None-Match` (304); adding a `v` query parameter makes the response cacheable for a year. # Get organisation single sign-on settings Source: https://docs.ankra.ai/api-reference/organisation/get-organisation-single-sign-on-settings https://platform.ankra.app/openapi.json get /org/organisation/{organisation_id}/sso The organisation's single sign-on settings: the connected identity provider, its policy, the claimed email domains with the DNS records that prove them, the group-to-role mappings, the redirect URI to register with the provider and the organisation's sign-in link. Requires organisation.manage. `is_available` is false while single sign-on is switched off for the installation. # Invite user to organisation Source: https://docs.ankra.ai/api-reference/organisation/invite-user-to-organisation https://platform.ankra.app/openapi.json post /api/v1/org/organisation/invite Invites a user by email into an organisation with the role `member`, `admin` or `read-only` and emails them the invitation. Only organisation admins may invite (403 otherwise); returns 400 when an invite already exists or the plan's member limit is reached. # List organisations Source: https://docs.ankra.ai/api-reference/organisation/list-organisations https://platform.ankra.app/openapi.json get /api/v1/org/organisation Lists the organisations the calling user is an active member of, sorted by name, with the user's role in each. # List the audit log of one cluster Source: https://docs.ankra.ai/api-reference/organisation/list-the-audit-log-of-one-cluster https://platform.ankra.app/openapi.json get /org/clusters/{cluster_id}/audit-logs Returns one cluster's slice of the organisation audit log, paginated with `page` and `page_size` (default 50) and filterable like the organisation listing. Requires `audit.read`; returns 404 when the cluster is not in the organisation. # Remove a claimed email domain Source: https://docs.ankra.ai/api-reference/organisation/remove-a-claimed-email-domain https://platform.ankra.app/openapi.json delete /org/organisation/{organisation_id}/sso/domains/{domain_id} Removes a claimed domain. The last verified domain of an enabled connection cannot be removed, because every sign-in through the connection would then be refused. # Remove organisation single sign-on Source: https://docs.ankra.ai/api-reference/organisation/remove-organisation-single-sign-on https://platform.ankra.app/openapi.json delete /org/organisation/{organisation_id}/sso Removes the organisation's single sign-on connection. Members keep the state they are in - suspended members stay suspended - and accounts that existed only through the connection are released so their owners can sign up again with the same address. # Replace the group-to-role mappings Source: https://docs.ankra.ai/api-reference/organisation/replace-the-group-to-role-mappings https://platform.ankra.app/openapi.json put /org/organisation/{organisation_id}/sso/group-mappings Replaces the group-to-role mappings. While any exist, every sign-in the provider vouches for sets the member's role from the groups the provider asserts: the highest mapped role wins, a member in no mapped group gets the default role, and owner is never mapped. # Store organisation logo Source: https://docs.ankra.ai/api-reference/organisation/store-organisation-logo https://platform.ankra.app/openapi.json put /org/organisation/{organisation_id}/logo Uploads the organisation's logo as the multipart `file` field, replacing any existing one. Accepts PNG, JPEG or WebP up to 512 KiB within a maximum pixel size; requires `organisation.manage`. # Switch user organisation Source: https://docs.ankra.ai/api-reference/organisation/switch-user-organisation https://platform.ankra.app/openapi.json post /api/v1/org/organisation/switch Switches the calling user's active organisation to `organisation_id`. Returns 403 when the user is not permitted to join that organisation. # Update organisation single sign-on policy Source: https://docs.ankra.ai/api-reference/organisation/update-organisation-single-sign-on-policy https://platform.ankra.app/openapi.json patch /org/organisation/{organisation_id}/sso Changes the single sign-on policy: whether it is enabled, whether it is required for members on the verified domains, just-in-time membership and its default role, the re-verification window after which a member the provider no longer vouches for is suspended, and the break-glass members who keep their other sign-in methods. Enabling needs a verified domain; requiring needs one successful single sign-on login first; the break-glass list can never be emptied. # Verify a claimed email domain by its DNS record Source: https://docs.ankra.ai/api-reference/organisation/verify-a-claimed-email-domain-by-its-dns-record https://platform.ankra.app/openapi.json post /org/organisation/{organisation_id}/sso/domains/{domain_id}/verify Looks up the domain's challenge record and marks the domain verified when the record is published. Until then the answer is 422 with the record to publish; DNS changes can take a few minutes to appear. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/ovh-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/vms Adds a standalone VM with the given `name` and `instance_type` on the cluster network; its location defaults to the bastion's. On a running cluster its creation starts at once and the result carries the `operation_id`; on a stopped cluster it is created at the next start. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Add node group Source: https://docs.ankra.ai/api-reference/ovh-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/node-groups Adds a worker node group with the given `name`, `instance_type` and `count` (default 1), plus optional labels, taints and autoscaling settings. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/ovh-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Create an OVH cluster Source: https://docs.ankra.ai/api-reference/ovh-clusters/create-an-ovh-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh Creates an OVH cluster from the request and starts provisioning it in the background, answering 201 with the new cluster's `cluster_id` and `name`. A configuration that fails validation answers 400 with the reason, and an organisation whose billing is restricted is refused with 402. A cluster name already in use answers 409 and an unknown credential 404. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/ovh-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/ovh/{cluster_id}/vms/{vm_id} Deletes a standalone VM: it is handed to teardown and its server is removed at the provider. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Delete node group Source: https://docs.ankra.ai/api-reference/ovh-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/ovh/{cluster_id}/node-groups/{group_name} Removes a node group by draining and deleting its workers one at a time through their PodDisruptionBudgets; a worker whose drain is refused stays in service and the operation fails with the reason. Pass `force_drain=true` to take the whole group down regardless. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. Requires `clusters.delete`. # Deprovision an OVH cluster Source: https://docs.ankra.ai/api-reference/ovh-clusters/deprovision-an-ovh-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/ovh/{cluster_id} Deletes the cluster and its cloud resources. A delete that would remove persistent volumes, or cannot tell whether it does, is refused with 409 unless `accept_volume_data_loss=true` is passed (see the deprovision-volumes read); `force=true` tolerates infrastructure that can no longer be reached. Active operations on the cluster are cancelled and the response carries the teardown's `operation_id`. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/ovh-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. # Diagnose node Source: https://docs.ankra.ai/api-reference/ovh-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `ovh_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get bastion health Source: https://docs.ankra.ai/api-reference/ovh-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. # Get bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/ovh-clusters/get-bastion-ssh-allowlist https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.read`. Returns the cluster's stored bastion SSH source allowlist, as the update on the same path stored it. `configured` tells a cleared list (true, empty) from one that was never set (false, empty); both leave SSH open from anywhere. # Get current Kubernetes version Source: https://docs.ankra.ai/api-reference/ovh-clusters/get-current-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/k8s-version Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Get current worker count Source: https://docs.ankra.ai/api-reference/ovh-clusters/get-current-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/worker-count Returns the cluster's current worker count and the minimum and maximum it can be scaled to. # Get disabled K3s components Source: https://docs.ankra.ai/api-reference/ovh-clusters/get-disabled-k3s-components https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/k3s-disabled-components Read the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), on top of the provider shape's own defaults. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states (a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider) answer 409 with a detail message. # Latest restorable state snapshot of a OVH cluster Source: https://docs.ankra.ai/api-reference/ovh-clusters/latest-restorable-state-snapshot-of-a-ovh-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the OVH cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List node groups Source: https://docs.ankra.ai/api-reference/ovh-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/node-groups Lists the cluster's worker node groups with each group's instance type, node count, autoscaling bounds, labels and taints. # List nodes Source: https://docs.ankra.ai/api-reference/ovh-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/nodes Lists the cluster's machines with each one's role, node group, instance type, location, state, private and public IPs, the kubelet version it reports and, where recorded, the provider's own status. # List OVH flavors available to a credential in a region Source: https://docs.ankra.ai/api-reference/ovh-clusters/list-ovh-flavors-available-to-a-credential-in-a-region https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/flavors Return the OVH Cloud instance flavors the supplied credential's project can deploy in the given region. Only available flavors are valid for cluster creation. # List OVH regions available to a credential Source: https://docs.ankra.ai/api-reference/ovh-clusters/list-ovh-regions-available-to-a-credential https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/regions Return the OVH Cloud regions the supplied credential's project can deploy in. Only these regions are valid for cluster creation. # List standalone VMs Source: https://docs.ankra.ai/api-reference/ovh-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/vms Lists the cluster's standalone VMs (servers on the cluster network that are not Kubernetes nodes) with their instance type, location, state, addresses and provider ID. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/ovh-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/control-plane Returns the control-plane count, the counts the cluster supports, the control planes' instance type, and whether a count or instance-type change is possible right now, with the reason when it is not. # Read node Source: https://docs.ankra.ai/api-reference/ovh-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/nodes/{node_id} Returns one machine of the cluster with its definition, its latest recorded info and data, and its dependencies and relationships. Answers 404 when the node is not part of the cluster. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/ovh-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `ovh_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Read private cluster access Source: https://docs.ankra.ai/api-reference/ovh-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/access-info Returns what you need to reach the cluster's machines over SSH: the bastion's public IP, the control-plane IPs and the cluster name. # Read SSH keys Source: https://docs.ankra.ai/api-reference/ovh-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/ssh-keys Returns the SSH key credentials authorised on the cluster's servers (`ssh_key_credential_ids`) and the organisation's SSH key credentials that can be attached. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/ovh-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/vms/{vm_id}/instance-type Resizes a standalone VM in place to the given `instance_type`. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/ovh-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/control-plane/instance-type Changes the instance type of every control plane. On a running cluster the control planes are resized one at a time behind an etcd quorum check and a drain, and the response carries the `operation_id` to follow (clusters with fewer than three control planes are refused); on a stopped cluster the new type is applied at the next start and no operation is returned. # Resize node group Source: https://docs.ankra.ai/api-reference/ovh-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/node-groups/{group_name}/instance-type Changes the instance type of every worker in the node group. Each node is power-cycled, so the workers are drained and resized in bounded batches; `force_drain` bypasses PodDisruptionBudgets for that drain. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/ovh-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/ssh-keys/resync Re-applies the cluster's configured SSH keys to its servers and returns the IDs of the resources queued for the update. # Scale node group Source: https://docs.ankra.ai/api-reference/ovh-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/node-groups/{group_name}/scale Sets the node group's node count to `count`. A scale-down drains the removed workers through their PodDisruptionBudgets; `force_drain` bypasses them for this request. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Scale OVH cluster workers Source: https://docs.ankra.ai/api-reference/ovh-clusters/scale-ovh-cluster-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/scale-workers Sets the cluster's worker count to `worker_count` and returns the previous and new counts. A scale-down drains the removed workers through their PodDisruptionBudgets unless `force_drain` is true. Requires `clusters.operate`. # Start an OVH cluster Source: https://docs.ankra.ai/api-reference/ovh-clusters/start-an-ovh-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/start Recreate the OVH cloud resources for a stopped cluster and reconcile it back to a running state. # Stop an OVH cluster Source: https://docs.ankra.ai/api-reference/ovh-clusters/stop-an-ovh-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/stop Initiate async deletion of OVH cloud resources (servers, networks, SSH keys) without removing the cluster from the database. # The persistent volumes deleting a OVH cluster removes Source: https://docs.ankra.ai/api-reference/ovh-clusters/the-persistent-volumes-deleting-a-ovh-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/ovh/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/ovh/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. # Update bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-bastion-ssh-allowlist https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.write`. Replaces the cluster's bastion SSH source allowlist and dispatches the bastion update that applies it: the bastion's SSH port then accepts only the listed IPv4 sources, the platform's own egress addresses and private network sources, and drops everything else. An empty list clears the allowlist. Clusters that never set one keep SSH open from anywhere. # Update control-plane count Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/control-plane Changes the number of control planes to `count`. On a running cluster the control plane can only grow, and the new control planes join live (`mode` `rolling`, with an `operation_id` to follow); a reduction is applied offline to a stopped cluster and takes effect at its next start. # Update disabled K3s components Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-disabled-k3s-components https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/k3s-disabled-components Replace the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), for example ["traefik"] on a cluster that brings its own ingress. k3s distribution only. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states answer 409 with a detail message: a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider, a list that drops a stored entry (re-enabling a bundled component is not supported yet, so the list only grows), a list that adds coredns (load-bearing for in-cluster DNS), and any change while a node update, version upgrade or node restart is in progress on the cluster. # Update node-group labels Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/node-groups/{group_name}/labels Replaces the Kubernetes labels on every node in the node group with the given `labels` map. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update node-group taints Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/node-groups/{group_name}/taints Replaces the taints on every node in the node group with the given `taints` list (each a `key`, an optional `value` and an `effect` of `NoSchedule`, `PreferNoSchedule` or `NoExecute`). By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update OVH gateway model Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-ovh-gateway-model https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/gateway-model Invalid states (unsupported model, non-OVH cluster, no recorded managed gateway, no active network resource) answer 409 with a detail message. # Update SSH keys Source: https://docs.ankra.ai/api-reference/ovh-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/ovh/{cluster_id}/ssh-keys Replaces the SSH key credentials authorised on the cluster's servers with `ssh_key_credential_ids` and starts rolling the keys out. An unknown or repeated credential, or two credentials carrying the same public key, is refused with 409. # Upgrade Kubernetes version Source: https://docs.ankra.ai/api-reference/ovh-clusters/upgrade-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/ovh/{cluster_id}/upgrade-k8s-version Upgrades the cluster to `target_version`, which must be in the supported version catalogue and must not downgrade or skip a minor version. The cluster must be online with no change in progress; the nodes are upgraded as a rolling operation whose `operation_id` is returned, and `force` continues past a node drain that PodDisruptionBudgets refuse. # Create an OVH API credential Source: https://docs.ankra.ai/api-reference/ovh-credentials/create-an-ovh-api-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/ovh Creates an OVH credential from `name`, `application_key`, `application_secret`, `consumer_key` and `project_id`. The keys are checked against the OVH API before they are stored in Vault; credentials that fail the check answer 400. Requires `credentials.write`; answers 201. # Create an SSH key credential Source: https://docs.ankra.ai/api-reference/ovh-credentials/create-an-ssh-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/ovh/ssh-key Creates an SSH key credential from `name` and either `ssh_public_key` or `generate=true`; a generated RSA-4096 key pair's private key is returned once, in this response. A public key the organisation already stores under another credential is refused with 400. Requires `credentials.write`; answers 201. # Get OVH credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/ovh-credentials/get-ovh-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/ovh/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the OVH API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get OVH credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/ovh-credentials/get-ovh-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/ovh/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the OVH limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # List OVH API credentials Source: https://docs.ankra.ai/api-reference/ovh-credentials/list-ovh-api-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/ovh Lists the organisation's OVH API credentials with their metadata; the secret material is never returned. # List SSH key credentials Source: https://docs.ankra.ai/api-reference/ovh-credentials/list-ssh-key-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/ovh/ssh-keys Lists the organisation's SSH key credentials. The list is the same under every cloud provider. Each item carries the stored public key with the fingerprint (as `ssh-keygen -l` prints it), key type and comment derived from it; a stored value that is not a readable key keeps its row, with a null `fingerprint` and `fingerprint_error` saying why. # Add a pull request to a repository's merge train Source: https://docs.ankra.ai/api-reference/pipelines/add-a-pull-request-to-a-repositorys-merge-train https://platform.ankra.app/openapi.json post /api/v1/org/pipeline-repositories/{repository_id}/merge-train/entries Puts a pull request at the end of the repository's merge train. Ankra tests it squashed onto the base branch and the pull requests ahead of it, exactly as the merge will land, and merges it through the host's own merge when that run passes. A pull request already in the train answers its entry with 200. A pull request based on the branch of another pull request in the train is stacked on it: tested on its car and merged after it, retargeted onto the base branch first; one based on a pull request that is not in the train is refused with 409. Must be called by a person, not a service account token. The caller must also be an organisation administrator, the pull request's author, or a member with write access to the repository on GitHub, as GitHub reports it for the caller's OAuth-verified GitHub identity. Requires pipelines.operate. # Add a pull request to an application's merge train Source: https://docs.ankra.ai/api-reference/pipelines/add-a-pull-request-to-an-applications-merge-train https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/merge-train/entries Puts a pull request at the end of the repository's merge train. Ankra tests it squashed onto the base branch and the pull requests ahead of it, exactly as the merge will land, and merges it through the host's own merge when that run passes. A pull request already in the train answers its entry with 200. A pull request based on the branch of another pull request in the train is stacked on it: tested on its car and merged after it, retargeted onto the base branch first; one based on a pull request that is not in the train is refused with 409. Addressed by application: the merge train of the pipeline repository the application is connected to. Must be called by a person, not a service account token. The caller must also be an organisation administrator, the pull request's author, or a member with write access to the repository on GitHub, as GitHub reports it for the caller's OAuth-verified GitHub identity. Requires pipelines.operate. # Add or re-weight a CI pool member Source: https://docs.ankra.ai/api-reference/pipelines/add-or-re-weight-a-ci-pool-member https://platform.ankra.app/openapi.json put /api/v1/org/ci-settings/pool/{cluster_id} Lists one cluster in the organisation's CI pool, or changes its weight. The cluster must be a live cluster of this organisation whose agent advertises pipeline steps, and in no other organisation's pool. Answers 201 when the cluster was newly listed. Organisation admins only. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication; writes also need the X-Ankra-CSRF header). # Change a repository's CI cluster Source: https://docs.ankra.ai/api-reference/pipelines/change-a-repositorys-ci-cluster https://platform.ankra.app/openapi.json patch /api/v1/org/pipelines/repositories/{repository_id} Changes a connected repository's CI cluster override (or clears it with null), its Workspaces-only mode, or both, without disconnecting the repository. The cluster must be a live cluster of this organisation whose agent advertises pipeline steps. It changes where the repository's next runs go: runs already queued or running keep the cluster they were pinned to. A Workspaces-only repository starts no pipeline run from any trigger, posts no commit status or check run and records no pipeline definition, while workspaces keep working; turning the mode on is refused with 409 while a run is queued or running. Needs pipelines.manage. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Compare a repository version with the tracked branch Source: https://docs.ankra.ai/api-reference/pipelines/compare-a-repository-version-with-the-tracked-branch https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-source Resolves an immutable tag through owned successful publish outputs, then reads the source provider graph against the actual tracked branch head. Missing, ambiguous and unavailable evidence has no commit count. Requires pipelines.read. Read-only. # Compare an application version with the tracked branch Source: https://docs.ankra.ai/api-reference/pipelines/compare-an-application-version-with-the-tracked-branch https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-source Resolves an immutable tag through owned successful publish outputs, then reads the source provider graph against the actual tracked branch head. Missing, ambiguous and unavailable evidence has no commit count. Requires pipelines.read. Read-only. # Connect a repository Source: https://docs.ankra.ai/api-reference/pipelines/connect-a-repository https://platform.ankra.app/openapi.json post /api/v1/org/pipelines/repositories Connects a repository to the organisation. By default it becomes a pipeline repository: its committed .ankra/pipeline.yaml is read from the default branch through the named credential and recorded as the definition of record, and pushes, pull requests and tags start runs. With workspaces_only true it is connected for Ankra Workspaces only: the pipeline file is not read, no definition is recorded, no run is started from any trigger and no commit status or check run is posted, which suits a repository whose pipeline already runs in another organisation. A disconnected repository with the same identity is revived. Needs pipelines.manage. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Count application releases in flight Source: https://docs.ankra.ai/api-reference/pipelines/count-application-releases-in-flight https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-release-queue Organisation, repository and application scoped. Requires pipelines.read. Counts do not use pagination; deployment identity never certifies live readiness. No execution output is exposed. # Count repository releases in flight Source: https://docs.ankra.ai/api-reference/pipelines/count-repository-releases-in-flight https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-release-queue Organisation, repository and application scoped. Requires pipelines.read. Counts do not use pagination; deployment identity never certifies live readiness. No execution output is exposed. # Dismiss a pipeline authority request Source: https://docs.ankra.ai/api-reference/pipelines/dismiss-a-pipeline-authority-request https://platform.ankra.app/openapi.json post /api/v1/org/pipelines/authority-requests/{id}/dismiss Declines a pending request with a reason the run's check run and run page then show. Requires pipelines.manage and a human actor: a service-account token is refused. A request already approved, dismissed or superseded answers 409. # Get a repository's merge train Source: https://docs.ankra.ai/api-reference/pipelines/get-a-repositorys-merge-train https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/merge-train The repository's Ankra merge train: whether it is enabled and how many cars it tests at once, the pull requests in it in order with each car's base, tree and pipeline run, and the most recently settled entries. Requires pipelines.read. Read-only. # Get a repository's merge train lead time Source: https://docs.ankra.ai/api-reference/pipelines/get-a-repositorys-merge-train-lead-time https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/merge-train/lead-time How long the repository's merged pull requests took from ready to production over the last day and the last week: p50 and p90 with the count, each segment's spread (admission wait, queue, failed cars, the car that passed, merge, deploy), failed car runs by class, the merges not deployed or not recorded yet, and the three slowest with the segment that held each up. Recorded by the pipeline_lead_time maintenance loop. Requires pipelines.read. Read-only. # Get a repository's test quarantine Source: https://docs.ankra.ai/api-reference/pipelines/get-a-repositorys-test-quarantine https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/quarantine The repository's flaky test quarantine: whether it is on and its thresholds, the tests quarantined now, and the ones released in the last 30 days. A quarantined test still runs and is reported; a failing run step whose only failed tests are quarantined settles as a success. Requires pipelines.read. Read-only. # Get a run's resource suggestions Source: https://docs.ankra.ai/api-reference/pipelines/get-a-runs-resource-suggestions https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-runs/{run_id}/resource-suggestions Right-sizing advice for the stages of the pipeline definition a run was planned from: for each stage, the 95th percentile of what its latest successful attempts used, plus 25% headroom, against what it asks for, with how many samples it rests on. Memory is sized from the kernel's memory peak; a stage whose peak reached its limit is at_limit with no suggestion. CPU is sized from mean cores and suggests the request. Only the caller's organisation's runs are counted. Requires pipelines.read. Read-only: nothing is written back to the definition. # Get a test's history on a repository branch Source: https://docs.ankra.ai/api-reference/pipelines/get-a-tests-history-on-a-repository-branch https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/history One test's outcome in the newest runs of a branch that recorded it, and its flaky and failure rates over them. Requires pipelines.read. Read-only. # Get a test's history on an application branch Source: https://docs.ankra.ai/api-reference/pipelines/get-a-tests-history-on-an-application-branch https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-tests/history One test's outcome in the newest runs of a branch that recorded it, and its flaky and failure rates over them. Requires pipelines.read. Read-only. # Get an application run's resource suggestions Source: https://docs.ankra.ai/api-reference/pipelines/get-an-application-runs-resource-suggestions https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/resource-suggestions The same advice addressed by application. Right-sizing advice for the stages of the pipeline definition a run was planned from: for each stage, the 95th percentile of what its latest successful attempts used, plus 25% headroom, against what it asks for, with how many samples it rests on. Memory is sized from the kernel's memory peak; a stage whose peak reached its limit is at_limit with no suggestion. CPU is sized from mean cores and suggests the request. Only the caller's organisation's runs are counted. Requires pipelines.read. Read-only: nothing is written back to the definition. # Get an application's merge train Source: https://docs.ankra.ai/api-reference/pipelines/get-an-applications-merge-train https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/merge-train The repository's Ankra merge train: whether it is enabled and how many cars it tests at once, the pull requests in it in order with each car's base, tree and pipeline run, and the most recently settled entries. Addressed by application: the merge train of the pipeline repository the application is connected to. Requires pipelines.read. Read-only. # Get an application's merge train lead time Source: https://docs.ankra.ai/api-reference/pipelines/get-an-applications-merge-train-lead-time https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/merge-train/lead-time How long the repository's merged pull requests took from ready to production over the last day and the last week: p50 and p90 with the count, each segment's spread (admission wait, queue, failed cars, the car that passed, merge, deploy), failed car runs by class, the merges not deployed or not recorded yet, and the three slowest with the segment that held each up. Recorded by the pipeline_lead_time maintenance loop. Requires pipelines.read. Read-only. Addressed by application: the lead time of the pipeline repository the application is connected to. # Get an application's test quarantine Source: https://docs.ankra.ai/api-reference/pipelines/get-an-applications-test-quarantine https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-tests/quarantine The repository's flaky test quarantine: whether it is on and its thresholds, the tests quarantined now, and the ones released in the last 30 days. A quarantined test still runs and is reported; a failing run step whose only failed tests are quarantined settles as a success. Requires pipelines.read. Read-only. # Get the recorded test timings of a repository's stage Source: https://docs.ankra.ai/api-reference/pipelines/get-the-recorded-test-timings-of-a-repositorys-stage https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/timings The mean recorded duration of each file or test of one stage across the newest runs of a branch, for a sharding script to balance by. Requires pipelines.read. Read-only. # Get the recorded test timings of an application's stage Source: https://docs.ankra.ai/api-reference/pipelines/get-the-recorded-test-timings-of-an-applications-stage https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-tests/timings The mean recorded duration of each file or test of one stage across the newest runs of a branch, for a sharding script to balance by. Requires pipelines.read. Read-only. # Get the test results of a repository pipeline run Source: https://docs.ankra.ai/api-reference/pipelines/get-the-test-results-of-a-repository-pipeline-run https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-runs/{run_id}/tests The test reports the run's steps declared with test_results and what became of each, the tests by outcome across them, and the failed, flaky and slowest tests. Requires pipelines.read. Read-only. # Get the test results of an application pipeline run Source: https://docs.ankra.ai/api-reference/pipelines/get-the-test-results-of-an-application-pipeline-run https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/tests The test reports the run's steps declared with test_results and what became of each, the tests by outcome across them, and the failed, flaky and slowest tests. Requires pipelines.read. Read-only. # Get the time a repository's pipeline saved Source: https://docs.ankra.ai/api-reference/pipelines/get-the-time-a-repositorys-pipeline-saved https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-time-saved The CI time one repository's runs did not spend over the last 7 or 30 days, in total, by reason and by UTC day. Each saving is estimated when the run is planned or skipped, from the stage's own history in the same repository: the median of its newest 20 successful runs among the repository's newest 300 concluded runs (a promoted step uses its duration in the run it was promoted from; a whole run uses the median total of the newest 20 successful runs of the same trigger). CPU-minutes are requested CPU times duration. A stage with no history is counted as unknown, never as a saving. The time is runner time, not wall-clock time. Requires pipelines.read. Read-only. # Get the time a run saved Source: https://docs.ankra.ai/api-reference/pipelines/get-the-time-a-run-saved https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-runs/{run_id}/time-saved The CI time one run did not spend: the stages a path filter left out, the stages skipped because a stage they need was left out, the steps a promoted publish did not run again, or the whole run when its trigger's paths excluded it. Each saving is estimated when the run is planned or skipped, from the stage's own history in the same repository: the median of its newest 20 successful runs among the repository's newest 300 concluded runs (a promoted step uses its duration in the run it was promoted from; a whole run uses the median total of the newest 20 successful runs of the same trigger). CPU-minutes are requested CPU times duration. A stage with no history is counted as unknown, never as a saving. The time is runner time, not wall-clock time. Requires pipelines.read. Read-only. # Get the time an application run saved Source: https://docs.ankra.ai/api-reference/pipelines/get-the-time-an-application-run-saved https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/time-saved The same savings addressed by application. Each saving is estimated when the run is planned or skipped, from the stage's own history in the same repository: the median of its newest 20 successful runs among the repository's newest 300 concluded runs (a promoted step uses its duration in the run it was promoted from; a whole run uses the median total of the newest 20 successful runs of the same trigger). CPU-minutes are requested CPU times duration. A stage with no history is counted as unknown, never as a saving. The time is runner time, not wall-clock time. Requires pipelines.read. Read-only. # Get the time an application's pipeline saved Source: https://docs.ankra.ai/api-reference/pipelines/get-the-time-an-applications-pipeline-saved https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-time-saved The same window summary addressed by application. Each saving is estimated when the run is planned or skipped, from the stage's own history in the same repository: the median of its newest 20 successful runs among the repository's newest 300 concluded runs (a promoted step uses its duration in the run it was promoted from; a whole run uses the median total of the newest 20 successful runs of the same trigger). CPU-minutes are requested CPU times duration. A stage with no history is counted as unknown, never as a saving. The time is runner time, not wall-clock time. Requires pipelines.read. Read-only. # Get the time the organisation's pipelines saved Source: https://docs.ankra.ai/api-reference/pipelines/get-the-time-the-organisations-pipelines-saved https://platform.ankra.app/openapi.json get /api/v1/org/pipelines/time-saved The CI time every pipeline repository the caller may see did not spend over the last 7 or 30 days, in total, by reason, by repository and by UTC day. Each saving is estimated when the run is planned or skipped, from the stage's own history in the same repository: the median of its newest 20 successful runs among the repository's newest 300 concluded runs (a promoted step uses its duration in the run it was promoted from; a whole run uses the median total of the newest 20 successful runs of the same trigger). CPU-minutes are requested CPU times duration. A stage with no history is counted as unknown, never as a saving. The time is runner time, not wall-clock time. Requires pipelines.read. Read-only. # List a repository's pipeline caches Source: https://docs.ankra.ai/api-reference/pipelines/list-a-repositorys-pipeline-caches https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-caches The keyed cache volumes a pipeline repository keeps between runs, most recently used first by keyset. Each cache carries the cluster its volume lives on, when it lapses the organisation's cache retention, and whether its deletion has already been dispatched. Requires pipelines.read. Read-only. # List a repository's test quarantine candidates Source: https://docs.ankra.ai/api-reference/pipelines/list-a-repositorys-test-quarantine-candidates https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/quarantine/candidates The tests of the repository's default branch that reach its quarantine thresholds over the last 30 days and are not quarantined, most flaky first. Only the branch's own runs count, never a pull request check. Requires pipelines.read. Read-only. # List an application's pipeline caches Source: https://docs.ankra.ai/api-reference/pipelines/list-an-applications-pipeline-caches https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-caches The same listing addressed by application: the keyed cache volumes of the pipeline repository the application is connected to, most recently used first by keyset. Requires pipelines.read. Read-only. # List an application's test quarantine candidates Source: https://docs.ankra.ai/api-reference/pipelines/list-an-applications-test-quarantine-candidates https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-tests/quarantine/candidates The tests of the repository's default branch that reach its quarantine thresholds over the last 30 days and are not quarantined, most flaky first. Only the branch's own runs count, never a pull request check. Requires pipelines.read. Read-only. # List connected pipeline repositories Source: https://docs.ankra.ai/api-reference/pipelines/list-connected-pipeline-repositories https://platform.ankra.app/openapi.json get /api/v1/org/pipelines/repositories Pages the organisation's connected repositories, newest first. provider, owner and name narrow the listing, so provider=github&owner=&name= finds the repository a checkout's origin names, with its merge train settings, in one request. Needs pipelines.read. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # List pipeline authority requests Source: https://docs.ankra.ai/api-reference/pipelines/list-pipeline-authority-requests https://platform.ankra.app/openapi.json get /api/v1/org/pipelines/authority-requests The approvals inbox: every request a pipeline run raised for protected settings (services, network, caches, resources, timeouts, permissions, secrets) no administrator has approved, newest first by keyset. Filter by status, repository or application. Requires pipelines.read. Approve one through POST /api/v1/org/pipelines/definitions/{definition_id}/approve; dismiss one through its dismiss route. # Move a pull request to the front of a repository's merge train Source: https://docs.ankra.ai/api-reference/pipelines/move-a-pull-request-to-the-front-of-a-repositorys-merge-train https://platform.ankra.app/openapi.json post /api/v1/org/pipeline-repositories/{repository_id}/merge-train/entries/{entry_id}/front Puts a pull request ahead of every car in the merge train that is still queued, building or testing. Cars that already passed or are being merged ahead of it stay ahead. Every car it passes is rebuilt on top of it and its running test is stopped, so only an organisation administrator may move a pull request to the front, and a service account token is refused. An entry already at the front is answered unchanged. Requires pipelines.operate and organisation.manage. # Move a pull request to the front of an application's merge train Source: https://docs.ankra.ai/api-reference/pipelines/move-a-pull-request-to-the-front-of-an-applications-merge-train https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/merge-train/entries/{entry_id}/front Puts a pull request ahead of every car in the merge train that is still queued, building or testing. Cars that already passed or are being merged ahead of it stay ahead. Every car it passes is rebuilt on top of it and its running test is stopped, so only an organisation administrator may move a pull request to the front, and a service account token is refused. An entry already at the front is answered unchanged. Requires pipelines.operate and organisation.manage. # Quarantine a flaky test of a repository Source: https://docs.ankra.ai/api-reference/pipelines/quarantine-a-flaky-test-of-a-repository https://platform.ankra.app/openapi.json post /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/quarantine Quarantines one test of the repository. The quarantine must be on, and the test's default-branch record over the last 30 days must reach the repository's thresholds (min_flaky_rate over at least min_samples observed runs); its suite, name and file are copied from its newest recorded run. The decision is audited. Requires pipelines.manage. # Quarantine a flaky test of an application Source: https://docs.ankra.ai/api-reference/pipelines/quarantine-a-flaky-test-of-an-application https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/pipeline-tests/quarantine Quarantines one test of the repository. The quarantine must be on, and the test's default-branch record over the last 30 days must reach the repository's thresholds (min_flaky_rate over at least min_samples observed runs); its suite, name and file are copied from its newest recorded run. The decision is audited. Requires pipelines.manage. # Read a pipeline authority request Source: https://docs.ankra.ai/api-reference/pipelines/read-a-pipeline-authority-request https://platform.ankra.app/openapi.json get /api/v1/org/pipelines/authority-requests/{id} One request: the run that raised it, the protected settings it asks for against the approved authority, where it stands and who decided it. Requires pipelines.read. # Read a repository release for a merged pull request Source: https://docs.ankra.ai/api-reference/pipelines/read-a-repository-release-for-a-merged-pull-request https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-runs/{run_id}/release Reads the checked run and provider-confirmed full merge commit, then the exact owned push run on the tracked branch. Failed releases remain visible. A missing exact run is null; a newer commit is never substituted. Requires pipelines.read. Read-only. # Read an application release for a merged pull request Source: https://docs.ankra.ai/api-reference/pipelines/read-an-application-release-for-a-merged-pull-request https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/release Reads the checked run and provider-confirmed full merge commit, then the exact owned push run on the tracked branch. Failed releases remain visible. A missing exact run is null; a newer commit is never substituted. Requires pipelines.read. Read-only. # Read application deployment requests for this exact release Source: https://docs.ankra.ai/api-reference/pipelines/read-application-deployment-requests-for-this-exact-release https://platform.ankra.app/openapi.json get /api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/deployments Organisation, repository and application scoped. Requires pipelines.read. Counts do not use pagination; deployment identity never certifies live readiness. No execution output is exposed. # Read repository deployment requests for this exact release Source: https://docs.ankra.ai/api-reference/pipelines/read-repository-deployment-requests-for-this-exact-release https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/pipeline-runs/{run_id}/deployments Organisation, repository and application scoped. Requires pipelines.read. Counts do not use pagination; deployment identity never certifies live readiness. No execution output is exposed. # Read the CPU and memory of the organisation's CI clusters Source: https://docs.ankra.ai/api-reference/pipelines/read-the-cpu-and-memory-of-the-organisations-ci-clusters https://platform.ankra.app/openapi.json get /api/v1/org/ci-settings/capacity/resources The CPU and memory of the nodes each member of the organisation's CI pool places pipelines on: their summed allocatable, what every pod on them requests, and the part pipeline pods request, from the platform's mirror of each cluster. Members come primary first, as in the capacity read's pool_members. It is read apart from the capacity read because it reads every node and running pod of each cluster; each cluster's answer is reused for up to a minute. A member whose nodes could not be read answers state unknown with every figure null. Readable by any member. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read the organisation's CI cluster pool Source: https://docs.ankra.ai/api-reference/pipelines/read-the-organisations-ci-cluster-pool https://platform.ankra.app/openapi.json get /api/v1/org/ci-settings/pool The clusters the organisation's pipeline runs are spread across, with each member's weight. The declared ci_cluster_id is always a member. Readable by any member. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication; writes also need the X-Ankra-CSRF header). # Read the organisation's CI usage history Source: https://docs.ankra.ai/api-reference/pipelines/read-the-organisations-ci-usage-history https://platform.ankra.app/openapi.json get /api/v1/org/ci-settings/capacity/history How the organisation's CI clusters have been used over a window of 24h, 7d or 30d, per cluster and per time bucket: steps started, the time-weighted average and peak number of steps running, the CPU and memory those steps requested and the CPU they used, and how long they waited for a slot. It is folded from the organisation's pipeline steps on its own clusters (steps Ankra's builders ran are not counted), so it reaches back no further than run_retention_days. A requested or used figure is null when steps ran and none recorded it and zero when nothing ran; request_coverage and usage_coverage give the share of step running time that recorded it. The slot count and node capacity at the time are not recorded; read today's from the capacity route. Readable by any member. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Read the organisation's pipeline capacity Source: https://docs.ankra.ai/api-reference/pipelines/read-the-organisations-pipeline-capacity https://platform.ankra.app/openapi.json get /api/v1/org/ci-settings/capacity How much pipeline work the organisation's pipeline cluster can run at once and how much is waiting. ci_worker_count is the number of CI slots the cluster's agent runs: a slot is one pipeline step the agent watches at a time, not a node, and the nodes those steps land on come from the cluster's node group autoscaler. steps_in_flight_on_cluster counts every step occupying a slot on that cluster. The organisation_* counts are this organisation's own runs and steps: runs queued, runs counted against max_parallel_runs, pending steps, and the pending steps held back because every slot is taken (zero whenever a slot is free). is_live_resize_supported reports whether the cluster's agent applies a changed worker count without restarting. cluster_id and cluster_name are null, and the cluster's numbers zero, when no pipeline cluster is chosen. Readable by any member. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication). # Release a repository's test quarantine Source: https://docs.ankra.ai/api-reference/pipelines/release-a-repositorys-test-quarantine https://platform.ankra.app/openapi.json post /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/quarantine/{quarantine_id}/release Releases one live quarantine of the repository, so the test's failures fail its step again. The release is audited with its optional reason. Requires pipelines.manage. # Release an application's test quarantine Source: https://docs.ankra.ai/api-reference/pipelines/release-an-applications-test-quarantine https://platform.ankra.app/openapi.json post /api/v1/org/applications/{application_id}/pipeline-tests/quarantine/{quarantine_id}/release Releases one live quarantine of the repository, so the test's failures fail its step again. The release is audited with its optional reason. Requires pipelines.manage. # Remove a CI pool member Source: https://docs.ankra.ai/api-reference/pipelines/remove-a-ci-pool-member https://platform.ankra.app/openapi.json delete /api/v1/org/ci-settings/pool/{cluster_id} Unlists one cluster from the organisation's CI pool and answers the pool that remains. Runs already pinned to it finish there. Unlisting the declared ci_cluster_id removes only its weight: it stays the implicit primary member. Organisation admins only. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication; writes also need the X-Ankra-CSRF header). # Remove a pull request from a repository's merge train Source: https://docs.ankra.ai/api-reference/pipelines/remove-a-pull-request-from-a-repositorys-merge-train https://platform.ankra.app/openapi.json delete /api/v1/org/pipeline-repositories/{repository_id}/merge-train/entries/{entry_id} Takes a pull request out of the repository's merge train and stops its car's run. A pull request being merged right now cannot be taken out. The member who put the entry in may take it out; anyone else must be an organisation administrator, the pull request's author, or a member with write access to the repository on GitHub, as GitHub reports it for the caller's OAuth-verified GitHub identity. Requires pipelines.operate. # Remove a pull request from an application's merge train Source: https://docs.ankra.ai/api-reference/pipelines/remove-a-pull-request-from-an-applications-merge-train https://platform.ankra.app/openapi.json delete /api/v1/org/applications/{application_id}/merge-train/entries/{entry_id} Takes a pull request out of the repository's merge train and stops its car's run. A pull request being merged right now cannot be taken out. Addressed by application: the merge train of the pipeline repository the application is connected to. The member who put the entry in may take it out; anyone else must be an organisation administrator, the pull request's author, or a member with write access to the repository on GitHub, as GitHub reports it for the caller's OAuth-verified GitHub identity. Requires pipelines.operate. # Replace the organisation's CI cluster pool Source: https://docs.ankra.ai/api-reference/pipelines/replace-the-organisations-ci-cluster-pool https://platform.ankra.app/openapi.json put /api/v1/org/ci-settings/pool Makes the listed members exactly the given list, in one transaction. Every member must be a live cluster of this organisation whose agent advertises pipeline steps, and in no other organisation's pool. Runs already pinned to an unlisted member finish there. Organisation admins only. A browser session twin is mounted at the same path without the /api/v1 prefix (cookie authentication; writes also need the X-Ankra-CSRF header). # Review a pipeline definition's authority before approval Source: https://docs.ankra.ai/api-reference/pipelines/review-a-pipeline-definitions-authority-before-approval https://platform.ankra.app/openapi.json get /api/v1/org/pipelines/definitions/{id}/review What an approver reads before approving a definition's protected sections: exactly which authority it declares, how that compares with the approved authority runs fall back to, the default-branch run it was read from, and whether the approve route would accept it now. Requires pipelines.read. Read-only. Literal values that can carry a credential (a stage's with: settings, a service's env values, a webhook URL's path and query) are withheld, and the stored pipeline YAML is never returned. # Set a repository's merge train settings Source: https://docs.ankra.ai/api-reference/pipelines/set-a-repositorys-merge-train-settings https://platform.ankra.app/openapi.json put /api/v1/org/pipeline-repositories/{repository_id}/merge-train/settings Switches the repository's merge train on or off, sets how many pull requests it tests at once, puts auto-admit in off, shadow or on, and sets the criterion it admits by (green, ready_shadow or ready) and the paths that keep green under ready. Switching the train off empties it: every pull request in it leaves, and its car's run stops. GitHub repositories only. Requires pipelines.manage; switching auto-admit on, its criterion to ready, or its protected paths also needs a human member, not a service account token. # Set a repository's test quarantine settings Source: https://docs.ankra.ai/api-reference/pipelines/set-a-repositorys-test-quarantine-settings https://platform.ankra.app/openapi.json put /api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/quarantine/settings Turns the repository's flaky test quarantine on or off and sets its thresholds. Every field is optional and an omitted one keeps its stored value. The change is audited with the values before and after. Requires pipelines.manage. # Set an application's merge train settings Source: https://docs.ankra.ai/api-reference/pipelines/set-an-applications-merge-train-settings https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/merge-train/settings Switches the repository's merge train on or off, sets how many pull requests it tests at once, puts auto-admit in off, shadow or on, and sets the criterion it admits by (green, ready_shadow or ready) and the paths that keep green under ready. Switching the train off empties it: every pull request in it leaves, and its car's run stops. Addressed by application: the merge train of the pipeline repository the application is connected to. GitHub repositories only. Requires pipelines.manage; switching auto-admit on, its criterion to ready, or its protected paths also needs a human member, not a service account token. # Set an application's test quarantine settings Source: https://docs.ankra.ai/api-reference/pipelines/set-an-applications-test-quarantine-settings https://platform.ankra.app/openapi.json put /api/v1/org/applications/{application_id}/pipeline-tests/quarantine/settings Turns the repository's flaky test quarantine on or off and sets its thresholds. Every field is optional and an omitted one keeps its stored value. The change is audited with the values before and after. Requires pipelines.manage. # Get Proxmox VE credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/proxmox-credentials/get-proxmox-ve-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/proxmox/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the Proxmox VE API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get Proxmox VE credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/proxmox-credentials/get-proxmox-ve-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/proxmox/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the Proxmox VE limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/scaleway-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/vms Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Add a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/add-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/vms Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Add node group Source: https://docs.ankra.ai/api-reference/scaleway-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/node-groups Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Add node group (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/add-node-group-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/node-groups Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/scaleway-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Check Kubernetes upgrade readiness (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/check-kubernetes-upgrade-readiness-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Create a Scaleway Instances cluster Source: https://docs.ankra.ai/api-reference/scaleway-clusters/create-a-scaleway-instances-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway Bearer PAT authentication; RBAC permission `clusters.create`. External cloud provider mode is mandatory; kubeadm accepts only Cilium. # Create a Scaleway Instances cluster (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/create-a-scaleway-instances-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway Browser session authentication; RBAC permission `clusters.create`. External cloud provider mode is mandatory; kubeadm accepts only Cilium. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/scaleway-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/scaleway/{cluster_id}/vms/{vm_id} Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Delete a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/delete-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/scaleway/{cluster_id}/vms/{vm_id} Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Delete node group Source: https://docs.ankra.ai/api-reference/scaleway-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name} Bearer PAT authentication; RBAC permission `clusters.delete`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Delete node group (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/delete-node-group-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/scaleway/{cluster_id}/node-groups/{group_name} Browser session authentication; RBAC permission `clusters.delete`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Deprovision a Scaleway Instances cluster Source: https://docs.ankra.ai/api-reference/scaleway-clusters/deprovision-a-scaleway-instances-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/scaleway/{cluster_id} Bearer PAT authentication; RBAC permission `clusters.delete`. Ordered teardown preserves explicitly retained storage and reports orphaned provider IDs. # Deprovision a Scaleway Instances cluster (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/deprovision-a-scaleway-instances-cluster-browser-session https://platform.ankra.app/openapi.json delete /org/clusters/scaleway/{cluster_id} Browser session authentication; RBAC permission `clusters.delete`. Ordered teardown preserves explicitly retained storage and reports orphaned provider IDs. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/scaleway-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. # Diagnose bastion (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/diagnose-bastion-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/bastion/diagnose Browser session authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. # Diagnose node Source: https://docs.ankra.ai/api-reference/scaleway-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `scaleway_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get bastion health Source: https://docs.ankra.ai/api-reference/scaleway-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. # Get bastion health (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/get-bastion-health-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/bastion/health Browser session authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. # Get disabled K3s components Source: https://docs.ankra.ai/api-reference/scaleway-clusters/get-disabled-k3s-components https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/k3s-disabled-components Read the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), on top of the provider shape's own defaults. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states (a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider) answer 409 with a detail message. # Latest restorable state snapshot of a Scaleway cluster Source: https://docs.ankra.ai/api-reference/scaleway-clusters/latest-restorable-state-snapshot-of-a-scaleway-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the Scaleway cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List live cluster Scaleway gateway types Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-live-cluster-scaleway-gateway-types https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/gateway-types Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List live cluster Scaleway gateway types (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-live-cluster-scaleway-gateway-types-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/gateway-types Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List live cluster Scaleway instance types Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-live-cluster-scaleway-instance-types https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/instance-types Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List live cluster Scaleway instance types (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-live-cluster-scaleway-instance-types-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/instance-types Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List node groups Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/node-groups Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # List node groups (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-node-groups-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/node-groups Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # List nodes Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/nodes Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # List nodes (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-nodes-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/nodes Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # List Scaleway gateway types Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-gateway-types https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/gateway-types Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway gateway types (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-gateway-types-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/gateway-types Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway instance types Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-instance-types https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/instance-types Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway instance types (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-instance-types-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/instance-types Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway locations Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-locations https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/locations Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway locations (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-locations-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/locations Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway networks Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-networks https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/networks Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway networks (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-networks-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/networks Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway pricing Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-pricing https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/pricing Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway pricing (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-pricing-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/pricing Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway regions Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-regions https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/regions Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway regions (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-regions-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/regions Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway zones Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-zones https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/zones Bearer PAT authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List Scaleway zones (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-scaleway-zones-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/zones Browser session authentication; RBAC permission `clusters.read`. Catalog prices carry currency/SKU metadata and explicit incomplete-pricing reasons. # List standalone VMs Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/vms Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # List standalone VMs (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/list-standalone-vms-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/vms Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Preflight a Scaleway Instances cluster Source: https://docs.ankra.ai/api-reference/scaleway-clusters/preflight-a-scaleway-instances-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/preflight Bearer PAT authentication; RBAC permission `clusters.create`. External cloud provider mode is mandatory; kubeadm accepts only Cilium. # Preflight a Scaleway Instances cluster (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/preflight-a-scaleway-instances-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/preflight Browser session authentication; RBAC permission `clusters.create`. External cloud provider mode is mandatory; kubeadm accepts only Cilium. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/control-plane Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read control-plane sizing (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-control-plane-sizing-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/control-plane Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read Kubernetes version Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/k8s-version Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read Kubernetes version (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-kubernetes-version-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/k8s-version Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read node Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/nodes/{node_id} Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read node (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-node-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/nodes/{node_id} Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `scaleway_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Read node-group autoscaling Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-node-group-autoscaling https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name}/autoscaling Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read node-group autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-node-group-autoscaling-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/node-groups/{group_name}/autoscaling Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read private cluster access Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/access-info Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read private cluster access (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-private-cluster-access-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/access-info Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read SSH keys Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/ssh-keys Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-ssh-keys-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/ssh-keys Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read worker count Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/worker-count Bearer PAT authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Read worker count (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/read-worker-count-browser-session https://platform.ankra.app/openapi.json get /org/clusters/scaleway/{cluster_id}/worker-count Browser session authentication; RBAC permission `clusters.read`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/vms/{vm_id}/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize a standalone VM (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-a-standalone-vm-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/vms/{vm_id}/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/control-plane/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize control-plane nodes (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-control-plane-nodes-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/control-plane/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize node group Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name}/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize node group (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-node-group-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/node-groups/{group_name}/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize the public gateway Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-the-public-gateway https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/bastion/instance-type Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resize the public gateway (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resize-the-public-gateway-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/bastion/instance-type Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Restart node Source: https://docs.ankra.ai/api-reference/scaleway-clusters/restart-node https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/nodes/{node_id}/restart Bearer PAT authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Restart node (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/restart-node-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/nodes/{node_id}/restart Browser session authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/ssh-keys/resync Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Resync SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/resync-ssh-keys-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/ssh-keys/resync Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Scale node group Source: https://docs.ankra.ai/api-reference/scaleway-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name}/scale Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Scale node group (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/scale-node-group-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/node-groups/{group_name}/scale Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Scale workers Source: https://docs.ankra.ai/api-reference/scaleway-clusters/scale-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/scale-workers Bearer PAT authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Scale workers (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/scale-workers-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/scale-workers Browser session authentication; RBAC permission `clusters.operate`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Start a stopped cluster Source: https://docs.ankra.ai/api-reference/scaleway-clusters/start-a-stopped-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/start Bearer PAT authentication; RBAC permission `clusters.operate`. # Start a stopped cluster (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/start-a-stopped-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/start Browser session authentication; RBAC permission `clusters.operate`. # Stop a cluster Source: https://docs.ankra.ai/api-reference/scaleway-clusters/stop-a-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/stop Bearer PAT authentication; RBAC permission `clusters.operate`. # Stop a cluster (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/stop-a-cluster-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/stop Browser session authentication; RBAC permission `clusters.operate`. # The persistent volumes deleting a Scaleway cluster removes Source: https://docs.ankra.ai/api-reference/scaleway-clusters/the-persistent-volumes-deleting-a-scaleway-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/scaleway/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/scaleway/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. Answers 404 while the organisation's `scaleway_provider` feature flag is off. # Update control-plane count Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/control-plane Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update control-plane count (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-control-plane-count-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/control-plane Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update disabled K3s components Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-disabled-k3s-components https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/k3s-disabled-components Replace the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), for example ["traefik"] on a cluster that brings its own ingress. k3s distribution only. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states answer 409 with a detail message: a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider, a list that drops a stored entry (re-enabling a bundled component is not supported yet, so the list only grows), a list that adds coredns (load-bearing for in-cluster DNS), and any change while a node update, version upgrade or node restart is in progress on the cluster. # Update node-group autoscaling Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-node-group-autoscaling https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name}/autoscaling Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update node-group autoscaling (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-node-group-autoscaling-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/node-groups/{group_name}/autoscaling Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update node-group labels Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name}/labels Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update node-group labels (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-node-group-labels-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/node-groups/{group_name}/labels Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update node-group taints Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/node-groups/{group_name}/taints Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update node-group taints (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-node-group-taints-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/node-groups/{group_name}/taints Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update SSH keys Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/scaleway/{cluster_id}/ssh-keys Bearer PAT authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Update SSH keys (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/update-ssh-keys-browser-session https://platform.ankra.app/openapi.json put /org/clusters/scaleway/{cluster_id}/ssh-keys Browser session authentication; RBAC permission `clusters.write`. PAT mutations support `wait=false` where implemented; browser mutations require CSRF. # Upgrade the Kubernetes version Source: https://docs.ankra.ai/api-reference/scaleway-clusters/upgrade-the-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/scaleway/{cluster_id}/upgrade-k8s-version Bearer PAT authentication; RBAC permission `clusters.operate`. # Upgrade the Kubernetes version (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-clusters/upgrade-the-kubernetes-version-browser-session https://platform.ankra.app/openapi.json post /org/clusters/scaleway/{cluster_id}/upgrade-k8s-version Browser session authentication; RBAC permission `clusters.operate`. # Create a Scaleway credential Source: https://docs.ankra.ai/api-reference/scaleway-credentials/create-a-scaleway-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/scaleway Bearer PAT authentication; RBAC permission `credentials.write`. The secret is stored in Vault and connection-tested without echoing key material. # Create a Scaleway credential (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-credentials/create-a-scaleway-credential-browser-session https://platform.ankra.app/openapi.json post /org/credentials/scaleway Browser session authentication; RBAC permission `credentials.write`. The secret is stored in Vault and connection-tested without echoing key material. # Create an SSH key credential Source: https://docs.ankra.ai/api-reference/scaleway-credentials/create-an-ssh-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/scaleway/ssh-key Creates an SSH key credential from `name` and either `ssh_public_key` or `generate=true`; a generated RSA-4096 key pair's private key is returned once, in this response. A public key the organisation already stores under another credential is refused with 400. Requires `credentials.write`; answers 201. # Get Scaleway credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-credentials/get-scaleway-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/scaleway/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the Scaleway API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. Answers 404 while the organisation's `scaleway_provider` feature flag is off. # Get Scaleway credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-credentials/get-scaleway-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/scaleway/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the Scaleway limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. Answers 404 while the organisation's `scaleway_provider` feature flag is off. # List Scaleway credentials Source: https://docs.ankra.ai/api-reference/scaleway-credentials/list-scaleway-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/scaleway Bearer PAT authentication; RBAC permission `credentials.read`. Returns credential metadata only; access_key and secret_key are never returned. # List Scaleway credentials (browser session) Source: https://docs.ankra.ai/api-reference/scaleway-credentials/list-scaleway-credentials-browser-session https://platform.ankra.app/openapi.json get /org/credentials/scaleway Browser session authentication; RBAC permission `credentials.read`. Returns credential metadata only; access_key and secret_key are never returned. # List SSH key credentials Source: https://docs.ankra.ai/api-reference/scaleway-credentials/list-ssh-key-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/scaleway/ssh-keys Lists the organisation's SSH key credentials. The list is the same under every cloud provider. Each item carries the stored public key with the fingerprint (as `ssh-keygen -l` prints it), key type and comment derived from it; a stored value that is not a readable key keeps its row, with a null `fingerprint` and `fingerprint_error` saying why. # Create a secret slot Source: https://docs.ankra.ai/api-reference/secret-slots/create-a-secret-slot https://platform.ankra.app/openapi.json post /api/v1/org/secret-slots Stores a secret in Vault and returns its slot, whose sentinel ${SECRET_SLOT:} can stand in for the value in manifests, addon values and MCP server env. Send either value or generate: generate has Ankra create a random value that nobody sees. The value is never returned. # Delete a secret slot Source: https://docs.ankra.ai/api-reference/secret-slots/delete-a-secret-slot https://platform.ankra.app/openapi.json delete /api/v1/org/secret-slots/{slot_id} Deletes the slot and its stored value. A slot that live resources still reference is refused, and the refusal names the first of them. # List secret slots Source: https://docs.ankra.ai/api-reference/secret-slots/list-secret-slots https://platform.ankra.app/openapi.json get /api/v1/org/secret-slots Lists the organisation's secret slots, newest first, with each slot's sentinel and the live resources that reference it. Values are stored in Vault and are never returned. # Replace a secret slot value Source: https://docs.ankra.ai/api-reference/secret-slots/replace-a-secret-slot-value https://platform.ankra.app/openapi.json put /api/v1/org/secret-slots/{slot_id} Replaces the slot's value in place. The slot keeps its id, so every ${SECRET_SLOT:} reference stays valid; its label, hint and expiry do not change. Send exactly one of value or generate. The value is never returned. # Cancel a security remediation run Source: https://docs.ankra.ai/api-reference/security/cancel-a-security-remediation-run https://platform.ankra.app/openapi.json post /api/v1/org/security/remediations/{run_id}/cancel Stops a live run; the pull request and any running pipeline are left for their reviewers. A finished run is answered unchanged. # Cancel a security remediation run (browser session) Source: https://docs.ankra.ai/api-reference/security/cancel-a-security-remediation-run-browser-session https://platform.ankra.app/openapi.json post /org/security/remediations/{run_id}/cancel Stops a live run; the pull request and any running pipeline are left for their reviewers. A finished run is answered unchanged. # Create a security evidence destination Source: https://docs.ankra.ai/api-reference/security/create-a-security-evidence-destination https://platform.ankra.app/openapi.json post /api/v1/org/security/evidence/destinations Stores the credential in Vault, then records the destination (a Vanta document, a Wazuh relay or a signed webhook). URLs must be https; addresses are re-checked at dial time. # Create a security evidence destination (browser session) Source: https://docs.ankra.ai/api-reference/security/create-a-security-evidence-destination-browser-session https://platform.ankra.app/openapi.json post /org/security/evidence/destinations Stores the credential in Vault, then records the destination (a Vanta document, a Wazuh relay or a signed webhook). URLs must be https; addresses are re-checked at dial time. # Create code finding disposition Source: https://docs.ankra.ai/api-reference/security/create-code-finding-disposition https://platform.ankra.app/openapi.json post /api/v1/org/security/code-dispositions Records a reviewed Semgrep or Checkov finding of a pipeline repository as a `false_positive` or an `accepted_risk` (which needs `expires_at` within a year), with a required `reason`; the image gate then stops blocking on that rule in that file and names the disposition on every run it lets through. A CRITICAL finding is refused (400), as is a second active disposition of the same finding (409). Requires `security.manage`. # Create code finding disposition (browser session) Source: https://docs.ankra.ai/api-reference/security/create-code-finding-disposition-browser-session https://platform.ankra.app/openapi.json post /org/security/code-dispositions Records a reviewed Semgrep or Checkov finding of a pipeline repository as a `false_positive` or an `accepted_risk` (which needs `expires_at` within a year), with a required `reason`; the image gate then stops blocking on that rule in that file and names the disposition on every run it lets through. A CRITICAL finding is refused (400), as is a second active disposition of the same finding (409). Requires `security.manage`. # Create vulnerability disposition policy Source: https://docs.ankra.ai/api-reference/security/create-vulnerability-disposition-policy https://platform.ankra.app/openapi.json post /api/v1/org/security/vulnerability-dispositions Creates a disposition policy from one finding occurrence (`occurrence_id`) that marks matching vulnerabilities as `acknowledged` or `accepted_risk`, with a required `reason`. Acknowledging needs `security.triage` and accepting risk needs `security.manage` plus an `expires_at` review deadline within one year; returns 409 for a duplicate policy or one that matches nothing unless `allow_unmatched` is set. # Create vulnerability disposition policy (browser session) Source: https://docs.ankra.ai/api-reference/security/create-vulnerability-disposition-policy-browser-session https://platform.ankra.app/openapi.json post /org/security/vulnerability-dispositions Creates a disposition policy from one finding occurrence (`occurrence_id`) that marks matching vulnerabilities as `acknowledged` or `accepted_risk`, with a required `reason`. Acknowledging needs `security.triage` and accepting risk needs `security.manage` plus an `expires_at` review deadline within one year; returns 409 for a duplicate policy or one that matches nothing unless `allow_unmatched` is set. # Daily security posture trend of one cluster Source: https://docs.ankra.ai/api-reference/security/daily-security-posture-trend-of-one-cluster https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/trivy/history Returns the cluster's daily vulnerability posture snapshots, oldest first: finding counts by severity, fixable critical and high counts, workloads, namespaces and risk score. `days` sets the window (default 90, at most 365); a cluster with no snapshots yet returns an empty list. # Daily security posture trend of one cluster (browser session) Source: https://docs.ankra.ai/api-reference/security/daily-security-posture-trend-of-one-cluster-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/trivy/history Returns the cluster's daily vulnerability posture snapshots, oldest first: finding counts by severity, fixable critical and high counts, workloads, namespaces and risk score. `days` sets the window (default 90, at most 365); a cluster with no snapshots yet returns an empty list. # Download an image's bill of materials Source: https://docs.ankra.ai/api-reference/security/download-an-images-bill-of-materials https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom/image/export Download one image's bill of materials, rebuilt from the stored components: a CycloneDX 1.5 JSON document by default, or a flat CSV. Every component of the image is included, unpaged. # Download an image's bill of materials (browser session) Source: https://docs.ankra.ai/api-reference/security/download-an-images-bill-of-materials-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom/image/export Download one image's bill of materials, rebuilt from the stored components: a CycloneDX 1.5 JSON document by default, or a flat CSV. Every component of the image is included, unpaged. # Email the cluster's security report now Source: https://docs.ankra.ai/api-reference/security/email-the-clusters-security-report-now https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/security/report-schedule/send-now Queues the cluster's security report for immediate delivery to its configured recipients; the email goes out within about a minute. Requires `clusters.write`; returns 404 when the cluster has no report schedule. # Email the cluster's security report now (browser session) Source: https://docs.ankra.ai/api-reference/security/email-the-clusters-security-report-now-browser-session https://platform.ankra.app/openapi.json post /org/clusters/imported/{cluster_id}/security/report-schedule/send-now Queues the cluster's security report for immediate delivery to its configured recipients; the email goes out within about a minute. Requires `clusters.write`; returns 404 when the cluster has no report schedule. # Enable or disable one compliance framework Source: https://docs.ankra.ai/api-reference/security/enable-or-disable-one-compliance-framework https://platform.ankra.app/openapi.json patch /api/v1/org/security/compliance/frameworks/{framework_key} Enables or disables one compliance framework for the organisation (`enabled`); enabling it records its first snapshot immediately. Requires `security.manage`. # Enable or disable one compliance framework (browser session) Source: https://docs.ankra.ai/api-reference/security/enable-or-disable-one-compliance-framework-browser-session https://platform.ankra.app/openapi.json patch /org/security/compliance/frameworks/{framework_key} Enables or disables one compliance framework for the organisation (`enabled`); enabling it records its first snapshot immediately. Requires `security.manage`. # Enable the curated security baseline stack Source: https://docs.ankra.ai/api-reference/security/enable-the-curated-security-baseline-stack https://platform.ankra.app/openapi.json post /api/v1/org/security/clusters/{cluster_id}/enable-baseline Installs the Ankra security baseline stack (trivy-operator and the pod-security manifests) on a cluster through GitOps and returns the stack write result with its commit. Re-running it adds any missing baseline members; returns 409 when the complete baseline is already installed or trivy-operator runs outside it, and requires `security.triage` or `security.manage`. # Enable the curated security baseline stack (browser session) Source: https://docs.ankra.ai/api-reference/security/enable-the-curated-security-baseline-stack-browser-session https://platform.ankra.app/openapi.json post /org/security/clusters/{cluster_id}/enable-baseline Installs the Ankra security baseline stack (trivy-operator and the pod-security manifests) on a cluster through GitOps and returns the stack write result with its commit. Re-running it adds any missing baseline members; returns 409 when the complete baseline is already installed or trivy-operator runs outside it, and requires `security.triage` or `security.manage`. # Export the organisation compliance evidence report Source: https://docs.ankra.ai/api-reference/security/export-the-organisation-compliance-evidence-report https://platform.ankra.app/openapi.json get /api/v1/org/security/compliance-report Downloads the organisation's compliance evidence report (posture, findings, dispositions, audit summary, attestation and compliance) as a JSON or sectioned CSV attachment, chosen with `format`. Requires both `security.read` and `audit.read`; the audit window can be bounded with `start_date` and `end_date`. # Export the organisation compliance evidence report (browser session) Source: https://docs.ankra.ai/api-reference/security/export-the-organisation-compliance-evidence-report-browser-session https://platform.ankra.app/openapi.json get /org/security/compliance-report Downloads the organisation's compliance evidence report (posture, findings, dispositions, audit summary, attestation and compliance) as a JSON or sectioned CSV attachment, chosen with `format`. Requires both `security.read` and `audit.read`; the audit window can be bounded with `start_date` and `end_date`. # Get cluster access posture Source: https://docs.ankra.ai/api-reference/security/get-cluster-access-posture https://platform.ankra.app/openapi.json get /api/v1/org/security/clusters/{cluster_id}/access-posture Evaluates one cluster's Kubernetes access posture from its kube gateway grants: a standing cluster-wide cluster-admin or admin grant, a grant over the organisation's access policy, the creator's grant left unchanged for 90 days on a GitOps cluster, and a grant identity the cluster's apiserver verified can impersonate users, groups or service accounts (any of them or only named ones). Each finding names the grant and the commands that fix it; checks that could not be evaluated are listed under unknowns. Requires `security.read`; grantee emails appear only when the caller also holds `kube_access.manage`, otherwise a grantee is its grant_id and ankra_user_id. # Get cluster access posture (browser session) Source: https://docs.ankra.ai/api-reference/security/get-cluster-access-posture-browser-session https://platform.ankra.app/openapi.json get /org/security/clusters/{cluster_id}/access-posture Evaluates one cluster's Kubernetes access posture from its kube gateway grants: a standing cluster-wide cluster-admin or admin grant, a grant over the organisation's access policy, the creator's grant left unchanged for 90 days on a GitOps cluster, and a grant identity the cluster's apiserver verified can impersonate users, groups or service accounts (any of them or only named ones). Each finding names the grant and the commands that fix it; checks that could not be evaluated are listed under unknowns. Requires `security.read`; grantee emails appear only when the caller also holds `kube_access.manage`, otherwise a grantee is its grant_id and ankra_user_id. # Get imported cluster add-on security posture Source: https://docs.ankra.ai/api-reference/security/get-imported-cluster-add-on-security-posture https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/addons/{addon_name}/security Returns the vulnerability posture of one add-on installed on an imported cluster. Requires `security.read`. # Get imported cluster add-on security posture (browser session) Source: https://docs.ankra.ai/api-reference/security/get-imported-cluster-add-on-security-posture-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/addons/{addon_name}/security Returns the vulnerability posture of one add-on installed on an imported cluster. Requires `security.read`. # Get imported cluster pod security posture Source: https://docs.ankra.ai/api-reference/security/get-imported-cluster-pod-security-posture https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/pods/{namespace}/{pod_name} Returns the security summary of one running pod on an imported cluster: every container with its CVE posture and bill of materials. Requires `security.read`. # Get imported cluster pod security posture (browser session) Source: https://docs.ankra.ai/api-reference/security/get-imported-cluster-pod-security-posture-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/pods/{namespace}/{pod_name} Returns the security summary of one running pod on an imported cluster: every container with its CVE posture and bill of materials. Requires `security.read`. # Get imported cluster stack security posture Source: https://docs.ankra.ai/api-reference/security/get-imported-cluster-stack-security-posture https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/security Returns the security summary of one stack on an imported cluster: its CVE posture, the CISA known-exploited findings among them, the bill-of-materials inventory of its running containers, and the same figures per stack member. Requires `security.read`. # Get imported cluster stack security posture (browser session) Source: https://docs.ankra.ai/api-reference/security/get-imported-cluster-stack-security-posture-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/stacks/{stack_name}/security Returns the security summary of one stack on an imported cluster: its CVE posture, the CISA known-exploited findings among them, the bill-of-materials inventory of its running containers, and the same figures per stack member. Requires `security.read`. # Get one compliance framework report Source: https://docs.ankra.ai/api-reference/security/get-one-compliance-framework-report https://platform.ankra.app/openapi.json get /api/v1/org/security/compliance/frameworks/{framework_key}/report Returns the scored, control-by-control report for one compliance framework with its month-over-month trend: live by default, or for a recorded month with `month=YYYY-MM`. Requires `security.read`; returns 409 when the framework is disabled and 404 when the month was not recorded. # Get one compliance framework report (browser session) Source: https://docs.ankra.ai/api-reference/security/get-one-compliance-framework-report-browser-session https://platform.ankra.app/openapi.json get /org/security/compliance/frameworks/{framework_key}/report Returns the scored, control-by-control report for one compliance framework with its month-over-month trend: live by default, or for a recorded month with `month=YYYY-MM`. Requires `security.read`; returns 409 when the framework is disabled and 404 when the month was not recorded. # Get one imported cluster's aggregated policy violations Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-aggregated-policy-violations https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/policy-violations Returns the cluster's aggregated Kyverno policy report, read live from the cluster: pass, warn and fail counts per policy, the top offending namespaces and report totals, plus the baseline's current audit or enforce mode. Requires `security.read`; answers 503 when the cluster or its agent cannot be reached. # Get one imported cluster's aggregated policy violations (browser session) Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-aggregated-policy-violations-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/policy-violations Returns the cluster's aggregated Kyverno policy report, read live from the cluster: pass, warn and fail counts per policy, the top offending namespaces and report totals, plus the baseline's current audit or enforce mode. Requires `security.read`; answers 503 when the cluster or its agent cannot be reached. # Get one imported cluster's compliance view Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-compliance-view https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/compliance Returns one cluster's compliance benchmark summaries with their failed checks and the aggregated configuration-audit counts, read live from the cluster. Requires `security.read`; answers 503 with `Retry-After` when the cluster or its agent cannot be reached. # Get one imported cluster's compliance view (browser session) Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-compliance-view-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/compliance Returns one cluster's compliance benchmark summaries with their failed checks and the aggregated configuration-audit counts, read live from the cluster. Requires `security.read`; answers 503 with `Retry-After` when the cluster or its agent cannot be reached. # Get one imported cluster's NetworkPolicy over-privilege score Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-networkpolicy-over-privilege-score https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/network-policy-overprivilege Scores how over-privileged the cluster's NetworkPolicies are, with ingress and egress scored separately and a remediation row per finding, computed live from the cluster's policy and workload objects. Requires `security.read`; answers 503 when the cluster or its agent cannot be reached. # Get one imported cluster's NetworkPolicy over-privilege score (browser session) Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-networkpolicy-over-privilege-score-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/network-policy-overprivilege Scores how over-privileged the cluster's NetworkPolicies are, with ingress and egress scored separately and a remediation row per finding, computed live from the cluster's policy and workload objects. Requires `security.read`; answers 503 when the cluster or its agent cannot be reached. # Get one imported cluster's stored security policy mode Source: https://docs.ankra.ai/api-reference/security/get-one-imported-clusters-stored-security-policy-mode https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/policy-mode Returns the security-baseline policy mode the platform has stored for the cluster (`off`, `audit` or `enforce`), read from the database without contacting the cluster, so it answers while the cluster is offline. `off` means the policy engine was switched off and is not installed; `null` means no security baseline is installed. Requires `security.read`. # Get organization security finding detail Source: https://docs.ankra.ai/api-reference/security/get-organization-security-finding-detail https://platform.ankra.app/openapi.json get /api/v1/org/security/findings/{finding_id} Returns one CVE finding with its occurrences, a per-cluster breakdown, the disposition policies that match it, the available fix routes and any base-image bump pull requests already opened for it. Requires `security.read`; returns 404 when the finding does not exist. # Get organization security finding detail (browser session) Source: https://docs.ankra.ai/api-reference/security/get-organization-security-finding-detail-browser-session https://platform.ankra.app/openapi.json get /org/security/findings/{finding_id} Returns one CVE finding with its occurrences, a per-cluster breakdown, the disposition policies that match it, the available fix routes and any base-image bump pull requests already opened for it. Requires `security.read`; returns 404 when the finding does not exist. # Get organization security overview Source: https://docs.ankra.ai/api-reference/security/get-organization-security-overview https://platform.ankra.app/openapi.json get /api/v1/org/security/overview Returns the organisation's fleet vulnerability overview: totals and severity counts, fixable severe and CISA known-exploited counts, scanner coverage, the top remediation candidates and the observed trend. Optionally narrowed with `cluster_id` or `addon_slug`; requires `security.read`. # Get organization security overview (browser session) Source: https://docs.ankra.ai/api-reference/security/get-organization-security-overview-browser-session https://platform.ankra.app/openapi.json get /org/security/overview Returns the organisation's fleet vulnerability overview: totals and severity counts, fixable severe and CISA known-exploited counts, scanner coverage, the top remediation candidates and the observed trend. Optionally narrowed with `cluster_id` or `addon_slug`; requires `security.read`. # Get the organisation compliance rollup Source: https://docs.ankra.ai/api-reference/security/get-the-organisation-compliance-rollup https://platform.ankra.app/openapi.json get /api/v1/org/security/compliance Returns the organisation's compliance rollup: the trivy-operator benchmark pass and fail totals for each cluster. Clusters that cannot be reached appear as not available rather than failing the request; requires `security.read`. # Get the organisation compliance rollup (browser session) Source: https://docs.ankra.ai/api-reference/security/get-the-organisation-compliance-rollup-browser-session https://platform.ankra.app/openapi.json get /org/security/compliance Returns the organisation's compliance rollup: the trivy-operator benchmark pass and fail totals for each cluster. Clusters that cannot be reached appear as not available rather than failing the request; requires `security.read`. # Get the platform advisory page for one CVE Source: https://docs.ankra.ai/api-reference/security/get-the-platform-advisory-page-for-one-cve https://platform.ankra.app/openapi.json get /api/v1/org/security/advisories/{cve_id} The parsed NVD/OSV advisory record for a CVE in one of three states (fetched, pending, missing - an error state carries the reason), the platform's CISA KEV / EPSS intelligence for it, and the organisation's own exposure. A CVE the platform has not read yet is reported pending and queued for the vulnerability_advisories loop, so callers poll until it is fetched or missing. # Get the platform advisory page for one CVE (browser session) Source: https://docs.ankra.ai/api-reference/security/get-the-platform-advisory-page-for-one-cve-browser-session https://platform.ankra.app/openapi.json get /org/security/advisories/{cve_id} The parsed NVD/OSV advisory record for a CVE in one of three states (fetched, pending, missing - an error state carries the reason), the platform's CISA KEV / EPSS intelligence for it, and the organisation's own exposure. A CVE the platform has not read yet is reported pending and queued for the vulnerability_advisories loop, so callers poll until it is fetched or missing. # Images with a bill of materials Source: https://docs.ankra.ai/api-reference/security/images-with-a-bill-of-materials https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom/images Every image with a stored bill of materials: OS, component count, the workloads, clusters and namespaces running it, and the actionable findings the scanner attributes to it. # Images with a bill of materials (browser session) Source: https://docs.ankra.ai/api-reference/security/images-with-a-bill-of-materials-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom/images Every image with a stored bill of materials: OS, component count, the workloads, clusters and namespaces running it, and the actionable findings the scanner attributes to it. # List cluster security posture Source: https://docs.ankra.ai/api-reference/security/list-cluster-security-posture https://platform.ankra.app/openapi.json get /api/v1/org/security/clusters Lists every cluster's current security posture, paginated (`page_size` default 50): scanner and posture status, latest report time, and finding counts by disposition and severity. Filter by `search` or `status` and order with `sort` and `order`; requires `security.read`. # List cluster security posture (browser session) Source: https://docs.ankra.ai/api-reference/security/list-cluster-security-posture-browser-session https://platform.ankra.app/openapi.json get /org/security/clusters Lists every cluster's current security posture, paginated (`page_size` default 50): scanner and posture status, latest report time, and finding counts by disposition and severity. Filter by `search` or `status` and order with `sort` and `order`; requires `security.read`. # List code finding dispositions Source: https://docs.ankra.ai/api-reference/security/list-code-finding-dispositions https://platform.ankra.app/openapi.json get /api/v1/org/security/code-dispositions Lists the organisation's repository code finding dispositions (Semgrep and Checkov false positives and accepted risks the Ankra Pipelines image gate honours), newest first, optionally for one application or pipeline repository. Requires `security.read`. # List code finding dispositions (browser session) Source: https://docs.ankra.ai/api-reference/security/list-code-finding-dispositions-browser-session https://platform.ankra.app/openapi.json get /org/security/code-dispositions Lists the organisation's repository code finding dispositions (Semgrep and Checkov false positives and accepted risks the Ankra Pipelines image gate honours), newest first, optionally for one application or pipeline repository. Requires `security.read`. # List organization security finding occurrences Source: https://docs.ankra.ai/api-reference/security/list-organization-security-finding-occurrences https://platform.ankra.app/openapi.json get /api/v1/org/security/findings/{finding_id}/occurrences Pages one finding's occurrences by the status predicates its row counts with, so every count on the finding row can be expanded into the rows behind it. Resolved occurrences list newest resolution first; every other status lists in the finding detail's workload order. # List organization security finding occurrences (browser session) Source: https://docs.ankra.ai/api-reference/security/list-organization-security-finding-occurrences-browser-session https://platform.ankra.app/openapi.json get /org/security/findings/{finding_id}/occurrences Pages one finding's occurrences by the status predicates its row counts with, so every count on the finding row can be expanded into the rows behind it. Resolved occurrences list newest resolution first; every other status lists in the finding detail's workload order. # List organization security findings Source: https://docs.ankra.ai/api-reference/security/list-organization-security-findings https://platform.ankra.app/openapi.json get /api/v1/org/security/findings Lists the organisation's CVE findings, paginated (`page_size` default 50), with each finding's severity, score, affected clusters and workloads, fixable and disposition counts and exploit intelligence. Filter by `search`, `severity`, `status`, `fixable`, `known_exploited`, `cluster_id`, `addon_slug` or `namespace`, and order with `sort` and `order`; requires `security.read`. # List organization security findings (browser session) Source: https://docs.ankra.ai/api-reference/security/list-organization-security-findings-browser-session https://platform.ankra.app/openapi.json get /org/security/findings Lists the organisation's CVE findings, paginated (`page_size` default 50), with each finding's severity, score, affected clusters and workloads, fixable and disposition counts and exploit intelligence. Filter by `search`, `severity`, `status`, `fixable`, `known_exploited`, `cluster_id`, `addon_slug` or `namespace`, and order with `sort` and `order`; requires `security.read`. # List organization security workloads Source: https://docs.ankra.ai/api-reference/security/list-organization-security-workloads https://platform.ankra.app/openapi.json get /api/v1/org/security/workloads Lists the organisation's scanned workloads, paginated (`page_size` default 50), each with its image, add-on attribution, severity counts by disposition, known-exploited count, priority and risk score. Takes the same filters as the findings list; requires `security.read`. # List organization security workloads (browser session) Source: https://docs.ankra.ai/api-reference/security/list-organization-security-workloads-browser-session https://platform.ankra.app/openapi.json get /org/security/workloads Lists the organisation's scanned workloads, paginated (`page_size` default 50), each with its image, add-on attribution, severity counts by disposition, known-exploited count, priority and risk score. Takes the same filters as the findings list; requires `security.read`. # List security evidence deliveries Source: https://docs.ankra.ai/api-reference/security/list-security-evidence-deliveries https://platform.ankra.app/openapi.json get /api/v1/org/security/evidence/deliveries The organisation's deliveries newest first, optionally for one destination (at most 100). # List security evidence deliveries (browser session) Source: https://docs.ankra.ai/api-reference/security/list-security-evidence-deliveries-browser-session https://platform.ankra.app/openapi.json get /org/security/evidence/deliveries The organisation's deliveries newest first, optionally for one destination (at most 100). # List security evidence destinations Source: https://docs.ankra.ai/api-reference/security/list-security-evidence-destinations https://platform.ankra.app/openapi.json get /api/v1/org/security/evidence/destinations The organisation's evidence destinations; credentials are never returned. # List security evidence destinations (browser session) Source: https://docs.ankra.ai/api-reference/security/list-security-evidence-destinations-browser-session https://platform.ankra.app/openapi.json get /org/security/evidence/destinations The organisation's evidence destinations; credentials are never returned. # List security remediation runs Source: https://docs.ankra.ai/api-reference/security/list-security-remediation-runs https://platform.ankra.app/openapi.json get /api/v1/org/security/remediations The organisation's remediation runs, newest first (at most 100), optionally for one finding or one application. Without application_id only runs of one finding are listed, each naming its finding_id; with application_id the application's batched runs (finding_id null) are listed too, and finding_id then also matches a batched run that fixes it. # List security remediation runs (browser session) Source: https://docs.ankra.ai/api-reference/security/list-security-remediation-runs-browser-session https://platform.ankra.app/openapi.json get /org/security/remediations The organisation's remediation runs, newest first (at most 100), optionally for one finding or one application. Without application_id only runs of one finding are listed, each naming its finding_id; with application_id the application's batched runs (finding_id null) are listed too, and finding_id then also matches a batched run that fixes it. # List the bill-of-materials components across one stack's workloads Source: https://docs.ankra.ai/api-reference/security/list-the-bill-of-materials-components-across-one-stacks-workloads https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/sbom Lists the software components in the images one stack's workloads run, paginated, with per-component finding counts; filter with `search` or `vulnerable`. Requires `security.read`. # List the bill-of-materials components across one stack's workloads (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-bill-of-materials-components-across-one-stacks-workloads-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/sbom Lists the software components in the images one stack's workloads run, paginated, with per-component finding counts; filter with `search` or `vulnerable`. Requires `security.read`. # List the compliance frameworks Source: https://docs.ankra.ai/api-reference/security/list-the-compliance-frameworks https://platform.ankra.app/openapi.json get /api/v1/org/security/compliance/frameworks Lists the compliance frameworks (GDPR, ISO 27001, SOC 2, NIST CSF) with whether each is enabled for the organisation and a live score for every enabled one. Requires `security.read`. # List the compliance frameworks (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-compliance-frameworks-browser-session https://platform.ankra.app/openapi.json get /org/security/compliance/frameworks Lists the compliance frameworks (GDPR, ISO 27001, SOC 2, NIST CSF) with whether each is enabled for the organisation and a live score for every enabled one. Requires `security.read`. # List the CVEs across one stack's workloads Source: https://docs.ankra.ai/api-reference/security/list-the-cves-across-one-stacks-workloads https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/findings Lists the CVEs found in one stack's workloads, paginated, with the same filters, sorting and disposition counts as the organisation findings list; the facets are the stack's own. Requires `security.read`. # List the CVEs across one stack's workloads (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-cves-across-one-stacks-workloads-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/findings Lists the CVEs found in one stack's workloads, paginated, with the same filters, sorting and disposition counts as the organisation findings list; the facets are the stack's own. Requires `security.read`. # List the resources failing one compliance control Source: https://docs.ankra.ai/api-reference/security/list-the-resources-failing-one-compliance-control https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/compliance/checks/{check_id}/resources Lists the Kubernetes objects failing one compliance control, each annotated with the add-on or manifest that deploys it and that resource's stack. Pass `source` (`benchmark` or `config_audit`) and, for benchmark checks, `benchmark_id`; requires `security.read`. # List the resources failing one compliance control (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-resources-failing-one-compliance-control-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/compliance/checks/{check_id}/resources Lists the Kubernetes objects failing one compliance control, each annotated with the add-on or manifest that deploys it and that resource's stack. Pass `source` (`benchmark` or `config_audit`) and, for benchmark checks, `benchmark_id`; requires `security.read`. # List the running pods of a stack with their containers' security posture Source: https://docs.ankra.ai/api-reference/security/list-the-running-pods-of-a-stack-with-their-containers-security-posture https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/pods Lists the running pods of one stack, riskiest first, with each container's CVE posture and bill of materials. Paginated, filterable by `search` and by stack member (`member_resource_id`); requires `security.read`. # List the running pods of a stack with their containers' security posture (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-running-pods-of-a-stack-with-their-containers-security-posture-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/pods Lists the running pods of one stack, riskiest first, with each container's CVE posture and bill of materials. Paginated, filterable by `search` and by stack member (`member_resource_id`); requires `security.read`. # List the security posture of every stack of an imported cluster Source: https://docs.ankra.ai/api-reference/security/list-the-security-posture-of-every-stack-of-an-imported-cluster https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/stacks Every live stack of the cluster with its security posture - what the read could attribute, the CVE posture over the workloads the stack owns, the running containers with and without a bill of materials - plus the outside bucket: everything on the cluster no stack owns, so the rows add up to the cluster. Each stack is attributed exactly as its own security read attributes it. # List the security posture of every stack of an imported cluster (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-security-posture-of-every-stack-of-an-imported-cluster-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/stacks Every live stack of the cluster with its security posture - what the read could attribute, the CVE posture over the workloads the stack owns, the running containers with and without a bill of materials - plus the outside bucket: everything on the cluster no stack owns, so the rows add up to the cluster. Each stack is attributed exactly as its own security read attributes it. # List the vulnerabilities on one image Source: https://docs.ankra.ai/api-reference/security/list-the-vulnerabilities-on-one-image https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom/image/findings List the vulnerabilities (CVEs) named on one image: one row per CVE and installed package version, aggregated across every workload container running the image, with the fixed version when one exists, the CISA KEV / EPSS intelligence, how the occurrences are dispositioned and where it runs. The summary totals the image's active occurrences before search and severity narrow the rows. Answers for an image with or without a bill of materials (image.sbom_status); an image the platform has never seen is not found. # List the vulnerabilities on one image (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-vulnerabilities-on-one-image-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom/image/findings List the vulnerabilities (CVEs) named on one image: one row per CVE and installed package version, aggregated across every workload container running the image, with the fixed version when one exists, the CISA KEV / EPSS intelligence, how the occurrences are dispositioned and where it runs. The summary totals the image's active occurrences before search and severity narrow the rows. Answers for an image with or without a bill of materials (image.sbom_status); an image the platform has never seen is not found. # List the workloads of an imported cluster stack with their security posture Source: https://docs.ankra.ai/api-reference/security/list-the-workloads-of-an-imported-cluster-stack-with-their-security-posture https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/workloads The workloads each member of the stack resolved to directly, with the pods each runs, the CVE occurrences the scanner attributed to it and its running containers with and without a bill of materials. A workload no report names has scanned=false; its counts are then absent, not clean. # List the workloads of an imported cluster stack with their security posture (browser session) Source: https://docs.ankra.ai/api-reference/security/list-the-workloads-of-an-imported-cluster-stack-with-their-security-posture-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/stacks/{stack_name}/security/workloads The workloads each member of the stack resolved to directly, with the pods each runs, the CVE occurrences the scanner attributed to it and its running containers with and without a bill of materials. A workload no report names has scanned=false; its counts are then absent, not clean. # List vulnerability disposition policies Source: https://docs.ankra.ai/api-reference/security/list-vulnerability-disposition-policies https://platform.ankra.app/openapi.json get /api/v1/org/security/vulnerability-dispositions Lists the organisation's vulnerability disposition policies, paginated (`page_size` default 50), each with its selector, disposition, reason, expiry, status and match counts. Filter by `search`, `status` or `disposition` and order with `sort` and `order`; requires `security.read`. # List vulnerability disposition policies (browser session) Source: https://docs.ankra.ai/api-reference/security/list-vulnerability-disposition-policies-browser-session https://platform.ankra.app/openapi.json get /org/security/vulnerability-dispositions Lists the organisation's vulnerability disposition policies, paginated (`page_size` default 50), each with its selector, disposition, reason, expiry, status and match counts. Filter by `search`, `status` or `disposition` and order with `sort` and `order`; requires `security.read`. # One image's bill of materials Source: https://docs.ankra.ai/api-reference/security/one-images-bill-of-materials https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom/image One image by identity (its digest, or repository:tag when the scanner knew no digest): the image, a page of its components with the findings that name them, and every workload container currently running it. # One image's bill of materials (browser session) Source: https://docs.ankra.ai/api-reference/security/one-images-bill-of-materials-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom/image One image by identity (its digest, or repository:tag when the scanner knew no digest): the image, a page of its components with the findings that name them, and every workload container currently running it. # Open base-image bump pull requests for a security finding Source: https://docs.ankra.ai/api-reference/security/open-base-image-bump-pull-requests-for-a-security-finding https://platform.ankra.app/openapi.json post /api/v1/org/security/findings/{finding_id}/rebuild Opens one pull request per affected application repository bumping the Dockerfile base image to the advisory's fixed version, through the application's own GitHub credential, and records each on the finding (served as image_rebuild_pull_requests on the finding detail). Every write goes to a new branch (ankra/security//) plus a pull request, never to the default branch; only base-image FROM lines change; an open Ankra pull request for the same finding and repository is reused, never duplicated. Repositories the plan refuses are answered as refusals with their reason; when no repository could be acted on the call is refused with 409 naming the reasons, as is a finding with no application image occurrence carrying a fixed version. # Open base-image bump pull requests for a security finding (browser session) Source: https://docs.ankra.ai/api-reference/security/open-base-image-bump-pull-requests-for-a-security-finding-browser-session https://platform.ankra.app/openapi.json post /org/security/findings/{finding_id}/rebuild Opens one pull request per affected application repository bumping the Dockerfile base image to the advisory's fixed version, through the application's own GitHub credential, and records each on the finding (served as image_rebuild_pull_requests on the finding detail). Every write goes to a new branch (ankra/security//) plus a pull request, never to the default branch; only base-image FROM lines change; an open Ankra pull request for the same finding and repository is reused, never duplicated. Repositories the plan refuses are answered as refusals with their reason; when no repository could be acted on the call is refused with 409 naming the reasons, as is a finding with no application image occurrence carrying a fixed version. # Plan base-image bump pull requests for a security finding Source: https://docs.ankra.ai/api-reference/security/plan-base-image-bump-pull-requests-for-a-security-finding https://platform.ankra.app/openapi.json get /api/v1/org/security/findings/{finding_id}/rebuild-plan Plans the base-image bump pull requests for a finding whose occurrences are application container images (the image_rebuild fix route): per affected application repository, the Dockerfile FROM lines that would move from the installed version to the advisory's fixed version, the branch and credential the pull request would use, and the open Ankra pull request already there, if any; per image the lane cannot act on, the refusal and its reason (no application builds it, the application has no repository or GitHub credential, the credential cannot read the repository, no Dockerfile, no FROM line pins the installed version). A read: nothing is opened. # Plan base-image bump pull requests for a security finding (browser session) Source: https://docs.ankra.ai/api-reference/security/plan-base-image-bump-pull-requests-for-a-security-finding-browser-session https://platform.ankra.app/openapi.json get /org/security/findings/{finding_id}/rebuild-plan Plans the base-image bump pull requests for a finding whose occurrences are application container images (the image_rebuild fix route): per affected application repository, the Dockerfile FROM lines that would move from the installed version to the advisory's fixed version, the branch and credential the pull request would use, and the open Ankra pull request already there, if any; per image the lane cannot act on, the refusal and its reason (no application builds it, the application has no repository or GitHub credential, the credential cannot read the repository, no Dockerfile, no FROM line pins the installed version). A read: nothing is opened. # Preview code finding disposition Source: https://docs.ankra.ai/api-reference/security/preview-code-finding-disposition https://platform.ankra.app/openapi.json post /api/v1/org/security/code-dispositions/preview Reports the recorded finding a code finding disposition would cover and whether it can be created, writing nothing. Requires `security.manage`. # Preview code finding disposition (browser session) Source: https://docs.ankra.ai/api-reference/security/preview-code-finding-disposition-browser-session https://platform.ankra.app/openapi.json post /org/security/code-dispositions/preview Reports the recorded finding a code finding disposition would cover and whether it can be created, writing nothing. Requires `security.manage`. # Preview vulnerability disposition policy Source: https://docs.ankra.ai/api-reference/security/preview-vulnerability-disposition-policy https://platform.ankra.app/openapi.json post /api/v1/org/security/vulnerability-dispositions/preview Session authentication with CSRF; RBAC permission `security.triage`. Accepted-risk dispositions and previews carrying `policy_id` require `security.manage` instead. # Preview vulnerability disposition policy (browser session) Source: https://docs.ankra.ai/api-reference/security/preview-vulnerability-disposition-policy-browser-session https://platform.ankra.app/openapi.json post /org/security/vulnerability-dispositions/preview Session authentication with CSRF; RBAC permission `security.triage`. Accepted-risk dispositions and previews carrying `policy_id` require `security.manage` instead. # Read a security remediation run Source: https://docs.ankra.ai/api-reference/security/read-a-security-remediation-run https://platform.ankra.app/openapi.json get /api/v1/org/security/remediations/{run_id} One remediation run with its receipt trail. # Read a security remediation run (browser session) Source: https://docs.ankra.ai/api-reference/security/read-a-security-remediation-run-browser-session https://platform.ankra.app/openapi.json get /org/security/remediations/{run_id} One remediation run with its receipt trail. # Revoke a security evidence destination Source: https://docs.ankra.ai/api-reference/security/revoke-a-security-evidence-destination https://platform.ankra.app/openapi.json post /api/v1/org/security/evidence/destinations/{destination_id}/revoke Disables the destination, deletes its credential and cancels its undelivered deliveries; the row stays for the history. # Revoke a security evidence destination (browser session) Source: https://docs.ankra.ai/api-reference/security/revoke-a-security-evidence-destination-browser-session https://platform.ankra.app/openapi.json post /org/security/evidence/destinations/{destination_id}/revoke Disables the destination, deletes its credential and cancels its undelivered deliveries; the row stays for the history. # Revoke code finding disposition Source: https://docs.ankra.ai/api-reference/security/revoke-code-finding-disposition https://platform.ankra.app/openapi.json post /api/v1/org/security/code-dispositions/{disposition_id}/revoke Revokes a code finding disposition with a required `reason`; the image gate blocks on the finding again from its next run and the record is kept for audit. Requires `security.manage`; returns 409 when it is already revoked. # Revoke code finding disposition (browser session) Source: https://docs.ankra.ai/api-reference/security/revoke-code-finding-disposition-browser-session https://platform.ankra.app/openapi.json post /org/security/code-dispositions/{disposition_id}/revoke Revokes a code finding disposition with a required `reason`; the image gate blocks on the finding again from its next run and the record is kept for audit. Requires `security.manage`; returns 409 when it is already revoked. # Running containers and their bills of materials Source: https://docs.ankra.ai/api-reference/security/running-containers-and-their-bills-of-materials https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom/containers Every container the resource cache says is running on the scoped clusters, init containers included, joined to the bill of materials of its image - or marked absent when the scanner has none for it. Absent is a state to act on (SBOM generation off, a registry the scanner cannot pull from, a tag rolled since the last scan), not an empty image. Sorted absent-first by default. # Running containers and their bills of materials (browser session) Source: https://docs.ankra.ai/api-reference/security/running-containers-and-their-bills-of-materials-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom/containers Every container the resource cache says is running on the scoped clusters, init containers included, joined to the bill of materials of its image - or marked absent when the scanner has none for it. Absent is a state to act on (SBOM generation off, a registry the scanner cannot pull from, a tag rolled since the last scan), not an empty image. Sorted absent-first by default. # Security filter options Source: https://docs.ankra.ai/api-reference/security/security-filter-options https://platform.ankra.app/openapi.json get /api/v1/org/security/filter-options The values the namespace and add-on filters can match: the distinct namespaces and high-confidence add-on slugs on the organisation’s active occurrences, narrowed to one cluster when cluster_id is given. Feeds the console’s filter pickers, so every value returned filters the findings and workloads lists to a non-empty result. # Security filter options (browser session) Source: https://docs.ankra.ai/api-reference/security/security-filter-options-browser-session https://platform.ankra.app/openapi.json get /org/security/filter-options The values the namespace and add-on filters can match: the distinct namespaces and high-confidence add-on slugs on the organisation’s active occurrences, narrowed to one cluster when cluster_id is given. Feeds the console’s filter pickers, so every value returned filters the findings and workloads lists to a non-empty result. # Security posture by cluster and namespace Source: https://docs.ankra.ai/api-reference/security/security-posture-by-cluster-and-namespace https://platform.ankra.app/openapi.json get /api/v1/org/security/namespaces Active finding occurrences grouped by cluster and namespace, with the scanned workloads and images, the pods the cluster runs there, and the actionable findings by severity. Cluster-scoped reports carry a row with an empty namespace and report_scope 'cluster'. # Security posture by cluster and namespace (browser session) Source: https://docs.ankra.ai/api-reference/security/security-posture-by-cluster-and-namespace-browser-session https://platform.ankra.app/openapi.json get /org/security/namespaces Active finding occurrences grouped by cluster and namespace, with the scanned workloads and images, the pods the cluster runs there, and the actionable findings by severity. Cluster-scoped reports carry a row with an empty namespace and report_scope 'cluster'. # Send security evidence to a destination Source: https://docs.ankra.ai/api-reference/security/send-security-evidence-to-a-destination https://platform.ankra.app/openapi.json post /api/v1/org/security/evidence/deliveries Captures one bounded snapshot (the framework report if requested, and the SBOM image inventory KEV first, at most 2,000 images) and records it in the outbox, due now. A disabled framework or unrecorded month is refused. # Send security evidence to a destination (browser session) Source: https://docs.ankra.ai/api-reference/security/send-security-evidence-to-a-destination-browser-session https://platform.ankra.app/openapi.json post /org/security/evidence/deliveries Captures one bounded snapshot (the framework report if requested, and the SBOM image inventory KEV first, at most 2,000 images) and records it in the outbox, due now. A disabled framework or unrecorded month is refused. # Set the security baseline policy mode (audit or enforce) Source: https://docs.ankra.ai/api-reference/security/set-the-security-baseline-policy-mode-audit-or-enforce https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/security/policy-mode Switches the security baseline's Kyverno policies between `audit` and `enforce` on one cluster by updating the policy add-on's configuration through GitOps, and returns the resulting commit. Requires `security.triage` or `security.manage`; returns 404 when the baseline policy add-on is not installed. # Set the security baseline policy mode (audit or enforce) (browser session) Source: https://docs.ankra.ai/api-reference/security/set-the-security-baseline-policy-mode-audit-or-enforce-browser-session https://platform.ankra.app/openapi.json post /org/clusters/imported/{cluster_id}/security/policy-mode Switches the security baseline's Kyverno policies between `audit` and `enforce` on one cluster by updating the policy add-on's configuration through GitOps, and returns the resulting commit. Requires `security.triage` or `security.manage`; returns 404 when the baseline policy add-on is not installed. # Soft-revoke vulnerability disposition policy Source: https://docs.ankra.ai/api-reference/security/soft-revoke-vulnerability-disposition-policy https://platform.ankra.app/openapi.json post /api/v1/org/security/vulnerability-dispositions/{policy_id}/revoke Revokes a disposition policy with a required `reason`, so it no longer applies to matching vulnerabilities; the policy record is kept for audit. Requires `security.manage`; returns 409 when it is already revoked. # Soft-revoke vulnerability disposition policy (browser session) Source: https://docs.ankra.ai/api-reference/security/soft-revoke-vulnerability-disposition-policy-browser-session https://platform.ankra.app/openapi.json post /org/security/vulnerability-dispositions/{policy_id}/revoke Revokes a disposition policy with a required `reason`, so it no longer applies to matching vulnerabilities; the policy record is kept for audit. Requires `security.manage`; returns 409 when it is already revoked. # Start or reuse remediation runs for a security finding Source: https://docs.ankra.ai/api-reference/security/start-or-reuse-remediation-runs-for-a-security-finding https://platform.ankra.app/openapi.json post /api/v1/org/security/findings/{finding_id}/remediations Opens or reuses the fix pull request per affected application repository (the image rebuild lane), starts or answers the live remediation run for each, and dispatches the pull request branch's head through Ankra Pipelines under the default branch's approved authority. Repeating the call reuses the live runs; nothing is approved, waived or accepted. The caller, a person through their session or their API token, is the one asking, so the organisation's AI autonomy pause and the application's keep_it_safe choice do not hold this start; they hold every step Ankra later takes on its own for the run, such as re-running its pipeline after an Ankra infrastructure error, which then stops the run at needs_human with the reason. # Start or reuse remediation runs for a security finding (browser session) Source: https://docs.ankra.ai/api-reference/security/start-or-reuse-remediation-runs-for-a-security-finding-browser-session https://platform.ankra.app/openapi.json post /org/security/findings/{finding_id}/remediations Opens or reuses the fix pull request per affected application repository (the image rebuild lane), starts or answers the live remediation run for each, and dispatches the pull request branch's head through Ankra Pipelines under the default branch's approved authority. Repeating the call reuses the live runs; nothing is approved, waived or accepted. The caller, a person through their session or their API token, is the one asking, so the organisation's AI autonomy pause and the application's keep_it_safe choice do not hold this start; they hold every step Ankra later takes on its own for the run, such as re-running its pipeline after an Ankra infrastructure error, which then stops the run at needs_human with the reason. # Store the cluster's scheduled security report Source: https://docs.ankra.ai/api-reference/security/store-the-clusters-scheduled-security-report https://platform.ankra.app/openapi.json put /api/v1/org/clusters/imported/{cluster_id}/security/report-schedule Creates or replaces the cluster's scheduled security report: `frequency` (`weekly` or `monthly`), up to 20 email `recipients`, and `enabled` (default true, which needs at least one recipient). Requires `clusters.write` and returns the stored schedule. # Store the cluster's scheduled security report (browser session) Source: https://docs.ankra.ai/api-reference/security/store-the-clusters-scheduled-security-report-browser-session https://platform.ankra.app/openapi.json put /org/clusters/imported/{cluster_id}/security/report-schedule Creates or replaces the cluster's scheduled security report: `frequency` (`weekly` or `monthly`), up to 20 email `recipients`, and `enabled` (default true, which needs at least one recipient). Requires `clusters.write` and returns the stored schedule. # Submit an uploaded Vanta evidence document Source: https://docs.ankra.ai/api-reference/security/submit-an-uploaded-vanta-evidence-document https://platform.ankra.app/openapi.json post /api/v1/org/security/evidence/deliveries/{delivery_id}/submit Submits the Vanta document an uploaded delivery put there; upload and submission are separate. A refused or uncertain submission leaves the delivery uploaded with the reason. # Submit an uploaded Vanta evidence document (browser session) Source: https://docs.ankra.ai/api-reference/security/submit-an-uploaded-vanta-evidence-document-browser-session https://platform.ankra.app/openapi.json post /org/security/evidence/deliveries/{delivery_id}/submit Submits the Vanta document an uploaded delivery put there; upload and submission are separate. A refused or uncertain submission leaves the delivery uploaded with the reason. # The cluster's scheduled security report Source: https://docs.ankra.ai/api-reference/security/the-clusters-scheduled-security-report https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/security/report-schedule Returns the cluster's scheduled security report configuration: frequency, email recipients and whether it is enabled. # The cluster's scheduled security report (browser session) Source: https://docs.ankra.ai/api-reference/security/the-clusters-scheduled-security-report-browser-session https://platform.ankra.app/openapi.json get /org/clusters/imported/{cluster_id}/security/report-schedule Returns the cluster's scheduled security report configuration: frequency, email recipients and whether it is enabled. # The fleet's software bill of materials Source: https://docs.ankra.ai/api-reference/security/the-fleets-software-bill-of-materials https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom Every package the scanner found in the fleet's container images, grouped by name, version and ecosystem, with how many images, workloads and clusters carry it and the findings that name that exact package and version on those images. The coverage block says how many scanned clusters publish a bill of materials; the rest have SBOM generation switched off. # The fleet's software bill of materials (browser session) Source: https://docs.ankra.ai/api-reference/security/the-fleets-software-bill-of-materials-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom Every package the scanner found in the fleet's container images, grouped by name, version and ecosystem, with how many images, workloads and clusters carry it and the findings that name that exact package and version on those images. The coverage block says how many scanned clusters publish a bill of materials; the rest have SBOM generation switched off. # The pods of one namespace joined to their scanned workload's findings Source: https://docs.ankra.ai/api-reference/security/the-pods-of-one-namespace-joined-to-their-scanned-workloads-findings https://platform.ankra.app/openapi.json get /api/v1/org/security/pods The running pods of one namespace on one cluster, each container joined to the findings of the scanned workload container behind it through the pod's owner chain. A container without a vulnerability report is marked scanned=false; a pod is scanned only when every container is, and scan_coverage and unscanned_containers name the containers a partially scanned pod is missing. Narrow to one workload with workload_uid, or workload_kind and workload_name. # The pods of one namespace joined to their scanned workload's findings (browser session) Source: https://docs.ankra.ai/api-reference/security/the-pods-of-one-namespace-joined-to-their-scanned-workloads-findings-browser-session https://platform.ankra.app/openapi.json get /org/security/pods The running pods of one namespace on one cluster, each container joined to the findings of the scanned workload container behind it through the pod's owner chain. A container without a vulnerability report is marked scanned=false; a pod is scanned only when every container is, and scan_coverage and unscanned_containers name the containers a partially scanned pod is missing. Narrow to one workload with workload_uid, or workload_kind and workload_name. # Update vulnerability disposition policy Source: https://docs.ankra.ai/api-reference/security/update-vulnerability-disposition-policy https://platform.ankra.app/openapi.json patch /api/v1/org/security/vulnerability-dispositions/{policy_id} Updates a disposition policy's `reason`, `expires_at` or `expire_when_fix_available`; at least one field is required. Requires `security.manage`; returns 409 when the policy has been revoked. # Update vulnerability disposition policy (browser session) Source: https://docs.ankra.ai/api-reference/security/update-vulnerability-disposition-policy-browser-session https://platform.ankra.app/openapi.json patch /org/security/vulnerability-dispositions/{policy_id} Updates a disposition policy's `reason`, `expires_at` or `expire_when_fix_available`; at least one field is required. Requires `security.manage`; returns 409 when the policy has been revoked. # Where one package runs: the images carrying it, the workload containers running them, and their clusters Source: https://docs.ankra.ai/api-reference/security/where-one-package-runs:-the-images-carrying-it-the-workload-containers-running-them-and-their-clusters https://platform.ankra.app/openapi.json get /api/v1/org/security/sbom/component Shows where one exact package (`name`, `version`, `package_type`) runs: the images carrying it, the workload containers running those images, and their clusters. Optionally narrowed with `cluster_id`; requires `security.read`. # Where one package runs: the images carrying it, the workload containers running them, and their clusters (browser session) Source: https://docs.ankra.ai/api-reference/security/where-one-package-runs:-the-images-carrying-it-the-workload-containers-running-them-and-their-clusters-browser-session https://platform.ankra.app/openapi.json get /org/security/sbom/component Shows where one exact package (`name`, `version`, `package_type`) runs: the images carrying it, the workload containers running those images, and their clusters. Optionally narrowed with `cluster_id`; requires `security.read`. # Bind an explicit application namespace Source: https://docs.ankra.ai/api-reference/services/bind-an-explicit-application-namespace https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/consumers Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Bind an explicit application namespace (browser session) Source: https://docs.ankra.ai/api-reference/services/bind-an-explicit-application-namespace-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/consumers Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Choose the service behind an application's conventional connection names Source: https://docs.ankra.ai/api-reference/services/choose-the-service-behind-an-applications-conventional-connection-names https://platform.ankra.app/openapi.json put /api/v1/org/service-admission/connection-defaults Requires the applications rollout flag, applications.deploy, and clusters.operate on the environment's cluster, all intersected with live token scopes. Chooses which managed service gives one application environment a protocol's conventional connection names (DATABASE_URL and the PG* names for postgres, REDIS_URL for redis, NATS_URL, OPENSEARCH_URL, S3_ENDPOINT and AWS_* for s3, ...), or, with instance_id null, that none does. Every service connected to the environment is always delivered under its own prefix (ORDERS_DB_DATABASE_URL); the conventional names belong to one service per protocol, recorded the first time a deploy delivers them, and never move by themselves. When that service stops serving the environment while another service of the protocol still does, the application's deploys are refused until a person chooses here; the instance read shows it as connection_delivery.holder_gone on the consumer. Nothing is deployed: the next deploy delivers the choice. A chosen service must be connected to the environment and speak the protocol. # Choose the service behind an application's conventional connection names (browser session) Source: https://docs.ankra.ai/api-reference/services/choose-the-service-behind-an-applications-conventional-connection-names-browser-session https://platform.ankra.app/openapi.json put /org/service-admission/connection-defaults Requires the applications rollout flag, applications.deploy, and clusters.operate on the environment's cluster, all intersected with live token scopes. Chooses which managed service gives one application environment a protocol's conventional connection names (DATABASE_URL and the PG* names for postgres, REDIS_URL for redis, NATS_URL, OPENSEARCH_URL, S3_ENDPOINT and AWS_* for s3, ...), or, with instance_id null, that none does. Every service connected to the environment is always delivered under its own prefix (ORDERS_DB_DATABASE_URL); the conventional names belong to one service per protocol, recorded the first time a deploy delivers them, and never move by themselves. When that service stops serving the environment while another service of the protocol still does, the application's deploys are refused until a person chooses here; the instance read shows it as connection_delivery.holder_gone on the consumer. Nothing is deployed: the next deploy delivers the choice. A chosen service must be connected to the environment and speak the protocol. # Confirm a managed-service disconnection by its digest Source: https://docs.ankra.ai/api-reference/services/confirm-a-managed-service-disconnection-by-its-digest https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/instances/{instance_id}/disconnections/{disconnection_id}/confirm Requires the applications rollout flag, applications.deploy, clusters.operate on the service's cluster, and clusters.operate and clusters.read on the application environment's cluster, all intersected with live token scopes: the authority connecting it needed. Accepts only the stored disconnection and the digest its actor reviewed, resolves the plan again under the same locks and refuses when anything it covers moved, or when the plan asks for a choice of who gives the conventional names and none was made. In one transaction it takes the consumer off the instance's plan, records the connection release entry the environment's stored release renders for it so every deploy keeps that Secret byte for byte while pods still read it, records the choice for the conventional names, and audits it. The platform's disconnect lane then removes the Secret once no pod and no deployed workload reads it, takes the namespace off the client port when the plan said so, and settles. A retry returns the same record. # Confirm a managed-service disconnection by its digest (browser session) Source: https://docs.ankra.ai/api-reference/services/confirm-a-managed-service-disconnection-by-its-digest-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/instances/{instance_id}/disconnections/{disconnection_id}/confirm Requires the applications rollout flag, applications.deploy, clusters.operate on the service's cluster, and clusters.operate and clusters.read on the application environment's cluster, all intersected with live token scopes: the authority connecting it needed. Accepts only the stored disconnection and the digest its actor reviewed, resolves the plan again under the same locks and refuses when anything it covers moved, or when the plan asks for a choice of who gives the conventional names and none was made. In one transaction it takes the consumer off the instance's plan, records the connection release entry the environment's stored release renders for it so every deploy keeps that Secret byte for byte while pods still read it, records the choice for the conventional names, and audits it. The platform's disconnect lane then removes the Secret once no pod and no deployed workload reads it, takes the namespace off the client port when the plan said so, and settles. A retry returns the same record. # Confirm a managed-service retirement by its digest Source: https://docs.ankra.ai/api-reference/services/confirm-a-managed-service-retirement-by-its-digest https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/instances/{instance_id}/retirements/{retirement_id}/confirm Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes. Accepts only the stored retirement and the digest its actor reviewed, resolves the plan again under the instance lock and enqueues one execution that removes the stack, waits for its members to be removed from the cluster, deletes the service's namespace once nothing else deploys into it, and releases the name only after the namespace is observed gone. Concurrent confirmations yield one execution; a retry returns the same receipt. The receipt is not proof of removal: follow the retirement's outcome. # Confirm a managed-service retirement by its digest (browser session) Source: https://docs.ankra.ai/api-reference/services/confirm-a-managed-service-retirement-by-its-digest-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/instances/{instance_id}/retirements/{retirement_id}/confirm Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes. Accepts only the stored retirement and the digest its actor reviewed, resolves the plan again under the instance lock and enqueues one execution that removes the stack, waits for its members to be removed from the cluster, deletes the service's namespace once nothing else deploys into it, and releases the name only after the namespace is observed gone. Concurrent confirmations yield one execution; a retry returns the same receipt. The receipt is not proof of removal: follow the retirement's outcome. # Confirm a reviewed managed-service plan by its digest Source: https://docs.ankra.ai/api-reference/services/confirm-a-reviewed-managed-service-plan-by-its-digest https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/reviews/{review_id}/confirm Requires the applications rollout flag and live membership intersected with token scopes; reviews require applications.deploy, profiles.read and clusters.read. Preparing a review resolves the immutable package, the declared cluster policy, every explicitly bound consumer and every secret grant on the primary database, and stores an actor-bound plan with a digest and a ten-minute expiry. Confirming accepts only the stored review id and the reviewed digest, revalidates the same authority under row locks, and enqueues one cluster-scoped execution for a customer or existing-service plan; a retry returns the same receipt after current access checks. Hosted plans are refused at preparation: hosted operator-to-customer binding and cell dispatch are not wired, so nothing hosted can be reviewed or confirmed. A name an unreleased service instance already holds on the service cluster is refused with 409 Service name is already in use when the review is prepared, and again when it is confirmed: the reservation made at confirmation decides between concurrent reviews of one name. Service review has changed means only that the digest or the stored review changed. Plans never carry credential values. The execution receipt is not proof of service readiness. On a playground, prepare and confirm also check the plan's quota against what the tenant commits now: a shortfall is refused with 409 naming it, and a capacity the platform cannot establish is returned as capacity.state unknown without blocking. # Confirm a reviewed managed-service plan by its digest (browser session) Source: https://docs.ankra.ai/api-reference/services/confirm-a-reviewed-managed-service-plan-by-its-digest-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/reviews/{review_id}/confirm Requires the applications rollout flag and live membership intersected with token scopes; reviews require applications.deploy, profiles.read and clusters.read. Preparing a review resolves the immutable package, the declared cluster policy, every explicitly bound consumer and every secret grant on the primary database, and stores an actor-bound plan with a digest and a ten-minute expiry. Confirming accepts only the stored review id and the reviewed digest, revalidates the same authority under row locks, and enqueues one cluster-scoped execution for a customer or existing-service plan; a retry returns the same receipt after current access checks. Hosted plans are refused at preparation: hosted operator-to-customer binding and cell dispatch are not wired, so nothing hosted can be reviewed or confirmed. A name an unreleased service instance already holds on the service cluster is refused with 409 Service name is already in use when the review is prepared, and again when it is confirmed: the reservation made at confirmation decides between concurrent reviews of one name. Service review has changed means only that the digest or the stored review changed. Plans never carry credential values. The execution receipt is not proof of service readiness. On a playground, prepare and confirm also check the plan's quota against what the tenant commits now: a shortfall is refused with 409 naming it, and a capacity the platform cannot establish is returned as capacity.state unknown without blocking. # Connect an application to a managed service Source: https://docs.ankra.ai/api-reference/services/connect-an-application-to-a-managed-service https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/instances/{instance_id}/consumers Requires the applications rollout flag, applications.deploy, and clusters.operate and clusters.read on the service's cluster, which is also the environment's, all intersected with live token scopes. Adds one bound application environment (POST .../consumers) to the consumers of a service Ankra runs in your cluster, so a second application uses the same service instead of getting one of its own. The service is not redeployed and the generation does not move; only its client network policy is re-rendered to admit the environment's namespace (alongside the service's own and the cluster agent's), on a service whose profile version renders that policy from its consumers (an older one admits every namespace until it is updated). The environment joins the service's record of who uses it, receives the service's connection the next time the application deploys, cannot be unbound while the service uses it, and is disconnected by a retirement like every other consumer. A retirement reviewed before the change is refused when confirmed. The environment must be on the service's own cluster and in its data boundary. Connecting an environment that already uses the service again re-applies its network access and answers the service. Refused for another generation than the one read, and a service that is still installing, being retired, retired, or not one Ankra runs in your cluster. One service serves at most 100 environments. A 409 that says the application is connected but the network policy could not be opened yet means the environment was recorded; Ankra retries the network policy automatically within a few minutes, and connecting it again retries at once. A 409 that says the service's client access was changed by hand means the environment was recorded but Ankra did not edit a policy a person rewrote; add the namespace the instance's client_access lists. # Connect an application to a managed service (browser session) Source: https://docs.ankra.ai/api-reference/services/connect-an-application-to-a-managed-service-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/instances/{instance_id}/consumers Requires the applications rollout flag, applications.deploy, and clusters.operate and clusters.read on the service's cluster, which is also the environment's, all intersected with live token scopes. Adds one bound application environment (POST .../consumers) to the consumers of a service Ankra runs in your cluster, so a second application uses the same service instead of getting one of its own. The service is not redeployed and the generation does not move; only its client network policy is re-rendered to admit the environment's namespace (alongside the service's own and the cluster agent's), on a service whose profile version renders that policy from its consumers (an older one admits every namespace until it is updated). The environment joins the service's record of who uses it, receives the service's connection the next time the application deploys, cannot be unbound while the service uses it, and is disconnected by a retirement like every other consumer. A retirement reviewed before the change is refused when confirmed. The environment must be on the service's own cluster and in its data boundary. Connecting an environment that already uses the service again re-applies its network access and answers the service. Refused for another generation than the one read, and a service that is still installing, being retired, retired, or not one Ankra runs in your cluster. One service serves at most 100 environments. A 409 that says the application is connected but the network policy could not be opened yet means the environment was recorded; Ankra retries the network policy automatically within a few minutes, and connecting it again retries at once. A 409 that says the service's client access was changed by hand means the environment was recorded but Ankra did not edit a policy a person rewrote; add the namespace the instance's client_access lists. # Create a managed-service credential family Source: https://docs.ankra.ai/api-reference/services/create-a-managed-service-credential-family https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/credentials Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # Create a managed-service credential family (browser session) Source: https://docs.ankra.ai/api-reference/services/create-a-managed-service-credential-family-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/credentials Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # Create or rotate a reviewed credential value Source: https://docs.ankra.ai/api-reference/services/create-or-rotate-a-reviewed-credential-value https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/credentials/{family_id}/values Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. expected_value_id is required: null requires no active value; otherwise it must match the current active value. A repeated or concurrent stale request cannot replace a newer value. Successful rotation retires the previous value, revokes its grants and returns replacement grant metadata. Store acknowledgement must identify immutable version one. After an uncertain response reload family metadata and review the current value before another write. # Create or rotate a reviewed credential value (browser session) Source: https://docs.ankra.ai/api-reference/services/create-or-rotate-a-reviewed-credential-value-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/credentials/{family_id}/values Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. expected_value_id is required: null requires no active value; otherwise it must match the current active value. A repeated or concurrent stale request cannot replace a newer value. Successful rotation retires the previous value, revokes its grants and returns replacement grant metadata. Store acknowledgement must identify immutable version one. After an uncertain response reload family metadata and review the current value before another write. # Declare cluster service placement policy Source: https://docs.ankra.ai/api-reference/services/declare-cluster-service-placement-policy https://platform.ankra.app/openapi.json put /api/v1/org/service-admission/cluster-policies/{cluster_id} Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Declare cluster service placement policy (browser session) Source: https://docs.ankra.ai/api-reference/services/declare-cluster-service-placement-policy-browser-session https://platform.ankra.app/openapi.json put /org/service-admission/cluster-policies/{cluster_id} Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Fingerprint an accessible profile version Source: https://docs.ankra.ai/api-reference/services/fingerprint-an-accessible-profile-version https://platform.ankra.app/openapi.json get /api/v1/org/service-packages/profile-pins/{profile_id}/{profile_version_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Fingerprint an accessible profile version (browser session) Source: https://docs.ankra.ai/api-reference/services/fingerprint-an-accessible-profile-version-browser-session https://platform.ankra.app/openapi.json get /org/service-packages/profile-pins/{profile_id}/{profile_version_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Get a managed service instance Source: https://docs.ankra.ai/api-reference/services/get-a-managed-service-instance https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/instances/{instance_id} Durable organisation inventory, independent of the confirming actor and review retention. Requires live applications.read and clusters.read on all requested destinations and the service cluster, intersected with live token scopes. Uses primary authority reads so replica lag cannot extend revoked access. Deleted or inaccessible resources are hidden. No credentials, grant IDs, private plans, operation payloads or raw failures are returned. Deployment state reports recorded operation progress only; evidence_updated_at makes its age explicit. Missing, malformed, foreign or later-generation evidence is unknown. Successful deployment remains verification_pending with readiness unverified; this endpoint does not observe a service-specific consumer canary. # Get a managed service instance (browser session) Source: https://docs.ankra.ai/api-reference/services/get-a-managed-service-instance-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/instances/{instance_id} Durable organisation inventory, independent of the confirming actor and review retention. Requires live applications.read and clusters.read on all requested destinations and the service cluster, intersected with live token scopes. Uses primary authority reads so replica lag cannot extend revoked access. Deleted or inaccessible resources are hidden. No credentials, grant IDs, private plans, operation payloads or raw failures are returned. Deployment state reports recorded operation progress only; evidence_updated_at makes its age explicit. Missing, malformed, foreign or later-generation evidence is unknown. Successful deployment remains verification_pending with readiness unverified; this endpoint does not observe a service-specific consumer canary. # Get a managed-service retirement Source: https://docs.ankra.ai/api-reference/services/get-a-managed-service-retirement https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/instances/{instance_id}/retirements/{retirement_id} Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes. Returns a retirement its actor prepared, with its state, phase and outcome. Another actor's retirement is not found. # Get a managed-service retirement (browser session) Source: https://docs.ankra.ai/api-reference/services/get-a-managed-service-retirement-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/instances/{instance_id}/retirements/{retirement_id} Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes. Returns a retirement its actor prepared, with its state, phase and outcome. Another actor's retirement is not found. # Issue a purpose-bound credential reference Source: https://docs.ankra.ai/api-reference/services/issue-a-purpose-bound-credential-reference https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/credential-grants Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. Each issue creates an independently revocable reference. At most 128 active grants per family bounds rotation work. # Issue a purpose-bound credential reference (browser session) Source: https://docs.ankra.ai/api-reference/services/issue-a-purpose-bound-credential-reference-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/credential-grants Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. Each issue creates an independently revocable reference. At most 128 active grants per family bounds rotation work. # List an application's service consumer bindings Source: https://docs.ankra.ai/api-reference/services/list-an-applications-service-consumer-bindings https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/consumers Requires the applications rollout flag and live membership intersected with token scopes. Lists one application's stored namespace bindings, oldest identifier first, gated on applications.read; a binding is included only when the caller also holds clusters.read on its cluster, so a page can hold fewer rows than the limit, or none, while next_cursor is set. Rows are the bindings as last saved, not live-resolved consumers: a binding whose deployment or cluster policy is missing is still listed. No service deployment is executed. # List an application's service consumer bindings (browser session) Source: https://docs.ankra.ai/api-reference/services/list-an-applications-service-consumer-bindings-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/consumers Requires the applications rollout flag and live membership intersected with token scopes. Lists one application's stored namespace bindings, oldest identifier first, gated on applications.read; a binding is included only when the caller also holds clusters.read on its cluster, so a page can hold fewer rows than the limit, or none, while next_cursor is set. Rows are the bindings as last saved, not live-resolved consumers: a binding whose deployment or cluster policy is missing is still listed. No service deployment is executed. # List immutable credential version metadata Source: https://docs.ankra.ai/api-reference/services/list-immutable-credential-version-metadata https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/credentials/{family_id}/values Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # List immutable credential version metadata (browser session) Source: https://docs.ankra.ai/api-reference/services/list-immutable-credential-version-metadata-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/credentials/{family_id}/values Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # List managed-service credential metadata Source: https://docs.ankra.ai/api-reference/services/list-managed-service-credential-metadata https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/credentials Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # List managed-service credential metadata (browser session) Source: https://docs.ankra.ai/api-reference/services/list-managed-service-credential-metadata-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/credentials Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # List managed service instances Source: https://docs.ankra.ai/api-reference/services/list-managed-service-instances https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/instances Durable organisation inventory, independent of the confirming actor and review retention. Requires live applications.read and clusters.read on all requested destinations and the service cluster, intersected with live token scopes. Uses primary authority reads so replica lag cannot extend revoked access. Deleted or inaccessible resources are hidden. No credentials, grant IDs, private plans, operation payloads or raw failures are returned. Deployment state reports recorded operation progress only; evidence_updated_at makes its age explicit. Missing, malformed, foreign or later-generation evidence is unknown. Successful deployment remains verification_pending with readiness unverified; this endpoint does not observe a service-specific consumer canary. Optional filters: application_id keeps the instances whose reviewed plan names a consumer of that application, so an application's services are one call; cluster_id keeps the instances running on that cluster; state keeps the instances in one state. Each instance carries cluster_name and each consumer its application_name, cluster_name and environment for display; the ids stay authoritative. Ascending UUID cursor order; one request examines at most 500 rows and returns at most 50 matching instances. next_cursor is set exactly when rows remain past it. A page shorter than limit that has a next_cursor always carries scan_capped true: the request stopped at its row bound before the page filled, which is never a statement that nothing more matches. Follow next_cursor until null. # List managed service instances (browser session) Source: https://docs.ankra.ai/api-reference/services/list-managed-service-instances-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/instances Durable organisation inventory, independent of the confirming actor and review retention. Requires live applications.read and clusters.read on all requested destinations and the service cluster, intersected with live token scopes. Uses primary authority reads so replica lag cannot extend revoked access. Deleted or inaccessible resources are hidden. No credentials, grant IDs, private plans, operation payloads or raw failures are returned. Deployment state reports recorded operation progress only; evidence_updated_at makes its age explicit. Missing, malformed, foreign or later-generation evidence is unknown. Successful deployment remains verification_pending with readiness unverified; this endpoint does not observe a service-specific consumer canary. Optional filters: application_id keeps the instances whose reviewed plan names a consumer of that application, so an application's services are one call; cluster_id keeps the instances running on that cluster; state keeps the instances in one state. Each instance carries cluster_name and each consumer its application_name, cluster_name and environment for display; the ids stay authoritative. Ascending UUID cursor order; one request examines at most 500 rows and returns at most 50 matching instances. next_cursor is set exactly when rows remain past it. A page shorter than limit that has a next_cursor always carries scan_capped true: the request stopped at its row bound before the page filled, which is never a statement that nothing more matches. Follow next_cursor until null. # List package recipients as its owner Source: https://docs.ankra.ai/api-reference/services/list-package-recipients-as-its-owner https://platform.ankra.app/openapi.json get /api/v1/org/service-packages/{version_id}/shares Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # List package recipients as its owner (browser session) Source: https://docs.ankra.ai/api-reference/services/list-package-recipients-as-its-owner-browser-session https://platform.ankra.app/openapi.json get /org/service-packages/{version_id}/shares Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # List purpose-bound credential grants Source: https://docs.ankra.ai/api-reference/services/list-purpose-bound-credential-grants https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/credentials/{family_id}/grants Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # List purpose-bound credential grants (browser session) Source: https://docs.ankra.ai/api-reference/services/list-purpose-bound-credential-grants-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/credentials/{family_id}/grants Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # List visible package versions Source: https://docs.ankra.ai/api-reference/services/list-visible-package-versions https://platform.ankra.app/openapi.json get /api/v1/org/service-packages Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # List visible package versions (browser session) Source: https://docs.ankra.ai/api-reference/services/list-visible-package-versions-browser-session https://platform.ankra.app/openapi.json get /org/service-packages Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # List your retirements of a managed service Source: https://docs.ankra.ai/api-reference/services/list-your-retirements-of-a-managed-service https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/instances/{instance_id}/retirements Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes - checked before any retirement is read, and again against every listed plan. Lists only your own retirements of the instance, never another actor's, read on the primary database so a review prepared a moment ago is already listed: a client whose prepare answer was lost finds the review here instead of preparing again, which would spend another of its 10 open retirements. Each item is the single read's projection, its state folded against the database clock, so a review past its ten-minute window lists as expired, never pending. state keeps only the retirements in that state. Newest first: ordered by creation time, latest first, with the retirement id breaking ties. next_cursor is the id of the page's last retirement when more match; pass it as after to continue with older ones. after must name one of your own retirements of this instance: an unknown id, another actor's or another instance's retirement, or one deleted by retention since the cursor was issued is refused with 422 (Invalid service review page), and the listing starts again from the first page. A cursor whose retirement has since changed state still continues the listing. Nothing is skipped, so a page shorter than limit is the last one. An instance the organisation does not hold is not found, exactly as for prepare. # List your retirements of a managed service (browser session) Source: https://docs.ankra.ai/api-reference/services/list-your-retirements-of-a-managed-service-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/instances/{instance_id}/retirements Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes - checked before any retirement is read, and again against every listed plan. Lists only your own retirements of the instance, never another actor's, read on the primary database so a review prepared a moment ago is already listed: a client whose prepare answer was lost finds the review here instead of preparing again, which would spend another of its 10 open retirements. Each item is the single read's projection, its state folded against the database clock, so a review past its ten-minute window lists as expired, never pending. state keeps only the retirements in that state. Newest first: ordered by creation time, latest first, with the retirement id breaking ties. next_cursor is the id of the page's last retirement when more match; pass it as after to continue with older ones. after must name one of your own retirements of this instance: an unknown id, another actor's or another instance's retirement, or one deleted by retention since the cursor was issued is refused with 422 (Invalid service review page), and the listing starts again from the first page. A cursor whose retirement has since changed state still continues the listing. Nothing is skipped, so a page shorter than limit is the last one. An instance the organisation does not hold is not found, exactly as for prepare. # List your service reviews Source: https://docs.ankra.ai/api-reference/services/list-your-service-reviews https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/reviews Actor-scoped history on the primary database. Requires current applications rollout and applications.deploy; each row requires clusters.read for every destination and clusters.operate for the service cluster, intersected with live token scopes. Foreign actors, deleted resources and inaccessible reviews are hidden. Secret values, secret reference IDs and internal consumer binding IDs are never returned. Pending means only that the original review window has not expired; confirmation revalidates authority and configuration. Expired reviews require a new review; confirmed receipts remain confirmed after execution retention and are not readiness evidence. Newest first: ordered by creation time, latest first, with the review id breaking ties. next_cursor is the id of the last review the page inspected; pass it as after to continue with older reviews. after must name one of your own reviews: an unknown id, another actor's review, or a review deleted by retention since the cursor was issued is refused with 422 (Invalid service review page), and the listing starts again from the first page. A review prepared while you page is newer than your cursor, so it appears on a fresh first page rather than in the continuation. Scans at most 100 owned rows per request, skipping denied rows; a short or empty page may have next_cursor. A continuation means more owned rows remain to inspect, not necessarily more readable reviews. Follow until next_cursor is null. # List your service reviews (browser session) Source: https://docs.ankra.ai/api-reference/services/list-your-service-reviews-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/reviews Actor-scoped history on the primary database. Requires current applications rollout and applications.deploy; each row requires clusters.read for every destination and clusters.operate for the service cluster, intersected with live token scopes. Foreign actors, deleted resources and inaccessible reviews are hidden. Secret values, secret reference IDs and internal consumer binding IDs are never returned. Pending means only that the original review window has not expired; confirmation revalidates authority and configuration. Expired reviews require a new review; confirmed receipts remain confirmed after execution retention and are not readiness evidence. Newest first: ordered by creation time, latest first, with the review id breaking ties. next_cursor is the id of the last review the page inspected; pass it as after to continue with older reviews. after must name one of your own reviews: an unknown id, another actor's review, or a review deleted by retention since the cursor was issued is refused with 422 (Invalid service review page), and the listing starts again from the first page. A review prepared while you page is newer than your cursor, so it appears on a fresh first page rather than in the continuation. Scans at most 100 owned rows per request, skipping denied rows; a short or empty page may have next_cursor. A continuation means more owned rows remain to inspect, not necessarily more readable reviews. Follow until next_cursor is null. # Prepare a managed-service disconnection review Source: https://docs.ankra.ai/api-reference/services/prepare-a-managed-service-disconnection-review https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/instances/{instance_id}/disconnections Requires the applications rollout flag, applications.deploy, clusters.operate on the service's cluster, and clusters.operate and clusters.read on the application environment's cluster, all intersected with live token scopes: the authority connecting it needed. Resolves, on the primary database, what disconnecting one application environment from a running managed service does: the variables it stops receiving and, when this service gives the protocol's conventional names (DATABASE_URL, ...) while another service of the protocol is connected, the choices for who gives them next; that the shared credential is not revoked and how to make sure the application can no longer authenticate; whether the environment's release delivered the connection Secret; which deployed workloads read it, separating references Ankra attached (the next deploy removes them and rolls the workloads) from references written by hand; whether the namespace leaves the service's client port; when it takes effect; and what stays. It stores an actor-bound plan with a digest and a ten-minute expiry. Nothing changes. timing now is refused while every application of the service shares one credential. Refused while the service installs or retires, while a deploy of the environment is in flight, while the environment is already being disconnected, and for another generation. An actor may hold at most 10 open disconnection reviews and an organisation 50. # Prepare a managed-service disconnection review (browser session) Source: https://docs.ankra.ai/api-reference/services/prepare-a-managed-service-disconnection-review-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/instances/{instance_id}/disconnections Requires the applications rollout flag, applications.deploy, clusters.operate on the service's cluster, and clusters.operate and clusters.read on the application environment's cluster, all intersected with live token scopes: the authority connecting it needed. Resolves, on the primary database, what disconnecting one application environment from a running managed service does: the variables it stops receiving and, when this service gives the protocol's conventional names (DATABASE_URL, ...) while another service of the protocol is connected, the choices for who gives them next; that the shared credential is not revoked and how to make sure the application can no longer authenticate; whether the environment's release delivered the connection Secret; which deployed workloads read it, separating references Ankra attached (the next deploy removes them and rolls the workloads) from references written by hand; whether the namespace leaves the service's client port; when it takes effect; and what stays. It stores an actor-bound plan with a digest and a ten-minute expiry. Nothing changes. timing now is refused while every application of the service shares one credential. Refused while the service installs or retires, while a deploy of the environment is in flight, while the environment is already being disconnected, and for another generation. An actor may hold at most 10 open disconnection reviews and an organisation 50. # Prepare a managed-service retirement review Source: https://docs.ankra.ai/api-reference/services/prepare-a-managed-service-retirement-review https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/instances/{instance_id}/retirements Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes. Resolves, on the primary database, what retiring the instance removes and keeps - its stack found by resource id with every member and the namespace it deploys into, the consumers it is disconnected from, the shared operators it keeps and what happens to its data - and stores an actor-bound plan with a digest and a ten-minute expiry. Nothing on the cluster changes. Refused while the installation runs, while another retirement runs, for another generation, for a disconnect set that is not exactly the service's consumers, for a stack changed outside Services, and without the data-loss acknowledgement. An actor may hold at most 10 open retirements and an organisation 50. # Prepare a managed-service retirement review (browser session) Source: https://docs.ankra.ai/api-reference/services/prepare-a-managed-service-retirement-review-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/instances/{instance_id}/retirements Requires the applications rollout flag, applications.deploy, and on the service cluster clusters.operate, stacks.delete and clusters.read, plus clusters.read on every consumer cluster, all intersected with live token scopes. Resolves, on the primary database, what retiring the instance removes and keeps - its stack found by resource id with every member and the namespace it deploys into, the consumers it is disconnected from, the shared operators it keeps and what happens to its data - and stores an actor-bound plan with a digest and a ten-minute expiry. Nothing on the cluster changes. Refused while the installation runs, while another retirement runs, for another generation, for a disconnect set that is not exactly the service's consumers, for a stack changed outside Services, and without the data-loss acknowledgement. An actor may hold at most 10 open retirements and an organisation 50. # Prepare an actor-bound managed-service plan review Source: https://docs.ankra.ai/api-reference/services/prepare-an-actor-bound-managed-service-plan-review https://platform.ankra.app/openapi.json post /api/v1/org/service-admission/reviews Requires the applications rollout flag and live membership intersected with token scopes; reviews require applications.deploy, profiles.read and clusters.read. Preparing a review resolves the immutable package, the declared cluster policy, every explicitly bound consumer and every secret grant on the primary database, and stores an actor-bound plan with a digest and a ten-minute expiry. Confirming accepts only the stored review id and the reviewed digest, revalidates the same authority under row locks, and enqueues one cluster-scoped execution for a customer or existing-service plan; a retry returns the same receipt after current access checks. Hosted plans are refused at preparation: hosted operator-to-customer binding and cell dispatch are not wired, so nothing hosted can be reviewed or confirmed. A name an unreleased service instance already holds on the service cluster is refused with 409 Service name is already in use when the review is prepared, and again when it is confirmed: the reservation made at confirmation decides between concurrent reviews of one name. Service review has changed means only that the digest or the stored review changed. The request body is bounded at 16 KiB, which fits the maximum of 100 consumer_ids, 32 parameters and 16 secret_references. An actor may hold at most 20 open reviews and an organisation 100; a review that was never confirmed is deleted seven days after it expires. Plans never carry credential values. The execution receipt is not proof of service readiness. On a playground, prepare and confirm also check the plan's quota against what the tenant commits now: a shortfall is refused with 409 naming it, and a capacity the platform cannot establish is returned as capacity.state unknown without blocking. # Prepare an actor-bound managed-service plan review (browser session) Source: https://docs.ankra.ai/api-reference/services/prepare-an-actor-bound-managed-service-plan-review-browser-session https://platform.ankra.app/openapi.json post /org/service-admission/reviews Requires the applications rollout flag and live membership intersected with token scopes; reviews require applications.deploy, profiles.read and clusters.read. Preparing a review resolves the immutable package, the declared cluster policy, every explicitly bound consumer and every secret grant on the primary database, and stores an actor-bound plan with a digest and a ten-minute expiry. Confirming accepts only the stored review id and the reviewed digest, revalidates the same authority under row locks, and enqueues one cluster-scoped execution for a customer or existing-service plan; a retry returns the same receipt after current access checks. Hosted plans are refused at preparation: hosted operator-to-customer binding and cell dispatch are not wired, so nothing hosted can be reviewed or confirmed. A name an unreleased service instance already holds on the service cluster is refused with 409 Service name is already in use when the review is prepared, and again when it is confirmed: the reservation made at confirmation decides between concurrent reviews of one name. Service review has changed means only that the digest or the stored review changed. The request body is bounded at 16 KiB, which fits the maximum of 100 consumer_ids, 32 parameters and 16 secret_references. An actor may hold at most 20 open reviews and an organisation 100; a review that was never confirmed is deleted seven days after it expires. Plans never carry credential values. The execution receipt is not proof of service readiness. On a playground, prepare and confirm also check the plan's quota against what the tenant commits now: a shortfall is refused with 409 naming it, and a capacity the platform cannot establish is returned as capacity.state unknown without blocking. # Preview managed service eligibility Source: https://docs.ankra.ai/api-reference/services/preview-managed-service-eligibility https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/eligibility Answers which catalogue package versions and modes one application environment - the namespace an application is, or is about to be, deployed to on a cluster - could set up right now, and for each that it cannot, the reason and what to do: a cluster that does not deploy through the Ankra native engine, a playground short of capacity, a mode that is not available (Ankra-hosted), a missing permission, and the rest a prepare refuses with. It runs the checks a prepare runs, through the same code, in a transaction it rolls back: no review is recorded, no name reserved and no review quota spent; the name is the one check it cannot make, since the name is chosen at setup. The service is previewed on the environment's own cluster with the package's default settings. An environment that is not bound yet is checked the way setup would bind it, which needs the application deployed in that namespace. Capacity the platform cannot establish reads unknown, never fits. It is a preview, not a reservation: it takes no lock, so a review quota, capacity or policy that changes before the setup is prepared can still refuse it, and the prepare answers authoritatively. Requires the applications rollout, applications.read and clusters.read on the cluster to ask, and profiles.read to read the catalogue; a member who may read but not deploy gets every mode blocked with permission_required. package_version_id checks one version; otherwise limit (default 10, at most 25) and after page the catalogue in its own order. # Preview managed service eligibility (browser session) Source: https://docs.ankra.ai/api-reference/services/preview-managed-service-eligibility-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/eligibility Answers which catalogue package versions and modes one application environment - the namespace an application is, or is about to be, deployed to on a cluster - could set up right now, and for each that it cannot, the reason and what to do: a cluster that does not deploy through the Ankra native engine, a playground short of capacity, a mode that is not available (Ankra-hosted), a missing permission, and the rest a prepare refuses with. It runs the checks a prepare runs, through the same code, in a transaction it rolls back: no review is recorded, no name reserved and no review quota spent; the name is the one check it cannot make, since the name is chosen at setup. The service is previewed on the environment's own cluster with the package's default settings. An environment that is not bound yet is checked the way setup would bind it, which needs the application deployed in that namespace. Capacity the platform cannot establish reads unknown, never fits. It is a preview, not a reservation: it takes no lock, so a review quota, capacity or policy that changes before the setup is prepared can still refuse it, and the prepare answers authoritatively. Requires the applications rollout, applications.read and clusters.read on the cluster to ask, and profiles.read to read the catalogue; a member who may read but not deploy gets every mode blocked with permission_required. package_version_id checks one version; otherwise limit (default 10, at most 25) and after page the catalogue in its own order. # Publish an immutable private package version Source: https://docs.ankra.ai/api-reference/services/publish-an-immutable-private-package-version https://platform.ankra.app/openapi.json post /api/v1/org/service-packages Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Publish an immutable private package version (browser session) Source: https://docs.ankra.ai/api-reference/services/publish-an-immutable-private-package-version-browser-session https://platform.ankra.app/openapi.json post /org/service-packages Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Read a cluster's service placement policy Source: https://docs.ankra.ai/api-reference/services/read-a-clusters-service-placement-policy https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/cluster-policies/{cluster_id} Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Read a cluster's service placement policy (browser session) Source: https://docs.ankra.ai/api-reference/services/read-a-clusters-service-placement-policy-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/cluster-policies/{cluster_id} Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Read a service consumer binding Source: https://docs.ankra.ai/api-reference/services/read-a-service-consumer-binding https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/consumers/{consumer_id} Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Read a service consumer binding (browser session) Source: https://docs.ankra.ai/api-reference/services/read-a-service-consumer-binding-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/consumers/{consumer_id} Requires the applications rollout flag and live membership intersected with token scopes. Cluster policy reads require clusters.read and writes clusters.write. Consumer reads require applications.read and clusters.read; binding requires applications.deploy, clusters.operate and clusters.read. Locations are organisation declarations, not independently verified. No service deployment is executed. Updates require the current revision; zero creates only. Browser mutations require CSRF. # Read an accessible package contract Source: https://docs.ankra.ai/api-reference/services/read-an-accessible-package-contract https://platform.ankra.app/openapi.json get /api/v1/org/service-packages/{version_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Read an accessible package contract (browser session) Source: https://docs.ankra.ai/api-reference/services/read-an-accessible-package-contract-browser-session https://platform.ankra.app/openapi.json get /org/service-packages/{version_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Read current managed-service credential metadata Source: https://docs.ankra.ai/api-reference/services/read-current-managed-service-credential-metadata https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/credentials/{family_id} Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # Read current managed-service credential metadata (browser session) Source: https://docs.ankra.ai/api-reference/services/read-current-managed-service-credential-metadata-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/credentials/{family_id} Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # Remove a service consumer binding Source: https://docs.ankra.ai/api-reference/services/remove-a-service-consumer-binding https://platform.ankra.app/openapi.json delete /api/v1/org/service-admission/consumers/{consumer_id} Requires the applications rollout flag and live membership intersected with token scopes. Removing a binding requires applications.deploy, clusters.operate and clusters.read, and the binding's current revision as expected_revision. A binding a service that has not been released was planned with is kept (409); retire the service first. A pending review naming the binding no longer confirms. A binding whose cluster or application has since been removed can still be removed. No deployment changes. Browser mutations require CSRF. # Remove a service consumer binding (browser session) Source: https://docs.ankra.ai/api-reference/services/remove-a-service-consumer-binding-browser-session https://platform.ankra.app/openapi.json delete /org/service-admission/consumers/{consumer_id} Requires the applications rollout flag and live membership intersected with token scopes. Removing a binding requires applications.deploy, clusters.operate and clusters.read, and the binding's current revision as expected_revision. A binding a service that has not been released was planned with is kept (409); retire the service first. A pending review naming the binding no longer confirms. A binding whose cluster or application has since been removed can still be removed. No deployment changes. Browser mutations require CSRF. # Resume your service review Source: https://docs.ankra.ai/api-reference/services/resume-your-service-review https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/reviews/{review_id} Actor-scoped history on the primary database. Requires current applications rollout and applications.deploy; each row requires clusters.read for every destination and clusters.operate for the service cluster, intersected with live token scopes. Foreign actors, deleted resources and inaccessible reviews are hidden. Secret values, secret reference IDs and internal consumer binding IDs are never returned. Pending means only that the original review window has not expired; confirmation revalidates authority and configuration. Expired reviews require a new review; confirmed receipts remain confirmed after execution retention and are not readiness evidence. # Resume your service review (browser session) Source: https://docs.ankra.ai/api-reference/services/resume-your-service-review-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/reviews/{review_id} Actor-scoped history on the primary database. Requires current applications rollout and applications.deploy; each row requires clusters.read for every destination and clusters.operate for the service cluster, intersected with live token scopes. Foreign actors, deleted resources and inaccessible reviews are hidden. Secret values, secret reference IDs and internal consumer binding IDs are never returned. Pending means only that the original review window has not expired; confirmation revalidates authority and configuration. Expired reviews require a new review; confirmed receipts remain confirmed after execution retention and are not readiness evidence. # Revoke a managed-service credential reference Source: https://docs.ankra.ai/api-reference/services/revoke-a-managed-service-credential-reference https://platform.ankra.app/openapi.json delete /api/v1/org/service-admission/credential-grants/{grant_id} Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # Revoke a managed-service credential reference (browser session) Source: https://docs.ankra.ai/api-reference/services/revoke-a-managed-service-credential-reference-browser-session https://platform.ankra.app/openapi.json delete /org/service-admission/credential-grants/{grant_id} Requires the applications rollout and live organisation membership. Metadata reads require applications.read; writes require applications.write, intersected with live PAT scope. Browser mutations require CSRF. No secret values or store paths are returned; all responses are no-store. This registry operation does not connect or restart a workload. # Revoke a package recipient Source: https://docs.ankra.ai/api-reference/services/revoke-a-package-recipient https://platform.ankra.app/openapi.json delete /api/v1/org/service-packages/{version_id}/shares/{organisation_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Revoke a package recipient (browser session) Source: https://docs.ankra.ai/api-reference/services/revoke-a-package-recipient-browser-session https://platform.ankra.app/openapi.json delete /org/service-packages/{version_id}/shares/{organisation_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Share a package version with an organisation Source: https://docs.ankra.ai/api-reference/services/share-a-package-version-with-an-organisation https://platform.ankra.app/openapi.json put /api/v1/org/service-packages/{version_id}/shares/{organisation_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Share a package version with an organisation (browser session) Source: https://docs.ankra.ai/api-reference/services/share-a-package-version-with-an-organisation-browser-session https://platform.ankra.app/openapi.json put /org/service-packages/{version_id}/shares/{organisation_id} Requires live profiles.read, profiles.write (publication), or profiles.share (recipient management) permission, intersected with token scopes. Catalogue publication does not grant deployment permission, approve hosted operation, or assert runtime readiness. Shared metadata can remain visible after underlying profile access is revoked; contract reads then return 404. Profile fingerprints are rechecked on contract reads. # Decrypt content using SOPS Source: https://docs.ankra.ai/api-reference/sops/decrypt-content-using-sops https://platform.ankra.app/openapi.json post /api/v1/org/sops/decrypt Decrypts a SOPS-encrypted YAML document (`encrypted_yaml`) with the organisation's key and returns the plaintext. Requires `sops.use`. # Encrypt content using SOPS Source: https://docs.ankra.ai/api-reference/sops/encrypt-content-using-sops https://platform.ankra.app/openapi.json post /api/v1/org/sops/encrypt Encrypts the values selected by `encrypted_paths` in a YAML document (`yaml_content`) with the organisation's AGE key and returns the SOPS-encrypted document. Requires `sops.use`; a path that matches nothing is rejected. # Get SOPS configuration including public key Source: https://docs.ankra.ai/api-reference/sops/get-sops-configuration-including-public-key https://platform.ankra.app/openapi.json get /api/v1/org/sops/config Returns the organisation's SOPS configuration: its AGE public key, whether SOPS is enabled, and whether a key pair has been initialised. The private key is never returned. # Approve stack profile generation consent Source: https://docs.ankra.ai/api-reference/stack-profiles/approve-stack-profile-generation-consent https://platform.ankra.app/openapi.json put /api/v1/org/stack-profiles/summary-generation-policy A human with organisation-wide ai.manage approves the exact route and data-use notice previously displayed. Both acknowledgements and explicit bounded limits are required. Endpoint, credential and notice identities are server-derived; stale displayed consent conflicts. Approval does not enable deployment admission or publish a summary. Unknown commit outcomes require rereading the policy. Explicit OpenRouter routing is saved atomically with consent; a stale revision or digest rolls back both. Global AI routing and other tenants are unaffected. # Approve stack profile generation consent (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/approve-stack-profile-generation-consent-browser-session https://platform.ankra.app/openapi.json put /org/stack-profiles/summary-generation-policy A human with organisation-wide ai.manage approves the exact route and data-use notice previously displayed. Both acknowledgements and explicit bounded limits are required. Endpoint, credential and notice identities are server-derived; stale displayed consent conflicts. Approval does not enable deployment admission or publish a summary. Unknown commit outcomes require rereading the policy. Explicit OpenRouter routing is saved atomically with consent; a stale revision or digest rolls back both. Global AI routing and other tenants are unaffected. # Cancel a profile runtime qualification Source: https://docs.ankra.ai/api-reference/stack-profiles/cancel-a-profile-runtime-qualification https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/summary-runtime-qualifications/{operation_id}/cancel Requires fresh organisation-wide ai.manage and clusters.read on the recorded target; start and cancel also require clusters.operate. Token scopes and current membership are rechecked. Start derives the target from organisation staging settings, requires one recently checked-in agent, and serializes active runs across API replicas. Only a fixed synthetic nonce reaches the probe: no profile configuration, credentials or model request. Cancellation records a request; external cleanup remains unverified until independently observed. History remains readable when new starts are unconfigured. Browser writes require CSRF. # Cancel a profile runtime qualification (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/cancel-a-profile-runtime-qualification-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/summary-runtime-qualifications/{operation_id}/cancel Requires fresh organisation-wide ai.manage and clusters.read on the recorded target; start and cancel also require clusters.operate. Token scopes and current membership are rechecked. Start derives the target from organisation staging settings, requires one recently checked-in agent, and serializes active runs across API replicas. Only a fixed synthetic nonce reaches the probe: no profile configuration, credentials or model request. Cancellation records a request; external cleanup remains unverified until independently observed. History remains readable when new starts are unconfigured. Browser writes require CSRF. # Cancel a stack profile summary generation Source: https://docs.ankra.ai/api-reference/stack-profiles/cancel-a-stack-profile-summary-generation https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/summary-generations/{generation_id}/cancel Requires fresh profiles.write authority and current source ownership, including after a draft edit or AI consent revocation. Accepts only an empty JSON object. Repeated cancellation is idempotent; other terminal jobs return 409. Returns cancelled only after a confirmed transaction commit. Cancellation fences later results but does not confirm physical provider cancellation or data deletion. On outcome_unknown, poll the source status before retrying. # Cancel a stack profile summary generation (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/cancel-a-stack-profile-summary-generation-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/summary-generations/{generation_id}/cancel Requires fresh profiles.write authority and current source ownership, including after a draft edit or AI consent revocation. Accepts only an empty JSON object. Repeated cancellation is idempotent; other terminal jobs return 409. Returns cancelled only after a confirmed transaction commit. Cancellation fences later results but does not confirm physical provider cancellation or data deletion. On outcome_unknown, poll the source status before retrying. # Check a demo for service collisions Source: https://docs.ankra.ai/api-reference/stack-profiles/check-a-demo-for-service-collisions https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/demos/{workspace_id}/collisions Reads one running demo of a stack profile on the organisation's staging cluster and answers whether two services set up from a service package built on the profile could run side by side on one cluster. Every service gets a namespace of its own while each chart keeps its release name, so a cluster-scoped object the demo's releases own (ClusterRole, ClusterRoleBinding, CustomResourceDefinition, admission webhook configurations, PriorityClass, StorageClass, IngressClass, Namespace) whose name does not carry the demo's namespace (which its stack is named after) collides with a second service's copy of it. Services and Secrets whose names do carry it are listed as instance-specific, since a package connection naming them would not match another service. Only names, Secret types and Helm's release annotation are kept; the Secret data the listing carries is dropped, never kept or returned, and nothing is installed or written. `verdict` is `no_collision` only when every kind was read and nothing collides, `collisions` when something does, and `unknown` when the demo, or a kind, could not be read; `reason` says why. # Check a demo for service collisions (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/check-a-demo-for-service-collisions-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/demos/{workspace_id}/collisions Reads one running demo of a stack profile on the organisation's staging cluster and answers whether two services set up from a service package built on the profile could run side by side on one cluster. Every service gets a namespace of its own while each chart keeps its release name, so a cluster-scoped object the demo's releases own (ClusterRole, ClusterRoleBinding, CustomResourceDefinition, admission webhook configurations, PriorityClass, StorageClass, IngressClass, Namespace) whose name does not carry the demo's namespace (which its stack is named after) collides with a second service's copy of it. Services and Secrets whose names do carry it are listed as instance-specific, since a package connection naming them would not match another service. Only names, Secret types and Helm's release annotation are kept; the Secret data the listing carries is dropped, never kept or returned, and nothing is installed or written. `verdict` is `no_collision` only when every kind was read and nothing collides, `collisions` when something does, and `unknown` when the demo, or a kind, could not be read; `reason` says why. # Classify the service a stack profile runs Source: https://docs.ankra.ai/api-reference/stack-profiles/classify-the-service-a-stack-profile-runs https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/service-classification Asks the organisation's service_classification AI lane (the Quick tier, Claude Haiku, unless the organisation chose another model for the lane) what kind of managed service the profile's latest version runs: an engine applications connect to over a protocol Ankra supports (`protocol` set), an engine Ankra has no protocol for, an application or piece of infrastructure, or unclear. Only the profile's name and description and each add-on's name, chart, chart version, registry and namespace are sent; configuration values and manifests never are. The answer is validated before it is returned and cached per profile version for 24 hours, and every call is metered and gated by the monthly AI allowance. Model trouble is never an error status: `available` false with `unavailable_reason` says why no classification could be made (no_addons, allowance_exhausted, ai_unavailable or model_error). Requires profiles.read on a profile the caller can see. # Classify the service a stack profile runs (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/classify-the-service-a-stack-profile-runs-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/service-classification Asks the organisation's service_classification AI lane (the Quick tier, Claude Haiku, unless the organisation chose another model for the lane) what kind of managed service the profile's latest version runs: an engine applications connect to over a protocol Ankra supports (`protocol` set), an engine Ankra has no protocol for, an application or piece of infrastructure, or unclear. Only the profile's name and description and each add-on's name, chart, chart version, registry and namespace are sent; configuration values and manifests never are. The answer is validated before it is returned and cached per profile version for 24 hours, and every call is metered and gated by the monthly AI allowance. Model trouble is never an error status: `available` false with `unavailable_reason` says why no classification could be made (no_addons, allowance_exhausted, ai_unavailable or model_error). Requires profiles.read on a profile the caller can see. # Create a stack profile from a Helm chart Source: https://docs.ankra.ai/api-reference/stack-profiles/create-a-stack-profile-from-a-helm-chart https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/from-chart Makes one Helm chart from a registry your organisation can use into a stack profile and stores its first version, so a managed service can be published over it: one stack holding the chart, installed into the namespace named after each service. With `generated_secret`, the profile also carries a credentials release that generates that Secret's keys in the service's cluster on the first install and keeps them on every upgrade; Ankra never sees the values. Answers 201 with the profile and the version, and 422 naming the field a request cannot be built from. # Create a stack profile from a Helm chart (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/create-a-stack-profile-from-a-helm-chart-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/from-chart Makes one Helm chart from a registry your organisation can use into a stack profile and stores its first version, so a managed service can be published over it: one stack holding the chart, installed into the namespace named after each service. With `generated_secret`, the profile also carries a credentials release that generates that Secret's keys in the service's cluster on the first install and keeps them on every upgrade; Ankra never sees the values. Answers 201 with the profile and the version, and 422 naming the field a request cannot be built from. # Create stack profile Source: https://docs.ankra.ai/api-reference/stack-profiles/create-stack-profile https://platform.ankra.app/openapi.json post /org/stack-profiles Creates a stack profile from a stack deployed on one of your organisation's clusters (`source_cluster_id` and `stack_name`) and stores it as the profile's first version, answering 201 with the profile, the version and any warnings. Secret values captured from the stack are redacted; answers 409 when your organisation already has a profile with that name. # Delete stack profile logo Source: https://docs.ankra.ai/api-reference/stack-profiles/delete-stack-profile-logo https://platform.ankra.app/openapi.json delete /org/stack-profiles/{profile_id}/logo Removes the uploaded logo from a profile your organisation owns and returns the updated profile. Removing a logo that does not exist succeeds. # Deprecate stack profile version Source: https://docs.ankra.ai/api-reference/stack-profiles/deprecate-stack-profile-version https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/{profile_id}/versions/{version}/deprecation Marks a published version of a profile your organisation owns as deprecated, with a `reason` (`incompatibility`, `critical_bug` or `cve`), an optional note and up to 20 references, and returns the version. The profile's current version cannot be deprecated; make another version current first. # Deprecate stack profile version (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/deprecate-stack-profile-version-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/{profile_id}/versions/{version}/deprecation Marks a published version of a profile your organisation owns as deprecated, with a `reason` (`incompatibility`, `critical_bug` or `cve`), an optional note and up to 20 references, and returns the version. The profile's current version cannot be deprecated; make another version current first. # Edit a stack profile summary candidate Source: https://docs.ankra.ai/api-reference/stack-profiles/edit-a-stack-profile-summary-candidate https://platform.ankra.app/openapi.json put /api/v1/org/stack-profiles/summary-generations/{generation_id}/candidate Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Edit a stack profile summary candidate (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/edit-a-stack-profile-summary-candidate-browser-session https://platform.ankra.app/openapi.json put /org/stack-profiles/summary-generations/{generation_id}/candidate Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Generate a summary for a stack profile draft Source: https://docs.ankra.ai/api-reference/stack-profiles/generate-a-summary-for-a-stack-profile-draft https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/drafts/{draft_id}/summary-generation Explicitly requests an unpublished summary for this exact owned source revision. Requires current profiles.write and ai.use, approved current data-use policy, available quota and the default-off admission gate. The server locks and normalizes stored configuration; clients cannot provide facts, source digests, models or consent. Retained exact-key retries return their original job before new-admission gate/policy checks. New stale UUIDv7 keys refuse rather than dispatch again after metadata cleanup. No model call occurs in this HTTP request; approval/publication are separate human actions. # Generate a summary for a stack profile draft (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/generate-a-summary-for-a-stack-profile-draft-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/drafts/{draft_id}/summary-generation Explicitly requests an unpublished summary for this exact owned source revision. Requires current profiles.write and ai.use, approved current data-use policy, available quota and the default-off admission gate. The server locks and normalizes stored configuration; clients cannot provide facts, source digests, models or consent. Retained exact-key retries return their original job before new-admission gate/policy checks. New stale UUIDv7 keys refuse rather than dispatch again after metadata cleanup. No model call occurs in this HTTP request; approval/publication are separate human actions. # Generate a summary for a stack profile version Source: https://docs.ankra.ai/api-reference/stack-profiles/generate-a-summary-for-a-stack-profile-version https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary-generation Explicitly requests an unpublished summary for this exact owned source revision. Requires current profiles.write and ai.use, approved current data-use policy, available quota and the default-off admission gate. The server locks and normalizes stored configuration; clients cannot provide facts, source digests, models or consent. Retained exact-key retries return their original job before new-admission gate/policy checks. New stale UUIDv7 keys refuse rather than dispatch again after metadata cleanup. No model call occurs in this HTTP request; approval/publication are separate human actions. # Generate a summary for a stack profile version (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/generate-a-summary-for-a-stack-profile-version-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/{profile_id}/versions/{version}/summary-generation Explicitly requests an unpublished summary for this exact owned source revision. Requires current profiles.write and ai.use, approved current data-use policy, available quota and the default-off admission gate. The server locks and normalizes stored configuration; clients cannot provide facts, source digests, models or consent. Retained exact-key retries return their original job before new-admission gate/policy checks. New stale UUIDv7 keys refuse rather than dispatch again after metadata cleanup. No model call occurs in this HTTP request; approval/publication are separate human actions. # Get a connection probe on a demo Source: https://docs.ankra.ai/api-reference/stack-profiles/get-a-connection-probe-on-a-demo https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/demos/{workspace_id}/connection-probes/{probe_id} Reads back a connection probe requested on one demo of a stack profile. `verdict` is passed (a pod in the demo's namespace connected), failed (the canary ran and did not connect; `reason` says how) or unknown (nothing was learned yet or at all; `reason` says why). `status` is queued, probed or expired; a probe not run within ten minutes expires, and an expired probe says nothing about the connection. # Get a connection probe on a demo (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/get-a-connection-probe-on-a-demo-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/demos/{workspace_id}/connection-probes/{probe_id} Reads back a connection probe requested on one demo of a stack profile. `verdict` is passed (a pod in the demo's namespace connected), failed (the canary ran and did not connect; `reason` says how) or unknown (nothing was learned yet or at all; `reason` says why). `status` is queued, probed or expired; a probe not run within ten minutes expires, and an expired probe says nothing about the connection. # Get stack profile logo Source: https://docs.ankra.ai/api-reference/stack-profiles/get-stack-profile-logo https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/logo Returns the profile's uploaded logo image to any caller who can see the profile. A URL carrying `?v=` may be cached for a year; otherwise the response revalidates with an ETag. # Get stack profile metadata suggestions Source: https://docs.ankra.ai/api-reference/stack-profiles/get-stack-profile-metadata-suggestions https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/metadata-suggestions Returns the categories and tags already used on stack profiles your organisation owns, for reuse when describing a profile. # Get stack profile metadata suggestions (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/get-stack-profile-metadata-suggestions-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/metadata-suggestions Returns the categories and tags already used on stack profiles your organisation owns, for reuse when describing a profile. # Probe a proposed connection on a demo Source: https://docs.ankra.ai/api-reference/stack-profiles/probe-a-proposed-connection-on-a-demo https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/{profile_id}/demos/{workspace_id}/connection-probes Requests a probe of a proposed connection block against one running demo of a stack profile: the connection's own canary (PostgreSQL's SELECT 1, or the protocol's pod canary) runs in the demo's namespace on the organisation's staging cluster, before any package is published. The connection is validated as publishing validates it, and a credential given as a literal is refused, because the request is stored. The probe runs in the background; the answer is the queued probe, to be read back by `probe_id`. Requires stacks.deploy. # Probe a proposed connection on a demo (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/probe-a-proposed-connection-on-a-demo-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/{profile_id}/demos/{workspace_id}/connection-probes Requests a probe of a proposed connection block against one running demo of a stack profile: the connection's own canary (PostgreSQL's SELECT 1, or the protocol's pod canary) runs in the demo's namespace on the organisation's staging cluster, before any package is published. The connection is validated as publishing validates it, and a credential given as a literal is refused, because the request is stored. The probe runs in the background; the answer is the queued probe, to be read back by `probe_id`. Requires stacks.deploy. # Propose a service connection from a demo Source: https://docs.ankra.ai/api-reference/stack-profiles/propose-a-service-connection-from-a-demo https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/demos/{workspace_id}/connection-proposal Observes one running demo of a stack profile on the organisation's staging cluster and proposes the connection blocks a service package built from the profile could declare: one per Service port speaking PostgreSQL, Redis, NATS, OpenSearch, S3, VictoriaLogs or VictoriaMetrics, with each field mapped to a key of the Secret that holds the most of its credentials. Only Service ports and Secret key names are read, never Secret values, and nothing is written or tried. `observable` false means the demo's namespace could not be observed yet (the demo is not ready, or the staging cluster could not list its Services and Secrets) and `reason` says why; it is never an empty answer. Publishing the package lets the readiness canary verify the connection. # Propose a service connection from a demo (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/propose-a-service-connection-from-a-demo-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/demos/{workspace_id}/connection-proposal Observes one running demo of a stack profile on the organisation's staging cluster and proposes the connection blocks a service package built from the profile could declare: one per Service port speaking PostgreSQL, Redis, NATS, OpenSearch, S3, VictoriaLogs or VictoriaMetrics, with each field mapped to a key of the Secret that holds the most of its credentials. Only Service ports and Secret key names are read, never Secret values, and nothing is written or tried. `observable` false means the demo's namespace could not be observed yet (the demo is not ready, or the staging cluster could not list its Services and Secrets) and `reason` says why; it is never an empty answer. Publishing the package lets the readiness canary verify the connection. # Publish a stack profile draft Source: https://docs.ankra.ai/api-reference/stack-profiles/publish-a-stack-profile-draft https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/drafts/{draft_id}/publish Publishes the final normalized configuration atomically. Omit approved_summary_revision to publish without a summary. When attaching an approval, version and approved_summary_revision must be explicit positive integers; source identity, current authority and approval are rechecked under the publication locks. A mismatch returns 409 without creating a version or consuming the draft. No request may supply snapshots, source digests or approval identity. A 503 outcome is uncertain: reload the draft and versions before retrying. # Publish a stack profile draft (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/publish-a-stack-profile-draft-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/drafts/{draft_id}/publish Publishes the final normalized configuration atomically. Omit approved_summary_revision to publish without a summary. When attaching an approval, version and approved_summary_revision must be explicit positive integers; source identity, current authority and approval are rechecked under the publication locks. A mismatch returns 409 without creating a version or consuming the draft. No request may supply snapshots, source digests or approval identity. A 503 outcome is uncertain: reload the draft and versions before retrying. # Read a profile runtime qualification Source: https://docs.ankra.ai/api-reference/stack-profiles/read-a-profile-runtime-qualification https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/summary-runtime-qualifications/{operation_id} Requires fresh organisation-wide ai.manage and clusters.read on the recorded target; start and cancel also require clusters.operate. Token scopes and current membership are rechecked. Start derives the target from organisation staging settings, requires one recently checked-in agent, and serializes active runs across API replicas. Only a fixed synthetic nonce reaches the probe: no profile configuration, credentials or model request. Cancellation records a request; external cleanup remains unverified until independently observed. History remains readable when new starts are unconfigured. Browser writes require CSRF. # Read a profile runtime qualification (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-a-profile-runtime-qualification-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/summary-runtime-qualifications/{operation_id} Requires fresh organisation-wide ai.manage and clusters.read on the recorded target; start and cancel also require clusters.operate. Token scopes and current membership are rechecked. Start derives the target from organisation staging settings, requires one recently checked-in agent, and serializes active runs across API replicas. Only a fixed synthetic nonce reaches the probe: no profile configuration, credentials or model request. Cancellation records a request; external cleanup remains unverified until independently observed. History remains readable when new starts are unconfigured. Browser writes require CSRF. # Read a stack profile summary generation Source: https://docs.ankra.ai/api-reference/stack-profiles/read-a-stack-profile-summary-generation https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/summary-generations/{generation_id} Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Read a stack profile summary generation (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-a-stack-profile-summary-generation-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/summary-generations/{generation_id} Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Read an approved stack profile summary Source: https://docs.ankra.ai/api-reference/stack-profiles/read-an-approved-stack-profile-summary https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Read an approved stack profile summary (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-an-approved-stack-profile-summary-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/versions/{version}/summary Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Read approved summary source labels Source: https://docs.ankra.ai/api-reference/stack-profiles/read-approved-summary-source-labels https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary/source-labels Requires fresh profiles.read authority and access to the exact published version. Exposes only resource and admitted boolean parameter names already visible in that version, including authorized public and shared reads. Rechecks the current approval revision and normalizes the locked source against its private snapshot binding. No fingerprint, draft, candidate, configuration values or provider metadata is returned. Independent failures must not erase summary status; stale approval or revoked access requires a summary refresh. No AI call or consent is required. # Read approved summary source labels (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-approved-summary-source-labels-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/versions/{version}/summary/source-labels Requires fresh profiles.read authority and access to the exact published version. Exposes only resource and admitted boolean parameter names already visible in that version, including authorized public and shared reads. Rechecks the current approval revision and normalizes the locked source against its private snapshot binding. No fingerprint, draft, candidate, configuration values or provider metadata is returned. Independent failures must not erase summary status; stale approval or revoked access requires a summary refresh. No AI call or consent is required. # Read draft summary source labels Source: https://docs.ankra.ai/api-reference/stack-profiles/read-draft-summary-source-labels https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/drafts/{draft_id}/summary-source-labels Requires fresh profiles.write authority and ownership of the exact source. Reads bounded normalized configuration without AI calls or consent. Returns names only if the exact snapshot fingerprint matches. This independent read cannot alter approval, generate work or withdraw a summary. Names are private and excluded from model input and public/shared summary projections. Failure is unavailable or stale, never an empty label map. # Read draft summary source labels (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-draft-summary-source-labels-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/drafts/{draft_id}/summary-source-labels Requires fresh profiles.write authority and ownership of the exact source. Reads bounded normalized configuration without AI calls or consent. Returns names only if the exact snapshot fingerprint matches. This independent read cannot alter approval, generate work or withdraw a summary. Names are private and excluded from model input and public/shared summary projections. Failure is unavailable or stale, never an empty label map. # Read stack profile generation consent Source: https://docs.ankra.ai/api-reference/stack-profiles/read-stack-profile-generation-consent https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/summary-generation-policy Requires current organisation-wide ai.manage permission. Returns the server-derived data-use notice and current selected route without credential details. Null policy means no saved consent; unavailable route resolution keeps saved consent visible and consent_current null. No provider request is made. Admission is a separate default-off deployment gate. Optional routing preview changes only the displayed consent digest, never stored settings. Both routing query fields must occur exactly once. # Read stack profile generation consent (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-stack-profile-generation-consent-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/summary-generation-policy Requires current organisation-wide ai.manage permission. Returns the server-derived data-use notice and current selected route without credential details. Null policy means no saved consent; unavailable route resolution keeps saved consent visible and consent_current null. No provider request is made. Admission is a separate default-off deployment gate. Optional routing preview changes only the displayed consent digest, never stored settings. Both routing query fields must occur exactly once. # Read stack profile summary evidence Source: https://docs.ankra.ai/api-reference/stack-profiles/read-stack-profile-summary-evidence https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary/evidence/{fact_id} Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Read stack profile summary evidence (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-stack-profile-summary-evidence-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/versions/{version}/summary/evidence/{fact_id} Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Read summary progress for a stack profile draft Source: https://docs.ankra.ai/api-reference/stack-profiles/read-summary-progress-for-a-stack-profile-draft https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/drafts/{draft_id}/summary-generation Requires current profiles.write authority and source ownership. Returns private progress metadata only, with null generation when no job exists. Active jobs take priority, followed by the latest recorded job. Draft reads require the currently loaded source revision and return 409 if it changed; an older draft job is explicitly stale. Version reads never return jobs from a different immutable version. Candidate content requires the separate exact-source generation read. Database failures are not absence. # Read summary progress for a stack profile draft (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-summary-progress-for-a-stack-profile-draft-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/drafts/{draft_id}/summary-generation Requires current profiles.write authority and source ownership. Returns private progress metadata only, with null generation when no job exists. Active jobs take priority, followed by the latest recorded job. Draft reads require the currently loaded source revision and return 409 if it changed; an older draft job is explicitly stale. Version reads never return jobs from a different immutable version. Candidate content requires the separate exact-source generation read. Database failures are not absence. # Read summary progress for a stack profile version Source: https://docs.ankra.ai/api-reference/stack-profiles/read-summary-progress-for-a-stack-profile-version https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary-generation Requires current profiles.write authority and source ownership. Returns private progress metadata only, with null generation when no job exists. Active jobs take priority, followed by the latest recorded job. Draft reads require the currently loaded source revision and return 409 if it changed; an older draft job is explicitly stale. Version reads never return jobs from a different immutable version. Candidate content requires the separate exact-source generation read. Database failures are not absence. # Read summary progress for a stack profile version (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-summary-progress-for-a-stack-profile-version-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/versions/{version}/summary-generation Requires current profiles.write authority and source ownership. Returns private progress metadata only, with null generation when no job exists. Active jobs take priority, followed by the latest recorded job. Draft reads require the currently loaded source revision and return 409 if it changed; an older draft job is explicitly stale. Version reads never return jobs from a different immutable version. Candidate content requires the separate exact-source generation read. Database failures are not absence. # Read version summary source labels Source: https://docs.ankra.ai/api-reference/stack-profiles/read-version-summary-source-labels https://platform.ankra.app/openapi.json get /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary-source-labels Requires fresh profiles.write authority and ownership of the exact source. Reads bounded normalized configuration without AI calls or consent. Returns names only if the exact snapshot fingerprint matches. This independent read cannot alter approval, generate work or withdraw a summary. Names are private and excluded from model input and public/shared summary projections. Failure is unavailable or stale, never an empty label map. # Read version summary source labels (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/read-version-summary-source-labels-browser-session https://platform.ankra.app/openapi.json get /org/stack-profiles/{profile_id}/versions/{version}/summary-source-labels Requires fresh profiles.write authority and ownership of the exact source. Reads bounded normalized configuration without AI calls or consent. Returns names only if the exact snapshot fingerprint matches. This independent read cannot alter approval, generate work or withdraw a summary. Names are private and excluded from model input and public/shared summary projections. Failure is unavailable or stale, never an empty label map. # Remove stack profile version deprecation Source: https://docs.ankra.ai/api-reference/stack-profiles/remove-stack-profile-version-deprecation https://platform.ankra.app/openapi.json delete /api/v1/org/stack-profiles/{profile_id}/versions/{version}/deprecation Clears the deprecation notice from a version of a profile your organisation owns and returns the version. Clearing a version that is not deprecated changes nothing. # Remove stack profile version deprecation (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/remove-stack-profile-version-deprecation-browser-session https://platform.ankra.app/openapi.json delete /org/stack-profiles/{profile_id}/versions/{version}/deprecation Clears the deprecation notice from a version of a profile your organisation owns and returns the version. Clearing a version that is not deprecated changes nothing. # Review a stack profile summary candidate Source: https://docs.ankra.ai/api-reference/stack-profiles/review-a-stack-profile-summary-candidate https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/summary-generations/{generation_id}/review Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Review a stack profile summary candidate (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/review-a-stack-profile-summary-candidate-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/summary-generations/{generation_id}/review Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Revoke stack profile generation consent Source: https://docs.ankra.ai/api-reference/stack-profiles/revoke-stack-profile-generation-consent https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/summary-generation-policy/revoke Revokes the expected organisation policy revision atomically with its audit. Remains usable when the selected provider or credentials are unavailable. Existing workers must recheck the changed consent; revocation does not promise provider-side physical deletion. # Revoke stack profile generation consent (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/revoke-stack-profile-generation-consent-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/summary-generation-policy/revoke Revokes the expected organisation policy revision atomically with its audit. Remains usable when the selected provider or credentials are unavailable. Existing workers must recheck the changed consent; revocation does not promise provider-side physical deletion. # Share stack profile Source: https://docs.ankra.ai/api-reference/stack-profiles/share-stack-profile https://platform.ankra.app/openapi.json post /org/stack-profiles/{profile_id}/shares Gives another organisation, identified by `organisation_slug`, read and instantiate access to a profile your organisation owns, answering 201 with the share. A profile can be shared with up to 50 organisations, and sharing is refused while any stored version contains secret values. # Start a profile runtime qualification Source: https://docs.ankra.ai/api-reference/stack-profiles/start-a-profile-runtime-qualification https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/summary-runtime-qualifications Requires fresh organisation-wide ai.manage and clusters.read on the recorded target; start and cancel also require clusters.operate. Token scopes and current membership are rechecked. Start derives the target from organisation staging settings, requires one recently checked-in agent, and serializes active runs across API replicas. Only a fixed synthetic nonce reaches the probe: no profile configuration, credentials or model request. Cancellation records a request; external cleanup remains unverified until independently observed. History remains readable when new starts are unconfigured. Browser writes require CSRF. # Start a profile runtime qualification (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/start-a-profile-runtime-qualification-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/summary-runtime-qualifications Requires fresh organisation-wide ai.manage and clusters.read on the recorded target; start and cancel also require clusters.operate. Token scopes and current membership are rechecked. Start derives the target from organisation staging settings, requires one recently checked-in agent, and serializes active runs across API replicas. Only a fixed synthetic nonce reaches the probe: no profile configuration, credentials or model request. Cancellation records a request; external cleanup remains unverified until independently observed. History remains readable when new starts are unconfigured. Browser writes require CSRF. # Store stack profile logo Source: https://docs.ankra.ai/api-reference/stack-profiles/store-stack-profile-logo https://platform.ankra.app/openapi.json put /org/stack-profiles/{profile_id}/logo Uploads a PNG, JPEG or WebP logo of up to 512 KB, sent as the multipart field `file`, for a profile your organisation owns, and returns the updated profile. The upload replaces any external `logo_url`. # Withdraw an approved stack profile summary Source: https://docs.ankra.ai/api-reference/stack-profiles/withdraw-an-approved-stack-profile-summary https://platform.ankra.app/openapi.json post /api/v1/org/stack-profiles/{profile_id}/versions/{version}/summary/withdraw Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Withdraw an approved stack profile summary (browser session) Source: https://docs.ankra.ai/api-reference/stack-profiles/withdraw-an-approved-stack-profile-summary-browser-session https://platform.ankra.app/openapi.json post /org/stack-profiles/{profile_id}/versions/{version}/summary/withdraw Reads and decisions use the primary database with fresh permission, credential and source checks. Responses are private/no-store. Summary revisions never create or advance a workload profile version. No request accepts snapshots, approval identities or provider configuration. # Add an attachment to a support ticket Source: https://docs.ankra.ai/api-reference/support/add-an-attachment-to-a-support-ticket https://platform.ankra.app/openapi.json post /api/v1/org/support/tickets/{ticket_id}/attachments Uploads a file, sent as the multipart field `file`, to the ticket and returns the updated ticket. Answers 415 for an unsupported file type, 413 for a file that is too large, and 409 when the ticket already has the maximum number of attachments. # Close a support ticket Source: https://docs.ankra.ai/api-reference/support/close-a-support-ticket https://platform.ankra.app/openapi.json post /api/v1/org/support/tickets/{ticket_id}/close Closes the ticket and returns it. Closing a ticket that is already closed changes nothing. # Comment on a support ticket Source: https://docs.ankra.ai/api-reference/support/comment-on-a-support-ticket https://platform.ankra.app/openapi.json post /api/v1/org/support/tickets/{ticket_id}/comments Adds a comment of up to 10,000 characters to the ticket and returns the updated ticket. A ticket waiting on the customer moves back to in progress. # Create a support ticket Source: https://docs.ankra.ai/api-reference/support/create-a-support-ticket https://platform.ankra.app/openapi.json post /api/v1/org/support/tickets Opens a support ticket and returns it, either from an earlier review (`review_id`) or directly from a subject and description. Answers 409 when the review has expired or when a flagged draft is sent without `acknowledged: true`, and 404 for a cluster outside the organisation. # Download a support ticket attachment Source: https://docs.ankra.ai/api-reference/support/download-a-support-ticket-attachment https://platform.ankra.app/openapi.json get /api/v1/org/support/tickets/{ticket_id}/attachments/{attachment_id} Returns the attachment's file content with its original filename and content type. # Get a support ticket Source: https://docs.ankra.ai/api-reference/support/get-a-support-ticket https://platform.ankra.app/openapi.json get /api/v1/org/support/tickets/{ticket_id} Returns the ticket with its comments and attachments. Answers 404 for a ticket that does not belong to the organisation. # List support tickets Source: https://docs.ankra.ai/api-reference/support/list-support-tickets https://platform.ankra.app/openapi.json get /api/v1/org/support/tickets Lists the organisation's support tickets, paginated, with filters for status, severity, category and cluster, a free-text search `q`, and sorting. # Review a support ticket draft Source: https://docs.ankra.ai/api-reference/support/review-a-support-ticket-draft https://platform.ankra.app/openapi.json post /api/v1/org/support/tickets/review Checks a ticket draft before it is opened and returns a `review_id`, valid for one hour, together with a quality verdict, clarifying questions and possible duplicate tickets. Pass the `review_id` when creating the ticket. # Add a standalone VM Source: https://docs.ankra.ai/api-reference/upcloud-clusters/add-a-standalone-vm https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/vms Adds a standalone VM with the given `name` and `instance_type` on the cluster network; its location defaults to the bastion's. On a running cluster its creation starts at once and the result carries the `operation_id`; on a stopped cluster it is created at the next start. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Add node group Source: https://docs.ankra.ai/api-reference/upcloud-clusters/add-node-group https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/node-groups Adds a worker node group with the given `name`, `instance_type` and `count` (default 1), plus optional labels, taints and autoscaling settings. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Change ingress load balancer plan Source: https://docs.ankra.ai/api-reference/upcloud-clusters/change-ingress-load-balancer-plan https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/load-balancer-plan Record a new UpCloud plan for the cluster's ingress load balancer. Ankra re-renders the Traefik Service's load balancer annotation and the UpCloud cloud controller manager moves the existing balancer onto the plan in place. The plan is checked against UpCloud's catalogue; refusals (unknown plan, non-UpCloud cluster, no Ankra-managed cloud controller or ingress, cloud provider stack not settled) answer 409. # Change UpCloud NAT gateway plan Source: https://docs.ankra.ai/api-reference/upcloud-clusters/change-upcloud-nat-gateway-plan https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/gateway-plan Changes the plan of the cluster's UpCloud NAT gateways in place. This interrupts the cluster's outbound internet access while UpCloud rebuilds the gateway: expect several minutes without outbound access, much longer than the roughly 20 seconds UpCloud documents, so choose a quiet time and confirm with the user first. Answers 202 once the change is recorded: the gateway update job carries it out and reports it complete only after the gateway runs on the new plan and the cluster's agent has checked in again over the restored egress. A stopped cluster's gateways take the plan at the next start. Invalid states (empty or unlisted plan, non-UpCloud cluster, no gateway, a gateway being created or deleted) answer 409 with a detail message. # Check Kubernetes upgrade readiness Source: https://docs.ankra.ai/api-reference/upcloud-clusters/check-kubernetes-upgrade-readiness https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/upgrade-preflight Runs the read-only upgrade preflight for a hop to target_version: the hop plan per node, running operations and the operations block, PodDisruptionBudgets that would park a drain, CloudNativePG stopDelay and single-instance clusters, control-plane memory headroom from the cluster's metrics source, restore points, control-plane SSH reachability and, on k3s, the address pin. Nothing is changed. The Kubernetes version upgrade runs the same preflight first and refuses on a block. Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Create an UpCloud cluster Source: https://docs.ankra.ai/api-reference/upcloud-clusters/create-an-upcloud-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud Creates an UpCloud cluster from the request and starts provisioning it in the background, answering 201 with the new cluster's `cluster_id` and `name`. A configuration that fails validation answers 400 with the reason, and an organisation whose billing is restricted is refused with 402. # Delete a standalone VM Source: https://docs.ankra.ai/api-reference/upcloud-clusters/delete-a-standalone-vm https://platform.ankra.app/openapi.json delete /api/v1/clusters/upcloud/{cluster_id}/vms/{vm_id} Deletes a standalone VM: it is handed to teardown and its server is removed at the provider. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Delete node group Source: https://docs.ankra.ai/api-reference/upcloud-clusters/delete-node-group https://platform.ankra.app/openapi.json delete /api/v1/clusters/upcloud/{cluster_id}/node-groups/{group_name} Removes a node group by draining and deleting its workers one at a time through their PodDisruptionBudgets; a worker whose drain is refused stays in service and the operation fails with the reason. Pass `force_drain=true` to take the whole group down regardless. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. Requires `clusters.delete`. # Deprovision an UpCloud cluster Source: https://docs.ankra.ai/api-reference/upcloud-clusters/deprovision-an-upcloud-cluster https://platform.ankra.app/openapi.json delete /api/v1/clusters/upcloud/{cluster_id} Deletes the cluster and its cloud resources. A delete that would remove persistent volumes, or cannot tell whether it does, is refused with 409 unless `accept_volume_data_loss=true` is passed (see the deprovision-volumes read); `force=true` tolerates infrastructure that can no longer be reached. Active operations on the cluster are cancelled and the response carries the teardown's `operation_id`. # Diagnose bastion Source: https://docs.ankra.ai/api-reference/upcloud-clusters/diagnose-bastion https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/bastion/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `_bastion_diagnose` job over SSH and waits for its report within a bounded budget; a slower job answers the operation ids to poll. Browser mutations require CSRF. # Diagnose node Source: https://docs.ankra.ai/api-reference/upcloud-clusters/diagnose-node https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/nodes/{node_id}/diagnose Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `upcloud_node_diagnose` job, the REST twin of the assistant's ssh_diagnose_node: over the platform's bastion SSH lane it snapshots the node's OS-level state (the Kubernetes service unit and its journal, disk, memory, uptime, the kernel log, the firewall path to the API server) and classifies the symptoms without changing anything on the host, and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Get bastion health Source: https://docs.ankra.ai/api-reference/upcloud-clusters/get-bastion-health https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/bastion/health Bearer PAT authentication; RBAC permission `clusters.read`. Reports the verdict the bastion_health maintenance loop last recorded for the cluster's bastion or gateway host; it never probes the host itself. # Get bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/upcloud-clusters/get-bastion-ssh-allowlist https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.read`. Returns the cluster's stored bastion SSH source allowlist, as the update on the same path stored it. `configured` tells a cleared list (true, empty) from one that was never set (false, empty); both leave SSH open from anywhere. # Get current Kubernetes version Source: https://docs.ankra.ai/api-reference/upcloud-clusters/get-current-kubernetes-version https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/k8s-version Returns the cluster's Kubernetes distribution with its current, desired and observed versions, whether any node still has to converge to the desired version, whether automatic patch upgrades are on, and the version of each node. # Get current worker count Source: https://docs.ankra.ai/api-reference/upcloud-clusters/get-current-worker-count https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/worker-count Returns the cluster's current worker count and the minimum and maximum it can be scaled to. # Get disabled K3s components Source: https://docs.ankra.ai/api-reference/upcloud-clusters/get-disabled-k3s-components https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/k3s-disabled-components Read the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), on top of the provider shape's own defaults. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states (a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider) answer 409 with a detail message. # Get ingress load balancer plan Source: https://docs.ankra.ai/api-reference/upcloud-clusters/get-ingress-load-balancer-plan https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/load-balancer-plan Read the UpCloud plan Ankra has recorded for the cluster's ingress load balancer beside the plans its live load balancers run on. load_balancer_plan is null when no plan was ever recorded: Ankra then leaves the balancer's plan alone. When UpCloud cannot be read, load_balancers_observed is false and load_balancers is null rather than empty. # Latest restorable state snapshot of a UpCloud cluster Source: https://docs.ankra.ai/api-reference/upcloud-clusters/latest-restorable-state-snapshot-of-a-upcloud-cluster https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/state-snapshots/latest The newest captured state snapshot (an encrypted etcd snapshot) a start of the UpCloud cluster may restore: its id, what triggered it (`stop`, `power_schedule` or `scheduled`), when it was captured and the Kubernetes version it holds. `snapshot` is null when the cluster has no restorable capture. Pass the id as `restore_snapshot_id` to start the cluster from it. # List node groups Source: https://docs.ankra.ai/api-reference/upcloud-clusters/list-node-groups https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/node-groups Lists the cluster's worker node groups with each group's instance type, node count, autoscaling bounds, labels and taints. # List nodes Source: https://docs.ankra.ai/api-reference/upcloud-clusters/list-nodes https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/nodes Lists the cluster's machines with each one's role, node group, instance type, location, state, private and public IPs, the kubelet version it reports and, where recorded, the provider's own status. # List standalone VMs Source: https://docs.ankra.ai/api-reference/upcloud-clusters/list-standalone-vms https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/vms Lists the cluster's standalone VMs (servers on the cluster network that are not Kubernetes nodes) with their instance type, location, state, addresses and provider ID. # List UpCloud load balancer plans Source: https://docs.ankra.ai/api-reference/upcloud-clusters/list-upcloud-load-balancer-plans https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/load-balancer-plans List UpCloud's managed load balancer plans, read live with the given credential: the choices for load_balancer_plan when creating an UpCloud cluster. development runs one load balancer node; the production plans run two (highly_available) and differ in session capacity. # List UpCloud NAT gateway plans Source: https://docs.ankra.ai/api-reference/upcloud-clusters/list-upcloud-nat-gateway-plans https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/gateway-plans Bearer PAT authentication; RBAC permission `clusters.read`. Lists the live UpCloud NAT gateway plans the credential's account can create (GET /1.3/gateway/plans) with bandwidth, server count and whether the plan is highly available, plus the plan a create without gateway_plan gets and the notice a day-2 plan change carries. # Read control-plane sizing Source: https://docs.ankra.ai/api-reference/upcloud-clusters/read-control-plane-sizing https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/control-plane Returns the control-plane count, the counts the cluster supports, the control planes' instance type, and whether a count or instance-type change is possible right now, with the reason when it is not. # Read node Source: https://docs.ankra.ai/api-reference/upcloud-clusters/read-node https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/nodes/{node_id} Returns one machine of the cluster with its definition, its latest recorded info and data, and its dependencies and relationships. Answers 404 when the node is not part of the cluster. # Read node cloud-init log Source: https://docs.ankra.ai/api-reference/upcloud-clusters/read-node-cloud-init-log https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/nodes/{node_id}/cloud-init-log Bearer PAT authentication; RBAC permission `clusters.operate`. Dispatches the read-only `upcloud_node_cloud_init_log` job: over the platform's bastion SSH lane it reads the node's `cloud-init status --long` and the tail of /var/log/cloud-init-output.log (the debug surface for node-group user_data), and waits for the report within a bounded budget; a slower job answers the operation ids to poll. Repeated calls attach to an in-flight run instead of dispatching duplicates. Browser mutations require CSRF. # Read private cluster access Source: https://docs.ankra.ai/api-reference/upcloud-clusters/read-private-cluster-access https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/access-info Returns what you need to reach the cluster's machines over SSH: the bastion's public IP, the control-plane IPs and the cluster name. # Read SSH keys Source: https://docs.ankra.ai/api-reference/upcloud-clusters/read-ssh-keys https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/ssh-keys Returns the SSH key credentials authorised on the cluster's servers (`ssh_key_credential_ids`) and the organisation's SSH key credentials that can be attached. # Resize a standalone VM Source: https://docs.ankra.ai/api-reference/upcloud-clusters/resize-a-standalone-vm https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/vms/{vm_id}/instance-type Resizes a standalone VM in place to the given `instance_type`. Answers 404 when the VM does not exist. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resize control-plane nodes Source: https://docs.ankra.ai/api-reference/upcloud-clusters/resize-control-plane-nodes https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/control-plane/instance-type Changes the instance type of every control plane. On a running cluster the control planes are resized one at a time behind an etcd quorum check and a drain, and the response carries the `operation_id` to follow (clusters with fewer than three control planes are refused); on a stopped cluster the new type is applied at the next start and no operation is returned. # Resize node group Source: https://docs.ankra.ai/api-reference/upcloud-clusters/resize-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/node-groups/{group_name}/instance-type Changes the instance type of every worker in the node group. Each node is power-cycled, so the workers are drained and resized in bounded batches; `force_drain` bypasses PodDisruptionBudgets for that drain. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Resync SSH keys Source: https://docs.ankra.ai/api-reference/upcloud-clusters/resync-ssh-keys https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/ssh-keys/resync Re-applies the cluster's configured SSH keys to its servers and returns the IDs of the resources queued for the update. # Scale node group Source: https://docs.ankra.ai/api-reference/upcloud-clusters/scale-node-group https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/node-groups/{group_name}/scale Sets the node group's node count to `count`. A scale-down drains the removed workers through their PodDisruptionBudgets; `force_drain` bypasses them for this request. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Scale UpCloud cluster workers Source: https://docs.ankra.ai/api-reference/upcloud-clusters/scale-upcloud-cluster-workers https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/scale-workers Sets the cluster's worker count to `worker_count` and returns the previous and new counts. A scale-down drains the removed workers through their PodDisruptionBudgets unless `force_drain` is true. Requires `clusters.operate`. # Start an UpCloud cluster Source: https://docs.ankra.ai/api-reference/upcloud-clusters/start-an-upcloud-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/start Recreate the UpCloud cloud resources for a stopped cluster and reconcile it back to a running state. # Stop an UpCloud cluster Source: https://docs.ankra.ai/api-reference/upcloud-clusters/stop-an-upcloud-cluster https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/stop Initiate async deletion of UpCloud cloud resources (servers, networks, gateways, SSH keys) without removing the cluster from the database. # The persistent volumes deleting a UpCloud cluster removes Source: https://docs.ankra.ai/api-reference/upcloud-clusters/the-persistent-volumes-deleting-a-upcloud-cluster-removes https://platform.ankra.app/openapi.json get /api/v1/clusters/upcloud/{cluster_id}/deprovision-volumes What deleting the cluster does to its persistent volumes: the cloud volumes its CSI driver provisioned that `DELETE /api/v1/clusters/upcloud/{cluster_id}` deletes with the cluster, or keeps when the cluster's retention_policy retains them. Read it before the delete to name the volumes and ask for `accept_volume_data_loss`. A cluster whose inventory was never synced, or could not be read, is unknown, never none. # Update bastion SSH allowlist Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-bastion-ssh-allowlist https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/bastion/allowed-ips Bearer PAT authentication; RBAC permission `clusters.write`. Replaces the cluster's bastion SSH source allowlist and dispatches the bastion update that applies it: the bastion's SSH port then accepts only the listed IPv4 sources, the platform's own egress addresses and private network sources, and drops everything else. An empty list clears the allowlist. Clusters that never set one keep SSH open from anywhere. # Update control-plane count Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-control-plane-count https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/control-plane Changes the number of control planes to `count`. On a running cluster the control plane can only grow, and the new control planes join live (`mode` `rolling`, with an `operation_id` to follow); a reduction is applied offline to a stopped cluster and takes effect at its next start. # Update disabled K3s components Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-disabled-k3s-components https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/k3s-disabled-components Replace the k3s packaged components the platform keeps disabled on this running cluster (k3s --disable), for example ["traefik"] on a cluster that brings its own ingress. k3s distribution only. The list is stored on every k8s node definition of the cluster. The maintenance read lane compares each control plane's k3s server config against it within about five minutes and flags the ones missing an entry; the reconcile lane then patches the config and restarts k3s on one control plane at a time, in the cluster's k8s update chain. Control planes already carrying every entry are not touched. Invalid states answer 409 with a detail message: a kubeadm or Scaleway cluster, no k8s node resources, a cluster of another provider, a list that drops a stored entry (re-enabling a bundled component is not supported yet, so the list only grows), a list that adds coredns (load-bearing for in-cluster DNS), and any change while a node update, version upgrade or node restart is in progress on the cluster. # Update node-group labels Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-node-group-labels https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/node-groups/{group_name}/labels Replaces the Kubernetes labels on every node in the node group with the given `labels` map. By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update node-group taints Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-node-group-taints https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/node-groups/{group_name}/taints Replaces the taints on every node in the node group with the given `taints` list (each a `key`, an optional `value` and an `effect` of `NoSchedule`, `PreferNoSchedule` or `NoExecute`). By default the change runs in the background and the call answers 202 `{"status": "accepted"}` (409 if the same change is already in flight); pass `wait=true` to run it in the request and receive the result. # Update SSH keys Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-ssh-keys https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/ssh-keys Replaces the SSH key credentials authorised on the cluster's servers with `ssh_key_credential_ids` and starts rolling the keys out. An unknown or repeated credential, or two credentials carrying the same public key, is refused with 409. # Update UpCloud zone pool Source: https://docs.ankra.ai/api-reference/upcloud-clusters/update-upcloud-zone-pool https://platform.ankra.app/openapi.json put /api/v1/clusters/upcloud/{cluster_id}/zones Extend an UpCloud cluster's zone pool. Zones can only be added; the cluster must run network_mode 'wireguard_mesh' and the grown pool must keep at least 3 zones with at least 3 control planes. Every new zone gets its own router, private network and NAT gateway; day-2 growth then spreads across the pool. Answers 404 while the organisation's network_overlay feature flag is off. # Upgrade Kubernetes version Source: https://docs.ankra.ai/api-reference/upcloud-clusters/upgrade-kubernetes-version https://platform.ankra.app/openapi.json post /api/v1/clusters/upcloud/{cluster_id}/upgrade-k8s-version Upgrades the cluster to `target_version`, which must be in the supported version catalogue and must not downgrade or skip a minor version. The cluster must be online with no change in progress; the nodes are upgraded as a rolling operation whose `operation_id` is returned, and `force` continues past a node drain that PodDisruptionBudgets refuse. # Create an SSH key credential Source: https://docs.ankra.ai/api-reference/upcloud-credentials/create-an-ssh-key-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/upcloud/ssh-key Creates an SSH key credential from `name` and either `ssh_public_key` or `generate=true`; a generated RSA-4096 key pair's private key is returned once, in this response. A public key the organisation already stores under another credential is refused with 400. Requires `credentials.write`; answers 201. # Create an UpCloud API credential Source: https://docs.ankra.ai/api-reference/upcloud-credentials/create-an-upcloud-api-credential https://platform.ankra.app/openapi.json post /api/v1/credentials/upcloud Creates an UpCloud API credential from `name` and `api_token`. The token is checked against the UpCloud API before it is stored in Vault; an invalid token answers 400. Requires `credentials.write`; answers 201. # Get UpCloud credential capacity (browser session) Source: https://docs.ankra.ai/api-reference/upcloud-credentials/get-upcloud-credential-capacity-browser-session https://platform.ankra.app/openapi.json get /org/credentials/upcloud/{credential_id}/capacity Browser session authentication; any member of the organisation that owns the credential. Reads the capacity and usage the UpCloud API exposes for this credential, live from the provider. Pass `measurement_version=1` to receive the versioned contract, which keeps measurements the provider did not report as null; without it the response omits `measurement_version` and projects unknown OVH and HPE Morpheus measurements as 0 for older clients. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # Get UpCloud credential quotas (browser session) Source: https://docs.ankra.ai/api-reference/upcloud-credentials/get-upcloud-credential-quotas-browser-session https://platform.ankra.app/openapi.json get /org/credentials/upcloud/{credential_id}/quotas Browser session authentication; any member of the organisation that owns the credential. Reads the UpCloud limits and usage behind this credential and normalises them into one dimension per limit-bearing resource, with the dimensions under pressure listed first and the path to raising a limit. Pass `measurement_version=1` to keep an unreported `used` as null; without it an unreported usage is projected as 0, and `remaining` and `utilisation_percent` stay absent either way so no headroom is invented. Any other `measurement_version` answers 400. A credential of another provider or organisation answers 404; a provider API that refuses the read answers 400 with the provider's detail. # List SSH key credentials Source: https://docs.ankra.ai/api-reference/upcloud-credentials/list-ssh-key-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/upcloud/ssh-keys Lists the organisation's SSH key credentials. The list is the same under every cloud provider. Each item carries the stored public key with the fingerprint (as `ssh-keygen -l` prints it), key type and comment derived from it; a stored value that is not a readable key keeps its row, with a null `fingerprint` and `fingerprint_error` saying why. # List UpCloud API credentials Source: https://docs.ankra.ai/api-reference/upcloud-credentials/list-upcloud-api-credentials https://platform.ankra.app/openapi.json get /api/v1/credentials/upcloud Lists the organisation's UpCloud API credentials with their metadata; the secret material is never returned. # Get application service usage Source: https://docs.ankra.ai/api-reference/usage/get-application-service-usage https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/applications/{application_id}/usage The instance time and storage of every managed service instance whose reviewed plan serves the application, one line per instance, meter and period. An instance that serves other applications too says so with is_shared. Included during the pilot: no amount. Requires billing.read and applications.read. # Get application service usage (browser session) Source: https://docs.ankra.ai/api-reference/usage/get-application-service-usage-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/applications/{application_id}/usage The instance time and storage of every managed service instance whose reviewed plan serves the application, one line per instance, meter and period. An instance that serves other applications too says so with is_shared. Included during the pilot: no amount. Requires billing.read and applications.read. # Get managed service instance usage Source: https://docs.ankra.ai/api-reference/usage/get-managed-service-instance-usage https://platform.ankra.app/openapi.json get /api/v1/org/service-admission/instances/{instance_id}/usage A managed service instance's instance time and storage, one line per meter and period. Instance time runs from the instance's deployment to its release and is counted while its cluster could be seen; storage is what the bound volume claims in its namespace requested. Minutes the platform could not see read unknown, never zero. Included during the pilot: no amount. Requires billing.read, and that the instance be visible to the caller as GET .../instances/{instance_id} decides. # Get managed service instance usage (browser session) Source: https://docs.ankra.ai/api-reference/usage/get-managed-service-instance-usage-browser-session https://platform.ankra.app/openapi.json get /org/service-admission/instances/{instance_id}/usage A managed service instance's instance time and storage, one line per meter and period. Instance time runs from the instance's deployment to its release and is counted while its cluster could be seen; storage is what the bound volume claims in its namespace requested. Minutes the platform could not see read unknown, never zero. Included during the pilot: no amount. Requires billing.read, and that the instance be visible to the caller as GET .../instances/{instance_id} decides. # Get organisation usage Source: https://docs.ankra.ai/api-reference/usage/get-organisation-usage https://platform.ankra.app/openapi.json get /api/v1/org/billing/usage Everything the organisation used of Ankra over a window, per meter and period: worker vCPU-hours, ordered playground time, managed service instance time and storage, hosted logs and metrics, and AI requests and tokens. Each line carries its unit, quantity, coverage and price state; usage nobody measured reads unknown with no quantity, never zero, and only a line with a published rate carries an amount, in euro cents. Requires billing.read. # Get organisation usage (browser session) Source: https://docs.ankra.ai/api-reference/usage/get-organisation-usage-browser-session https://platform.ankra.app/openapi.json get /org/billing/usage Everything the organisation used of Ankra over a window, per meter and period: worker vCPU-hours, ordered playground time, managed service instance time and storage, hosted logs and metrics, and AI requests and tokens. Each line carries its unit, quantity, coverage and price state; usage nobody measured reads unknown with no quantity, never zero, and only a line with a published rate carries an amount, in euro cents. Requires billing.read. # Create a cluster variable Source: https://docs.ankra.ai/api-reference/variables/create-a-cluster-variable https://platform.ankra.app/openapi.json post /api/v1/org/clusters/imported/{cluster_id}/variables Creates a variable with a `name`, `value` and optional `description` on the cluster and returns it. Answers 409 when the cluster already has a variable with that name. # Create an organisation variable Source: https://docs.ankra.ai/api-reference/variables/create-an-organisation-variable https://platform.ankra.app/openapi.json post /api/v1/org/variables Creates an organisation-wide variable with a `name`, `value` and optional `description` and returns it. Answers 409 when a variable with that name already exists. # Delete a cluster variable Source: https://docs.ankra.ai/api-reference/variables/delete-a-cluster-variable https://platform.ankra.app/openapi.json delete /api/v1/org/clusters/imported/{cluster_id}/variables/{variable_name} Deletes the named variable from the cluster. Answers 404 when the cluster or the variable does not exist. # Delete an organisation variable Source: https://docs.ankra.ai/api-reference/variables/delete-an-organisation-variable https://platform.ankra.app/openapi.json delete /api/v1/org/variables/{variable_name} Deletes the named organisation variable. Answers 404 when it does not exist. # List cluster variables Source: https://docs.ankra.ai/api-reference/variables/list-cluster-variables https://platform.ankra.app/openapi.json get /api/v1/org/clusters/imported/{cluster_id}/variables Lists the variables defined on the cluster, with their values and descriptions. Answers 404 for a cluster outside the organisation. # List organisation variables Source: https://docs.ankra.ai/api-reference/variables/list-organisation-variables https://platform.ankra.app/openapi.json get /api/v1/org/variables Lists the organisation-wide variables with their descriptions. Needs `variables.read`. A variable marked `is_secret` is listed with `value: null`; read it back with the reveal route. Platform-owned settings are not listed. # List where an organisation variable is used Source: https://docs.ankra.ai/api-reference/variables/list-where-an-organisation-variable-is-used https://platform.ankra.app/openapi.json get /api/v1/org/variables/{variable_name}/usage Lists the live stack members in the organisation that refer to the variable as `${{ ankra. }}`: manifests, applications, add-on values and namespaces, and stack variables, each with its cluster and stack, and whether a cluster or stack variable of the same name shadows the organisation value. Needs `variables.read`; never carries a value. `unreadable` counts members whose stored content could not be decoded, so a non-zero count means the list may be incomplete. # Reveal an organisation variable value Source: https://docs.ankra.ai/api-reference/variables/reveal-an-organisation-variable-value https://platform.ankra.app/openapi.json get /api/v1/org/variables/{variable_name}/value Returns one variable's stored value, including a secret one. Needs `variables.write`, and every reveal is recorded in the organisation audit log. Answers 404 when it does not exist. # Update a cluster variable Source: https://docs.ankra.ai/api-reference/variables/update-a-cluster-variable https://platform.ankra.app/openapi.json put /api/v1/org/clusters/imported/{cluster_id}/variables/{variable_name} Replaces the variable's `value` and `description` and returns it; omitting `description` clears it. Answers 404 when the cluster or the variable does not exist. # Update an organisation variable Source: https://docs.ankra.ai/api-reference/variables/update-an-organisation-variable https://platform.ankra.app/openapi.json put /api/v1/org/variables/{variable_name} Partially updates the variable: each of `value`, `description` (null clears it) and `is_secret` is applied only when present, so a description or the secret flag can change without the value. Needs `variables.write`. Answers 404 when it does not exist and 403 for a platform-owned name. # Bring a workspace up Source: https://docs.ankra.ai/api-reference/workspaces/bring-a-workspace-up https://platform.ankra.app/openapi.json post /api/v1/org/workspaces Bearer PAT authentication; RBAC permission `workspaces.use`. Brings the caller's workspace for a repository and kind up, or answers the live one: 201 with a workspace this call created (placed on the repository's CI cluster, else the organisation's CI pool, else its pipeline cluster, and `provisioning` until a read finds its pod ready), 200 with the existing one. A ready workspace whose profile changed, and a failed one, are replaced. # Cancel a workspace run Source: https://docs.ankra.ai/api-reference/workspaces/cancel-a-workspace-run https://platform.ankra.app/openapi.json post /api/v1/org/workspaces/{workspace_id}/runs/{run_id}/cancel Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Cancels a run: a command that has not started never will, and a running one gets SIGTERM with its whole process group. The run reads `cancelled` (without `finished_at`) until a read sees it end. A run that already ended answers as it is. # Export a workspace run's results Source: https://docs.ankra.ai/api-reference/workspaces/export-a-workspace-runs-results https://platform.ankra.app/openapi.json post /api/v1/org/workspaces/{workspace_id}/runs/{run_id}/exports Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Uploads what a run left behind to the organisation's backup vault and answers a presigned GET of it: `patch` is the binary git diff an `apply` run made against the snapshot, `dirs` a gzipped tar of the named directories of the run's working directory. `{"empty": true}` when there is nothing to upload. # Get a workspace Source: https://docs.ankra.ai/api-reference/workspaces/get-a-workspace https://platform.ankra.app/openapi.json get /api/v1/org/workspaces/{workspace_id} Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Reads one workspace. A `provisioning` workspace is checked first (devbox-preflight in its pod) and answered `ready`, `failed` with the reason, or still `provisioning`. The owner's read counts as a use and pushes `expires_at` out by the idle TTL. # Get a workspace run Source: https://docs.ankra.ai/api-reference/workspaces/get-a-workspace-run https://platform.ankra.app/openapi.json get /api/v1/org/workspaces/{workspace_id}/runs/{run_id} Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Reads a run. One that has not ended is read from the pod (devbox-status) and its state recorded: `running`, `finished` with its exit code, `not_started` with the reason as its exit code, or `lost` when the pod no longer has its files. # Get repository workspace profiles Source: https://docs.ankra.ai/api-reference/workspaces/get-repository-workspace-profiles https://platform.ankra.app/openapi.json get /api/v1/org/pipeline-repositories/{repository_id}/workspace-profiles Bearer PAT authentication; RBAC permission `workspaces.use`. Answers the repository's workspace profiles keyed by kind: the image, size and service sidecars a workspace of that kind runs. A caller with `workspaces.manage` reads them whole; one with only `workspaces.use` reads which kinds exist, each as an empty profile. An empty object means its workspaces take their image from the repository's pipeline. # List workspaces Source: https://docs.ankra.ai/api-reference/workspaces/list-workspaces https://platform.ankra.app/openapi.json get /api/v1/org/workspaces Bearer PAT authentication; RBAC permission `workspaces.use`, or `workspaces.manage` with `all=true`. Lists the caller's live workspaces, newest first, or with `all=true` every member's and every run workspace (an unattended run's own workspace, `purpose` `run`), so one a run left behind can be found and deleted. `is_capped` is true when the listing stopped at 500. # Presign a workspace bundle upload Source: https://docs.ankra.ai/api-reference/workspaces/presign-a-workspace-bundle-upload https://platform.ankra.app/openapi.json post /api/v1/org/workspaces/{workspace_id}/bundles Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Answers a presigned PUT for the client's git delta bundle, keyed by its SHA-256 under the workspace's own prefix in the organisation's backup vault, and whether the workspace already holds it (`exists`: the PUT can be skipped). The bundle is named to a run by `bundle_object_key`. # Replace repository workspace profiles Source: https://docs.ankra.ai/api-reference/workspaces/replace-repository-workspace-profiles https://platform.ankra.app/openapi.json put /api/v1/org/pipeline-repositories/{repository_id}/workspace-profiles Bearer PAT authentication; RBAC permission `workspaces.manage`. Replaces the repository's workspace profiles; `null` or `{}` clears them. Every profile is validated as a workspace would be rendered from it. A running workspace picks the change up at its owner's next bring-up, which replaces it. # Start a workspace run Source: https://docs.ankra.ai/api-reference/workspaces/start-a-workspace-run https://platform.ankra.app/openapi.json post /api/v1/org/workspaces/{workspace_id}/runs Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Starts a command in the workspace against the client's snapshot and answers the run `accepted` (202) without waiting for it: the pod syncs the snapshot (base fetched from origin with the repository's credential, the uploaded bundle on top) and starts the command detached. Follow it on the stream. Only a redacted preview of the argv and the names of the forwarded environment variables are stored. # Stream a workspace run's output Source: https://docs.ankra.ai/api-reference/workspaces/stream-a-workspace-runs-output https://platform.ankra.app/openapi.json get /api/v1/org/workspaces/{workspace_id}/runs/{run_id}/stream Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Relays a run's output as server-sent events from the given byte offsets: `stdout` and `stderr` events carry `{"data": ""}` with the id `:` after them; `exit` (`{"code", "started", "state", "message"}`) ends a run that ran to an exit, and a run that ended without an exit code for a reason the platform could tell (its `error_message`, such as running the workspace out of memory) gets that explanation as one `stderr` event without an id just before it and as the exit's `message`; `reconnect` (`{"stdout_offset", "stderr_offset", "reason"}`) ends a stream the platform cut (lost frames, a gateway restart, a dropped session, a 30 minute connection) and says where to resume; `error` (`{"message", "state"}`) ends one for a run the workspace no longer has. A keepalive comment is sent after 15 s of silence. `Last-Event-ID` wins over the offset parameters. The command runs on in the pod whatever happens to the connection, so output arrives exactly once across reconnects. # Tear a workspace down Source: https://docs.ankra.ai/api-reference/workspaces/tear-a-workspace-down https://platform.ankra.app/openapi.json delete /api/v1/org/workspaces/{workspace_id} Bearer PAT authentication; RBAC permission `workspaces.use` for the caller's own workspace, `workspaces.manage` for another member's. Tears the workspace down (its namespace, pod and volume go with it) and answers it `destroyed`; a destroyed workspace answers as it is. A teardown the cluster refused leaves it `tearing_down` with the reason, and is retried by the next call. # Changelog Source: https://docs.ankra.ai/changelog Product updates and announcements Every change to the Ankra platform, newest first. Use the tag filter beside this page to narrow it to `Pipelines`, `Security`, `Cost`, `Clusters`, `AI`, `GitOps`, `CLI`, `API` or `Fixes` - the selection is in the URL, so a filtered view is a link you can share. Subscribe at [docs.ankra.ai/changelog/rss.xml](https://docs.ankra.ai/changelog/rss.xml) to get each change as it ships. Changes up to the end of August 2026 are kept as monthly records. From September 2026 each change has its own entry, and each week's smaller fixes are collected in one **Also shipped** entry. Command-line releases have their own [CLI changelog](/integrations/ankra-cli-changelog). **2026-11-03** ## Each project's notifications and AI board threads can go to its own Slack or Teams channel A notification route in a project now delivers only that project's notifications; an organisation-wide route still hears everything. A route created without a choice is in its creator's project, so check older routes. Board threads can bind a channel per project too, with the organisation's channel as the fallback. [One channel per project →](/guides/notification-routing#example-one-channel-per-project) **2026-11-02** ## Ankra AI also fixes a failed test step on a pull request A failed `run` step (your tests, or any script the pipeline runs) on the setup pull request or on a pull request from a branch of your repository now gets the same fix as a failed build or image gate: Ankra AI commits the fix to that branch and watches the run its commit starts; before, a failing test reached nobody. [When a pull request fails its checks →](/concepts/applications#when-a-pull-request-fails-its-checks) **2026-11-01** ## Concurrency flags take an expression, so pull requests supersede old runs while pushes always finish `cancel_in_progress` and `cancel_queued` now accept one `${{ }}` expression, settled for each run. One pipeline can let a pull request's new commit supersede its old run, while a push to your default branch always finishes and keeps only its newest waiting commit. It reads what is known before a run starts: the event, the commit and declared manual inputs. Anything it cannot settle counts as false, and `ankra pipeline validate` warns about it. [A concurrency policy per event →](/guides/pipeline-reference#a-concurrency-policy-per-event) **2026-11-01** ## Disconnect one application from a managed service that keeps running for the others In closed beta: you can take one application environment off a managed service without deleting the service. A review comes first, and says that the shared credential is not revoked. Nothing changes until the application's next deploy, which rolls its workloads off the connection. Ankra then removes the connection Secret once no pod reads it and, unless you keep it, the namespace's network access. Available in the new Ankra portal, the CLI, the API, the AI Assistant and MCP. [Disconnect an Application from a Managed Service →](/guides/disconnect-an-application-from-a-managed-service) **2026-10-31** ## Add an application environment on a new cluster, copied from one you already run In the new Ankra portal, **Add** beside an application's environments now offers **On a new cluster…**: Ankra creates the cluster with a copy of the stacks the environment's cluster runs, then deploys the application there as a new environment with the same image and settings and an address of its own. Secret overrides can come along; data does not. [Add an environment on another cluster →](/guides/application-environments#add-an-environment-on-another-cluster) **2026-10-31** ## Ankra leaves your own VMs, networks and storage alone on a shared Proxmox VE You can connect a Proxmox VE installation that already runs your own VMs. Ankra only adds what it needs next to them. When saving the credential would touch your networks or storage, it stops before changing anything. Creating a cluster clones only Ankra's own template and never overcommits the memory on your hosts. Every VM Ankra creates is tagged `ankra` so you can tell it apart from yours. [Connecting a Proxmox VE that already runs VMs →](/platform/credentials/proxmox#connecting-a-proxmox-ve-that-already-runs-vms) **2026-10-30** ## Ankra AI is in the Microsoft Teams Store Ankra AI is now published on the Microsoft Teams Store. In Teams, open **Apps**, search for **Ankra AI** and press **Add**, or use **Get it from the Teams Store** and **Open in Teams** on the portal's Microsoft Teams card. A tenant that restricts Store apps needs a Teams administrator to allow it once under **Manage apps**, and the app package stays available as a fallback for tenants that block Store apps entirely. [Ankra AI on the Microsoft Teams Store →](https://marketplace.microsoft.com/en-us/product/teams-app/WA200012870) **2026-10-30** ## The GitOps status shows the commit a cluster is in step with, and when each diverged file was found The GitOps status now shows the commit a cluster is in step with and the Git head Ankra last read, with times, and each listed preserved file's detection time and decision; `ankra cluster gitops status` prints them with CLI v0.52.2 or later. The last sync time can read days old on a current cluster, because a commit that leaves its directory unchanged moves the applied commit without a sync. Deciding a file still takes the API. [Applied Commit and Git Head →](/concepts/gitops#applied-commit-and-git-head) **2026-10-30** ## The DevOps AI model by Ankra: \$50 a month on every plan, and the model that answers when you run out Every catalog now carries **DevOps AI**, Ankra's own model, served on Ankra's own servers with the Ankra CLI skills preloaded. Every plan, the free tier included, gets \$50 a month on it, separate from the free monthly allowance; past that it bills per token. When the free allowance or your credit runs out, AI features switch to the DevOps AI model instead of stopping. Pick it in the model picker to use it on purpose. [The DevOps AI model by Ankra →](/platform/ai-provider#the-devops-ai-model-by-ankra) **2026-10-30** ## PersistentVolumes work on kubeadm Proxmox VE clusters A `PersistentVolumeClaim` on a kubeadm Proxmox VE cluster now gets a disk on your Proxmox storage, attached to the VM that runs the pod. Nodes register with the providerID and topology labels the bundled CSI needs, and the driver talks to the API root. Nodes created before the fix need replacing, and the API token needs `Datastore.Audit`. [Persistent Volumes on Proxmox VE Clusters →](/guides/proxmox-persistent-volumes) **2026-10-28** ## A GitOps sync that would remove much of a cluster waits for you, and a migration re-run names each database it replaces A GitOps sync that would remove a large share of a cluster, such as two of its four stacks, applies nothing until you remove them through Ankra. A restore into a database Ankra already restored into needs `--overwrite-database`, so the `ankra migrate up` cutover names each one. A stopped cluster's state snapshot is kept while it stays stopped. Ankra AI, MCP and service accounts can schedule a scale-to-zero stop only on a cluster a person confirmed as non-production. [Large Removals Are Held →](/concepts/gitops#large-removals-are-held) **2026-10-28** ## Resize the only control plane of a running Hetzner cluster, with a few minutes of API downtime A Hetzner cluster with one controller no longer has to be stopped to change the controller's server type. Ankra resizes it in place: the Kubernetes API is down for a few minutes while the server reboots, and workloads on the workers keep running. You accept the downtime first, with **Resize with API downtime** in the dashboard or `--accept-api-downtime` on the CLI. Other providers still need three controllers or a stopped cluster. [Change the control plane instance type →](/guides/operate-ankra-managed-clusters#change-the-control-plane-instance-type) **2026-10-28** ## A guide to running Ankra on Talos Linux clusters A new guide sets out what Talos owns - the nodes, the OS and the control plane - and what Ankra adds above the Kubernetes API, from Stacks and GitOps to backups, access and AI operations. It imports a Talos cluster, declares its first Stack, and covers Pod Security admission and the CNI. [Talos Linux Clusters →](/guides/talos-clusters) **2026-10-28** ## A guide to building Bazel repositories on Ankra Pipelines A new guide runs Bazel on Ankra Pipelines: Bazelisk and the output tree on the shared workspace, a disk and repository cache kept between runs, JUnit results from `bazel-testlogs`, an optional remote cache, and a Bazel-built binary scanned, gated and published as an image. [Bazel →](/guides/bazel-pipelines) **2026-10-28** ## The Hetzner guide covers the payment method, SSH key and server types you need first The Hetzner Clusters guide now says up front that creating a Hetzner cluster needs a payment method on the organisation and an SSH key credential, and walks through the new portal's wizard at **Infrastructure → Clusters → Add a cluster → Hetzner**. Its examples use `cpx12` and `cpx22`, because the `cx` line is often unavailable in `fsn1` and other locations; check what a location sells with `ankra cluster hetzner server-types` first. [Hetzner Clusters →](/guides/hetzner-clusters#before-you-start) **2026-10-28** ## A new film walks through a Hetzner cluster from create to an app over TLS The Hetzner Clusters guide has a new video: a real cluster in `fsn1` with one `cpx32` control plane and two `cpx22` workers, created from the dashboard and the CLI. All three nodes were Ready five minutes after the create call, and a small stack then answered on the cluster's own hostname with a Let's Encrypt certificate. [Hetzner Clusters →](/guides/hetzner-clusters#create-the-cluster) **2026-10-28** ## Projects: give each team its own room inside your organisation Every organisation on the new portal now has projects. A project holds its own clusters, applications, pipelines and cost; people, roles and the invoice stay organisation-wide. Everything you have is in your Default project, so nothing changes until you create a second one. Then move resources into it, invite **Project only** members who see nothing else, read cost by project, and use `ankra project` and `--project` from the CLI. [Projects →](/concepts/projects) **2026-10-27** ## With no AI allowance left, Ankra upgrades vulnerable Python packages on a pull request itself When the image gate blocks a pull request on vulnerable Python packages and your organisation's AI allowance is used up, Ankra no longer waits for the allowance. It moves each package a lock file pins to a release that fixes it, checks the new pins against the packages' own requirements, and commits that to the pull request on GitHub. The run that commit starts checks the result, and anything left waits for the allowance. [When a pull request fails its checks →](/concepts/applications#when-a-pull-request-fails-its-checks) **2026-10-27** ## Kubernetes upgrades have a guide with tested resume and recovery paths A new guide covers upgrading Kubernetes on clusters Ankra provisions: measured timings per node, the restore points that exist, and a pre-upgrade checklist. It shows how to resume a hop a PodDisruptionBudget or a cancel stopped with `ankra cluster operations retry`, how Ankra rolls back a failed control plane, and how to rebuild a cluster from its off-cluster state with a stop and a restoring start, each tested end to end. [Upgrade Kubernetes on a Cluster →](/guides/kubernetes-upgrades) **2026-10-27** ## Each application environment can follow its own branches, and a pipeline can see where a push deploys Give an environment a branch rule and pushes to matching branches deploy there: production follows `main`, development `develop` or every branch except `main`, staging `release/*`. Rules use the same patterns as `when.branches`, and changing one is never itself a deploy. In a pipeline, `ankra.environments` lists the environments the run's push deploys to, so a stage can run only on its way to production. [Environments and branch rules →](/guides/application-environments) **2026-10-26** ## Ankra AI fixes a failing pull request on its own branch, merge conflicts included A failed build or security gate on a pull request from a branch of your repository is now fixed on that pull request: Ankra AI commits the fix to its branch and watches the new run, with no second pull request. A pull request that conflicts with its base gets a merge commit that resolves it. Forks still get a diagnosis. With the AI allowance used up, the pull request says when the fix will start. [When a pull request fails its checks →](/concepts/applications#when-a-pull-request-fails-its-checks) **2026-10-26** ## A managed service's connection reaches its application without a redeploy In closed beta: once a managed service is ready, Ankra writes its connection Secret into the namespace of each application it serves on the same cluster, without redeploying or restarting anything, while no running pod of the application references it. When running pods already reference it, the application's next deploy writes it and rolls them together. The API's new `connection_delivery.pickup` says whether an application needs a deploy or a restart to read it. [Connect to a Managed Service →](/guides/connect-to-a-managed-service#how-your-application-receives-the-connection) **2026-10-26** ## Importing an application keeps the CI you already have When the repository you import already builds and pushes its image, from GitHub Actions, GitLab CI, Bitbucket Pipelines or CircleCI, Ankra now adopts that pipeline: it deploys the image your CI publishes and the setup pull request adds no second pipeline or Dockerfile. CI that only runs tests is kept. The analysis also tells you when your CI is not starting, and how to fix it. [When your repository already has CI →](/concepts/applications#when-your-repository-already-has-ci) **2026-10-24** ## The welcome credit is now €59 for 60 days New organisations now unlock €59 of welcome credit when they save a payment method, instead of €249. The credit is still valid for 60 days. New Ankra Cloud accounts receive the same €59 sign-up credit. Credit that was granted before this change keeps its original amount and expiry date. [Welcome credit →](/platform/billing#welcome-credit) **2026-10-23** ## The merge train keeps testing while GitHub declines a merge When GitHub declines the first car's merge, for example because a required check is still running, the pull requests behind it now keep building and testing instead of waiting. A merge declined because a required check failed leaves the train after 15 minutes instead of three hours, so one red check no longer holds the train. [The merge train →](/pipelines/merge-train#merges-github-declines) **2026-10-22** ## Approve a portability or next.config pull request in the Launch Plan and Ankra opens it A portability pull request, or the next.config pull request that moves your `vercel.json` routing into `next.config`, is now an action you can approve in the Launch Plan. Open it once under your approval and Ankra opens it on your repository at the plan's commit. If Ankra cannot make the change with confidence, it says why a person has to, and opens nothing. Ankra never merges these pull requests. [Portability pull requests →](/use-cases/vercel-to-ankra#portability-pull-requests) **2026-10-22** ## Move a pull request to the front of the merge train With CLI 0.47.0 or later, an organisation administrator can put a pull request at the front of a repository's merge train with `ankra pipeline train front `. It goes ahead of every pull request that is still waiting or being tested, and those are tested again on top of it. Pull requests that already passed or are being merged stay ahead. [The merge train →](/guides/ankra-pipelines#the-merge-train-test-every-pull-request-as-it-will-merge) **2026-10-21** ## Ankra AI acts on an approved Launch Plan without asking again Once you approve a Launch Plan, Ankra AI runs the approved actions without asking you again: in your chat, in an agent run, or over MCP. Anything outside the plan still asks first. Each approved action runs once. The approval stops working when it expires, when you revoke it, or when the plan changes. Only your own runs can use it. [MCP server →](/platform/mcp-server#how-calls-behave) **2026-10-21** ## Move a vendor's data into your cluster under the Launch Plan's one approval, with a rollback window In closed beta: when you move a service into your cluster, the Launch Plan now carries its data migration. Postgres moves by dump and restore through a backup vault, and S3-compatible object storage is copied with checksums. At one cutover the vendor sources go read-only and the last changes are copied, then your app's variables switch in one change. During a rollback window, `ankra application data-migration rollback` puts everything back. Ankra never deletes data at the vendor. [Moving your data →](/use-cases/vercel-to-ankra#moving-your-data-closed-beta) **2026-10-21** ## The proof of a Vercel project's setup pull request is compared with Vercel's own preview For a Vercel project, the proof run now requests the same routes on Vercel's preview of the commit as on the ankra.cc preview. It compares status, content type, JSON keys or page title and landmarks, and size. The proof card marks each route as match, differs (with the reason) or unknown. Unknown is never counted as a match, and a difference is a warning that does not fail the proof. [Compared with your Vercel preview →](/use-cases/vercel-to-ankra#compared-with-your-vercel-preview) **2026-10-20** ## The PR shepherd drives the pull requests Ankra opens to ready, and merges them only if you opt in An **Ankra PR shepherd** check now follows each setup pull request. When your CI never starts, Ankra runs its own pipeline, build and proof on the head. It merges the base branch in when the pull request falls behind, resolves conflicts only in Ankra's own files, and fixes its own failures up to 3 times. It merges only where you opted in, per organisation or per application. [The PR shepherd →](/concepts/applications#the-pr-shepherd) **2026-10-20** ## Vercel to Ankra: one Launch Plan, one approval, and your crons and variables come along On Vercel and GitHub pages, the Chrome extension now shows where your project stands and one Launch Plan to approve. Bring variables over from Vercel. In the setup pull request: * A standalone Next.js build ships as just its server. * Vercel crons become CronJobs. * Rewrites and headers are listed with where to move them. [Vercel to Ankra →](/use-cases/vercel-to-ankra) **2026-10-20** ## A run handed to Ankra platform builders is now scanned, gated and published When a cluster's agent is too old to run pipeline steps and the build goes to Ankra platform builders, its scans now run on Ankra's platform scanner instead of being skipped. The image gate judges them under the same policy, and the image is published when the gate approves. If the scanner is unavailable, the scan fails with the reason and nothing is published. [Ankra platform builders →](/guides/ankra-pipelines#ankra-platform-builders) **2026-10-19** ## Guided setup takes a new organisation from a free cluster to a live website Four steps: create a free cluster, add a payment method to unlock €249 of credit for 60 days, choose the Ankra default or a new GitHub repository, then describe a website in one sentence and Ankra deploys it with a public address. Welcome credit now comes with a saved card, not at sign-up, and you are never charged without a top-up. Playgrounds are moving to Ankra Cloud dev clusters: free for 24 hours, kept with a card. [Guided Setup →](/get-started/guided-setup) **2026-10-19** ## New use case: self-host an AI agent observability backend from one shared stack profile A vendor can now hand its whole backend to customers as one shared stack profile. The new use case walks a platform engineer through Moyai's: the trace API, highly available Postgres, S3, an Iceberg REST catalog, an LLM and Prometheus, applied to Proxmox, HPE Morpheus, an imported cluster or any cloud with a dry run, secret files and one deploy, then upgraded with a rollout. [Self-host an AI agent observability backend on-prem →](/use-cases/self-host-ai-agent-observability) **2026-10-17** ## A card is required to create resources on Ankra Cloud whatever credit you hold, and automatic top-up stays off until you turn it on On Ankra Cloud, creating a resource now needs a card on file, whatever credit the account holds. Organisations Ankra invoices under a custom agreement need no card. Credit is still spent first, and the card pays only the rest. On the Ankra platform, saving a card does not switch automatic top-up on: you turn it on under **Billing** with your own threshold, amount and daily and monthly caps, and Billing warns you while it is not on. [Credit and payment method →](/platform/billing#credit-and-payment-method) **2026-10-17** ## A cancelled run now runs its finally stages before it concludes Cancelling a run that had started now stops its main and `on_failure` stages, then runs its `finally` stages before the run concludes `cancelled`, so cleanup happens after a cancel too. This replaces the 2026-10-16 note that a cancelled run skipped them. `ankra pipeline cancel --force` skips the cleanup and concludes the run at once. [Cancelling a run →](/guides/pipeline-reference#cancelling-a-run) **2026-10-17** ## List and clear what Ankra's AI remembers about you from the CLI `ankra ai memories list` shows the memories Ankra's AI has saved for you in the current organisation, newest first, and `ankra ai memories clear` deletes all of them after a confirmation. Both act on your own memories only. They need the ankra CLI v0.40.0 or later. [AI Memory and Feedback →](/platform/ai-memory) **2026-10-17** ## Security patches, Kubernetes patch upgrades and other routine changes are recorded as pre-approved standard changes The audit log now marks routine changes as pre-approved standard changes for ISO 27001 change management, naming the catalogue entry and why each qualified: security-advisory add-on upgrades, Kubernetes patch upgrades, autoscaling, scheduled power actions, access expiry and token rotation. Security upgrades also name the advisories they fix in their GitOps commit. Pre-approval is on by default; admins can turn it off under **Organisation settings > Security**. [Change Management →](/security/change-management) **2026-10-17** ## Manage everything the CLI can create from Terraform with provider 0.3.0 The Ankra Terraform provider now covers what the Ankra CLI can create, change or delete: clusters on every cloud, managed Kubernetes, node groups, credentials, Helm registries, variables, cluster settings, alerts, DNS, applications, pipelines, stack profiles, backups, members, tokens and organisation settings. Set `project_id` on the provider if your organisation uses projects. [Ankra Terraform Provider →](/integrations/terraform) **2026-10-17** ## Run every stage when you need to, keep chosen stages from ever being skipped, and skip a push with \[skip ci] Path filters can now be set aside. Run every stage with `--full` on `ankra pipeline run` or `rerun`, or add the `ankra:full-run` label to a GitHub pull request. A `full_run` block can run every push to main in full, and each full run records why. Your organisation can name stages a pull request can never filter away. A pushed commit with `[skip ci]` records a skipped run, except on the default branch by default. [Setting path filters aside →](/guides/pipeline-reference#setting-path-filters-aside) **2026-10-16** ## on\_failure stages now run only when a run fails, and finally stages run after a failure too An `on_failure` stage ran after a green run and was skipped after a failed one, and a `finally` stage was skipped whenever the run failed. Now an `on_failure` stage runs only when a main stage failed, and a `finally` stage runs whatever the outcome. A run cancelled as a whole does not run its `finally` stages yet. [Failure and cleanup stages →](/guides/pipeline-reference#failure-and-cleanup-stages) **2026-10-14** ## Applications can refuse images that did not come from your pipeline gate Set an application to `warn` or `enforce` and each deploy checks that its images carry a signed statement from your organisation's pipeline. Manage the mode and your signing keys in the UI or with `ankra application provenance` and `ankra org provenance`. [Verified provenance at deploy →](/security/deploy-provenance) **2026-10-14** ## Chat with Ankra and deploy from inside Lovable with the Chrome extension The Ankra — Chat & Deploy extension for Chrome adds an **Ask Ankra** button to your Lovable projects. Chat with Ankra AI, connect the project's GitHub repository, and deploy it to your cluster without leaving Lovable. If your organisation has no cluster yet, it can request a temporary Playground for you. While the Chrome Web Store listing is in review, download it from ankra.ai and load it into Chrome yourself. [Chat and deploy from inside Lovable →](/use-cases/lovable-to-ankra#chat-and-deploy-from-inside-lovable) **2026-10-13** ## A new application's setup pull request now fixes itself when its checks fail When the setup pull request fails its build or security checks, often on dependencies the repository already had, Ankra AI commits a fix to that same pull request and its status comment says so in plain words. Creating the application from the portal also approves the generated pipeline in your name, so the first runs are not held for an approval. [Applications →](/concepts/applications#when-the-setup-pull-request-does-not-pass-its-checks) **2026-10-11** ## Run your tests and lint in Ankra Workspaces instead of on your laptop `ankra exec` runs a command for your checkout, uncommitted changes included, in a warm pod on your organisation's CI pool and streams the output back. `ankra dev install` routes the heavy commands you and your coding agents already type - `go test`, `golangci-lint`, `pnpm typecheck` - there automatically. A repository whose pipeline runs elsewhere can be connected for Workspaces only. [Run tests and lint in Ankra Workspaces →](/guides/ankra-workspaces) **2026-10-10** ## The merge train: every pull request is tested as it will merge Turn on a repository's merge train and Ankra tests each queued pull request squashed onto the base branch and the pull requests ahead of it, then merges it through GitHub when that run passes. A failing pull request leaves the train and the rest are retested without it. Every merge is a tree a run already passed, so `promote_from: pull_request` publishes the tested image instead of rebuilding. GitHub only. [The merge train →](/guides/ankra-pipelines#the-merge-train-test-every-pull-request-as-it-will-merge) **2026-10-09** ## A repository Ankra cannot build no longer gets a setup pull request Setup now stops before the pull request when a repository holds nothing that keeps running, or no Dockerfile that Ankra found or could write. It used to open a pull request that could never deploy a working application. The setup ends as failed, its message says what Ankra found and what to add, and it ends: No setup pull request was opened and nothing was deployed. [How it works →](/concepts/applications#how-it-works) **2026-10-09** ## Promote a pull request's tested image instead of rebuilding on merge A publish stage can now declare `promote_from: pull_request`. When a merge carries exactly the files a pull request run already built, scanned, gated and tested, the merge publishes that run's approved image as its own `sha-<7>` tag instead of building and testing it again. Any other merge builds in full as before, and the run says why. [Promote a tested image instead of rebuilding →](/guides/pipeline-reference#promote-a-tested-image-instead-of-rebuilding) **2026-10-09** ## Pipeline steps record what they used, and each stage gets a resource suggestion Every step now records its memory peak and CPU use next to what it asked for, so a stage that declares 14Gi and peaks at 8 GiB shows on the run itself. The API suggests `resources.memory` and `resources.cpu` per stage from its latest successful runs, and says how many runs each suggestion rests on. Suggestions are advice only: your pipeline changes only if you change it. [Capacity and concurrency →](/guides/ankra-pipelines#capacity-and-concurrency) **2026-10-09** ## Acknowledge or accept findings no add-on owns, per workload or per image A disposition needed a finding attributed to an add-on, so findings in operator-managed database pods, plain manifests, control-plane pods and charts outside the catalogue could never be acknowledged or accepted. `ankra security dispositions preview|create --scope workload` pins the decision to one workload, and `--scope image` to an image digest wherever it runs. The same `scope` works from the API and the MCP tools. [What a disposition is pinned to →](/guides/cluster-security#what-a-disposition-is-pinned-to) **2026-10-09** ## Generated pipelines keep their package caches between runs A new application's generated test stage now keeps its package caches: npm, pnpm or Yarn, Go modules and builds, pip, uv, Poetry or Pipenv, and cargo. Each is keyed on the lock file and saved only after the step passes. A pull request starts warm from the default branch and never writes into it. `fallback: none` keeps a cache off shared volumes on older agents. [Caches →](/guides/pipeline-reference#caches) **2026-10-09** ## UpCloud node groups: a plan from another family replaces the nodes instead of stranding them UpCloud changes a server's plan in place only within its plan family, so resizing a STARTER group to a general-purpose plan such as `4xCPU-8GB` was refused after each node had been stopped, and the retries left the nodes off. A cross-family change now replaces each node with the new plan and the resize says so, a control-plane change across families is refused up front, and a server whose in-place resize fails is powered back on. [Node groups →](/platform/cluster-nodes#node-groups) **2026-10-09** ## Run every pipeline pod on a dedicated node group `ankra cluster agent ci set --node-group pipelines` puts every pipeline pod of the CI cluster on that node group: step pods, their cache and artifact helpers, and image builds select its nodes and tolerate its taints, with no pipeline change and no Kyverno policy. The portal's Pipelines settings page edits the placement, the CI slots and the storage class. [Step placement →](/guides/ankra-pipelines#step-placement) **2026-10-08** ## Ankra Cloud dev clusters: Kubernetes on one server Ankra Cloud Kubernetes can create a dev cluster: one server running k3s that is the control plane, the only node and its own network edge. It has no control-plane fee, so you pay for the server. It has no high availability and is not for production. Pick **Dev cluster on one server** in **Create Cluster**, or pass `--dev-cluster` to `ankra cluster managed create` (CLI v0.24.0). It is offered zone by zone, and Ankra Cloud is in closed beta. [Dev clusters →](/guides/ankra-cloud-kubernetes#dev-clusters) **2026-10-08** ## Path filters per event: check a pull request's component, build everything on push A stage's `when` can name its event: `when.pull_request.paths` filters pull requests, `push: {}` leaves pushes unfiltered, so a monorepo needs one stage per component, not duplicates. A stage whose `if:` calls `always()` or `skipped()` now runs past a skipped need. Converted workflows keep their own pull request paths, and `ankra pipeline validate --changed-file` prints each stage against each event. [Event-scoped filters →](/guides/pipeline-reference#event-scoped-filters) **2026-10-08** ## Pull request runs apply their path filters to the files the pull request changes A pull request run had no list of changed files, so every `when.paths` filter passed and every stage ran. Ankra now reads the changed files from GitHub, GitLab or Bitbucket, up to 300, and keeps them on the run for dispatch and re-runs. A list it cannot read in full never skips a stage, and the run's `paths_unverified` says why. [How path filters decide →](/guides/pipeline-reference#triggers) **2026-10-08** ## Every sign-in is bound to the browser that started it Every sign-in - password, social account, your organisation's single sign-on provider or an invitation link - now ties the identity provider's answer to the browser that asked for it, with OpenID Connect `state`, `nonce` and PKCE. A response this browser did not start is never completed; the sign-in restarts once instead. Connecting another sign-in method is bound the same way. Day to day nothing changes: if a long-idle tab lands on "sign in again", signing in once more completes it. [Sign-in and SSO →](/security/identity-and-access#sign-in-and-sso) **2026-10-08** ## See what your organisation used of Ankra, meter by meter One usage report covers worker vCPU-hours, playgrounds, managed service time and storage, Ankra hosted logs and metrics, and AI, per month or day, split by cluster, application or managed service. Each line says how completely it was measured - a period nobody measured reads as not measured, never 0 - and what it costs. Managed services and hosted logs and metrics are included during the pilot. Read it through the API, the AI Assistant or `ankra org usage` (CLI v0.27.0). [Usage →](/platform/usage) **2026-10-08** ## Steps of a run stay on the node their workspace is attached to Each step of a run, and its cache and artifact helpers, now runs on the node its `rwo` workspace is attached to, so sequential steps no longer wait for the volume to move. When that node is gone, cordoned or not Ready, the step log says why the pin was dropped. Helpers also carry the step's tolerations, so caches restore on a tainted node group. `ankra pipeline get` shows where each step ran. [Workspace access →](/guides/pipeline-reference#top-level-keys) **2026-10-08** ## A step says whether its caches restored, and why not A step's cache result is now `hit` only when every cache restored, `partial` when some did, and `restore_failed` when a restore broke, each cache carrying a reason such as `download_failed`. `ankra pipeline get` shows it in a CACHE column, `--step ` prints the per-cache table, and the run timeline shows a chip. Where a step's network allows it, the restore runs inside the step's own pod. [What a step reports about its caches →](/guides/pipeline-reference#what-a-step-reports-about-its-caches) **2026-10-08** ## Ankra Cloud Kubernetes: development and production control planes An Ankra Cloud Kubernetes cluster now has a control plane you choose at create. **Production** runs three replicas, is highly available and costs €40 a month per cluster. **Development** runs one replica and has no control-plane fee, so you pay for the nodes only. It is not for production: an upgrade or a host node failure interrupts the Kubernetes API while it restarts. The choice cannot be changed afterwards. [Choosing a control plane →](/guides/ankra-cloud-kubernetes#choosing-a-control-plane) **2026-10-07** ## A publish stage can give the published image extra tags, such as latest `kind: publish` takes a `publish.tags` list: each entry becomes another tag on the digest the gate approved, in the application's repository, with no second push - a moving `latest`, or `"0.2.${{ ankra.run.number }}"`. Expressions resolve when the run is planned, the stage's `when:` applies, and a tag the registry refuses fails the step by name. [The publish block →](/guides/pipeline-reference#kind-specific-blocks) **2026-10-07** ## A declared registry project that does not exist is created, or the declaration is refused `ankra application registry set` with `--admin-credential` now checks that the declared project exists on your registry. A missing project is created with that credential - private, or public with `--project-public` - when it may create projects; otherwise the declaration is refused naming the project and host, instead of every later build failing at the push. [Have the project, or let Ankra create it →](/guides/harbor-registry#what-harbor-needs-first) **2026-10-07** ## Create registry robots on a Harbor you run yourself The **Registry robots** page and `ankra registry robots create` can now create a robot on a Harbor your organisation runs and has connected as an OCI registry. Pick the registry, a credential of yours that may manage robots there, and one of that registry's projects, such as `collectorcharts`: the robot reaches only that project. Ankra acts only with your credential. [Robots on a Harbor you run yourself →](/guides/harbor-registry#robots-on-a-harbor-you-run-yourself) **2026-10-06** ## Protecting a CloudNativePG stack now backs it up Protecting a stack with a CloudNativePG cluster never gave the `Cluster` its barman-cloud archiver, so every restore point failed with "the cluster has no plugin configured". Protecting the stack, or taking a restore point by hand, now wires the archiver and its object store into the stack, and unprotecting removes both. Wiring it restarts the instances one at a time; a scheduled backup never does, and skips a stack that is not wired yet. [Protect a stack →](/guides/backups#protect-a-stack) **2026-10-05** ## Cluster groups on environment follow each cluster's Environment A dynamic cluster group on `environment` now matches the **Environment** set on each cluster. Before, it matched a copy taken when cluster groups launched, so a group on `environment` = `production` missed clusters created or moved to production since then, and kept a cluster moved out of production. Roles assigned over the group, and the Kubernetes access they bundle, now follow a cluster's environment: platform permissions within about a minute, Kubernetes access within about 90 seconds. [Restrict production to a DevOps role →](/guides/roles-and-access#example-restrict-production-to-a-devops-role) **2026-10-04** ## Tag runs no longer use shared pipeline caches A pipeline run triggered by a tag, or a rerun of one, now starts with empty cache volumes. Before, it shared a cache scope with feature branches and pull requests, so a release could start from files another branch's run left behind. The step reports `cache_result: disabled`. Other runs keep their caches, and a tag run takes as long as a run with a cold cache. [How a stage's cache is scoped →](/guides/pipeline-reference#fields-common-to-every-stage) **2026-10-04** ## One component can build both an image and a release bundle Declare `build.component` on a build stage to name the component it belongs to, so `build-portal-image` can build portal's image beside a `build-portal` release bundle. A component's bundle now publishes to its own `-bundle` repository, so the image and the bundle no longer overwrite each other's commit tag. `ankra pipeline validate` reports a stage that belongs to no component before a run. [Which component a build belongs to →](/guides/ankra-pipelines#which-component-a-build-belongs-to) **2026-10-02** ## See pipeline capacity live, and steps waiting for a CI slot no longer burn their timeout The Pipelines settings page shows your CI slots, steps in flight and queued runs, and edits the parallel run and step limits. The run limit now defaults to 16. A queued run can say it is waiting for a CI slot, a step's timeout and duration start when it begins, and steps show their queue wait. Current agents change slot counts live, up to 128, without a restart. [Capacity and concurrency →](/guides/ankra-pipelines#capacity-and-concurrency) **2026-10-02** ## Extra registry projects, and robot accounts bound to the one you pick Everything an organisation publishes lands in one registry project, so every robot account could reach all of it. You can now create up to five extra projects with `ankra registry projects create` or from the **Registry robots** page, push the images you want kept apart there, and bind a robot account to that project alone with `--project`. A robot reaches the one project it is bound to and nothing else. [Scoping a robot to one project →](/guides/harbor-registry#scoping-a-robot-to-one-project) **2026-10-01** ## Secret value digests can no longer be used to check a guessed value Without the reveal permission you see each Secret value as a digest, which shows that a value changed. That digest used to be an unkeyed SHA-256 prefix anyone could compare against guesses. It is now keyed per cluster with a key only the platform holds, so it cannot be used to check a guessed value. The AI assistant also finds Secrets only by name, namespace and labels, never by their stored content. [Configs →](/platform/kubernetes-insights#configs) **2026-10-01** ## Helm release values and kubectl Secret reads need the Secret reveal permission A Helm release now hides the passwords, tokens and keys in its values, rendered Secrets and notes until a member with `kubernetes.secrets_reveal` clicks **Reveal values**. An upgrade that still carries a hidden placeholder is refused. `kubectl` through Ankra refuses Secret requests from members without the permission, whatever their access grant. Every Helm reveal and every Secret request it lets through is written to the audit log. [Secrets need their own permission →](/guides/cluster-access#secrets-need-their-own-permission) **2026-10-01** ## The Teams claim code is sent in a private chat and no longer changes while you use it When **Ankra AI** is added to a team or group chat, it now sends the claim code that connects your Microsoft tenant to the person who added it in a private chat, instead of posting it in the channel. The welcome card shows when the code expires, and messaging the bot again returns the same code until it is close to expiry, so a colleague talking to the bot no longer invalidates the code an admin is about to redeem. [Connecting Microsoft Teams →](/platform/ai-connections#connecting-microsoft-teams) **2026-10-01** ## The cost estimate prices GPU node groups, starting with UpCloud The what-if draft and **Compare providers** now price GPU node groups. Each group has a **GPUs** field, a GPU group is placed only on a GPU size, and every UpCloud GPU plan can be priced, spot plans included. Providers with no GPU sizes in the price book show the group as not priced instead of quoting a CPU size. The cluster cost card no longer prices UpCloud B200, RTX PRO 6000 and GPU spot nodes at zero. [What if the node groups changed? →](/platform/cloud-cost#what-if-the-node-groups-changed) **2026-10-01** ## Connect your own identity provider for single sign-on, with nothing to request from Ankra An owner or admin can now connect the organisation's own OpenID Connect provider - Microsoft Entra ID, Okta or any other - under **Organisation** → **Settings** → **Single sign-on**. The provider decides who is a member and, through group mappings, which role they hold. A member it stops admitting is suspended at the end of the re-verification window, 24 hours by default, which stops their sessions, API tokens and Kubernetes access together. [Single Sign-On →](/guides/single-sign-on) **2026-10-01** ## The Credentials page fills the window, and its empty states lead somewhere The **Credentials** page now fits the window: the title and the search, filter and sort row stay in place while the cards scroll. With no credentials yet, the page says what they are for and offers a shortcut to each kind (source control, cloud providers, container registries, access keys), opening the provider list at that group. A search or filter that matches nothing offers **Clear filters**. [Credentials →](/platform/credentials#view-credentials) **2026-10-01** ## Registry robots list Ankra's own robots, and yours can hold exact permissions and expire The **Registry robots** page and `ankra registry robots list` now show every login your registry project honours: the ones you created and the ones Ankra manages (`ci`, `pull`, one per application), read-only. A robot you create can hold exactly the permissions a job needs, such as deleting artifacts or moving tags, instead of push or pull, and can be set to expire. Each robot names the one project it is bound to. [Robot accounts you create yourself →](/guides/harbor-registry#robot-accounts-you-create-yourself) **2026-10-01** ## Invitations can be accepted by people who sign in with Microsoft Someone invited to an organisation who signed in with Microsoft was told their email address was not verified, with no way to verify it. The link in the invitation email now confirms the address when they open it and sign in, so the invitation can be accepted. An invitation sent before this change needs a resend from **Organisation → Members** to carry the new link, and a resend retires any link mailed earlier. [Roles and access →](/guides/roles-and-access) **2026-10-01** ## Reading a Kubernetes Secret's values needs its own permission and is audited Opening a Secret in Ankra now shows its key names only. To see the values, click **Reveal values**: that needs the new `kubernetes.secrets_reveal` permission, which `owner`, `admin` and `operator` hold and `member` and `viewer` do not, and each reveal is written to the audit log. The API follows the same rule, and the AI never sees the values. Custom roles that held `kubernetes.write` were given the new permission. [Kubernetes Resources →](/platform/kubernetes-insights#configs) **2026-10-01** ## Pipeline steps reserve half their CPU, so more of a run fits on one node A step used to reserve the whole CPU figure its stage declared. A run's steps share one node, so that node filled up and the rest of the run waited, whatever the cluster's node count. A step now reserves half of its `resources.cpu` (at least 100m) and can still use all of it while the node has CPU free. Memory is reserved in full, as before. Nothing in a pipeline definition changes. [Capacity and concurrency →](/guides/ankra-pipelines#capacity-and-concurrency) **2026-10-01** ## Creates and scale-ups that a cluster's pod range cannot hold are refused up front A cluster Ankra builds holds at most 64 nodes with the default network ranges. A create, a node group change or an autoscaler scale-up that would go past that is now refused with a message naming the range and the limit, instead of adding nodes that join and never become Ready. [Network ranges and their limits →](/platform/cluster-mesh#network-ranges-and-their-limits) **2026-10-01** ## Ankra's builders run at most three builds per organisation at a time The builder pool is shared by every organisation. One organisation's burst of builds now waits its turn after three instead of holding every builder, so another organisation's build starts without waiting for it. [Waiting for a builder →](/concepts/applications#waiting-for-a-builder) **2026-09-30** ## Applications are available to every organisation Applications are out of closed beta and on for every organisation, with nothing to request. Connect a GitHub repository and Ankra generates the Dockerfile, chart and pipeline, builds the image, and deploys it. Pull request previews, branch demos, the application registry, publishing an application as an add-on, builds on Ankra's own builders and the Claude Design import come with it. [Applications →](/concepts/applications) **2026-09-30** ## Self-managed AWS clusters are available to every organisation Ankra-built k3s and kubeadm clusters on EC2 are out of closed beta. Every organisation now sees AWS EC2 next to Amazon EKS in the create-cluster dialog, and the `ankra cluster aws` commands and the AWS cluster endpoints are served for everyone. [AWS clusters →](/guides/aws-clusters) **2026-09-30** ## The AI section is available to every organisation The **AI** section of the sidebar is out of closed beta: Home, Board, Chat, Inbox, Automations, AI Agents and Settings are on for every organisation. That brings the AI board and its agents, automations, AI code review, Slack and Teams connections, ticket sync, and chat autonomy, where low-risk writes can run without a confirmation once you pick **Auto**. [Ankra AI overview →](/platform/ai-overview) **2026-09-30** ## Cluster Mesh and multi-zone UpCloud clusters are available to every organisation Cluster Mesh and the WireGuard overlay it runs on are out of closed beta. Any organisation can join its clusters into one mesh, and stretch an UpCloud cluster across a pool of zones. New Ankra-built clusters get a network identity from the organisation's pools, so they can join a mesh later. [Cluster Mesh →](/platform/cluster-mesh) **2026-09-30** ## Cloning a stack with its data is available to every organisation Cloning with data is out of closed beta. The clone dialog's **Data** step and `--with-data` carry a stack's volumes and database contents to the target through a backup vault, for every organisation. [Clone a stack →](/guides/clone-stack) **2026-09-30** ## Log sources are available to every organisation Log sources are out of closed beta. Any organisation can connect a cluster to Loki, Elasticsearch or OpenSearch under **Settings** → **Integrations**, search them from the **Search** tab on the cluster's **Logs** page, and let Ankra's AI query them. **Log Source** is no longer marked **Coming soon**. [Log sources →](/integrations/log-sources) **2026-09-30** ## The organisation audit log can be exported with an API token, so SIEM forwarding can run on a schedule The audit-log export only accepted a signed-in browser session, so a scheduled job holding a token was redirected to the sign-in page. `GET /api/v1/org/organisation/audit-logs/export` now takes a service token or personal API token holding `audit.read`, with the same filters, CSV and JSON formats and 100,000-row cap as the portal download. [Forwarding to a SIEM →](/security/audit-export#forwarding-to-a-siem) **2026-09-30** ## MCP clients hand you portal links The Ankra MCP server now tells clients to give you a link to the portal page for what they are describing. `list_clusters` and `get_cluster_details` return each cluster's `portal_url`, and the new read-only `get_portal_url` tool returns the address of any other page - a stack, an add-on, a pod, an application, a pipeline run, a ticket, an alert or a CVE - opening in the right organisation. A new Portal URLs page lists every page and how its address is built. [Portal URLs →](/platform/portal-urls) **2026-09-29** ## Bitbucket Cloud pull requests reach Ankra AI review, mention replies and pipelines Ankra's Bitbucket webhooks subscribed to pushes only, so reviews, `@ankraai` replies and pull request pipelines never started. Webhooks now carry pull request events and are registered for repositories with AI review on or connected to Ankra Pipelines. Drafts are honoured, replies thread under the question, and `event.provider` reads `bitbucket`, as documented. Reconnect each Bitbucket credential to grant the new pull request permission; until then, GitOps sync keeps working. [Grant the pull request permission →](/integrations/bitbucket-cloud#granting-the-pull-request-permission) **2026-09-29** ## Connecting Bitbucket Cloud works end to end again Repository lists froze once Atlassian removed the workspace listing Ankra read, and GitOps settings could not save a Bitbucket repository. Both work again, with a branch picker. Organisation credentials can be deleted, their webhooks removed first; tokens refresh before they expire; a connection must be finished by the Ankra user who started it; and **View source** opens on bitbucket.org. [Bitbucket Cloud →](/integrations/bitbucket-cloud) **2026-09-29** ## `ankra cluster exec` runs one command in a pod and exits with its exit code `ankra cluster exec -n -- [args...]` runs one command in a pod - a `psql -c`, a `curl` to localhost, reading a mounted file - and exits with its exit code, so scripts and CI can branch on it. The command reaches the container exactly as given, its output streams as it runs, and every run is audited like a terminal session. It needs CLI 0.20.0 and cluster agent 2.1.1166 or newer. [One-off commands →](/platform/pod-terminal#one-off-commands) **2026-09-29** ## Ankra Cloud is a cluster provider, managed and self-managed The create-cluster dialog offers Ankra Cloud two ways. **Ankra Cloud Kubernetes** is a control plane the Ankra team runs, with node pools, autoscaling and upgrades driven from Ankra. **Ankra Cloud** builds kubeadm or k3s on Ankra Cloud servers behind a bastion and a NAT router, with the cloud controller and CSI driver deployed for you. One Ankra Cloud credential serves both. It is in closed beta, enabled per organisation on request. [Ankra Cloud Clusters →](/guides/ankra-cloud-clusters) **2026-09-29** ## Domains you hold in Avura can serve your clusters and root their generated domains Connect an Avura workspace to its Ankra organisation from Avura, then share domains one at a time. A shared domain can be added as a custom DNS zone with no credential of your own, its records are edited under Settings > Avura, and it can be the Custom Ankra domain without delegating it to Ankra's nameservers. Ankra sees only the domains you share, and disconnecting is refused while anything still uses them. [Use Avura domains with Ankra →](/guides/avura-domains) **2026-09-28** ## A restore point you take by hand is kept, and a restore stays undoable for a week A second **Back up now** in one afternoon used to expire the first within minutes, and with it the safety restore point taken before an in-place restore. Retention now counts only scheduled restore points: one you take yourself is kept for the policy's longest period, and a safety restore point for seven days. The Backups tab also names each restore point and says whether it can still be restored. [Protect a stack →](/guides/backups#protect-a-stack) **2026-09-28** ## Deleting a cluster names its persistent volumes and deletes them only once you accept it A plain delete of a Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway or AWS cluster left its CSI volumes billing, and only `--force` deleted them, silently. Every delete now lists the volumes and deletes them, forced or not, once you accept it: a checkbox in the dashboard, a prompt or `--accept-volume-data-loss` in the CLI, `accept_volume_data_loss` on the API. A `retain` retention policy keeps them. [Persistent volumes →](/platform/cluster-settings#persistent-volumes) **2026-09-28** ## A forced stop keeps your volumes; only a deprovision deletes them A stop with `--force` deleted the cluster's CSI volumes on DigitalOcean, UpCloud, OVH, Hetzner, AWS and Scaleway, although a stop is reversible and a start expects its persistent volumes to still be there. A forced stop now cancels in-flight work, tolerates unreachable infrastructure and removes load balancers as before, but keeps every volume. They keep billing while the cluster is stopped; a deprovision deletes them once you accept it. [Stopping and starting a cluster →](/guides/upcloud-clusters#stopping-and-starting-a-cluster) **2026-09-22** ## A stack profile version can be deprecated, and nothing deploys it again A published version could only ever be superseded. When one turns out to be incompatible, to carry a critical bug or to ship a CVE, the owner can now deprecate it - from the History tab or `ankra stack-profiles deprecate` - with a reason, a note and the advisories behind it. Every launch, rollout and update then refuses it with that reason, and deployments still on it are flagged. The current version cannot be deprecated; `undeprecate` restores one. [Deprecate a version →](/concepts/stack-profiles#deprecate-a-version) **2026-09-21** ## Backup Vaults and stack restore points are available to every organisation Backups are out of closed beta and on for every organisation, with nothing to request. Register or provision a backup vault, protect a stack on a schedule, take restore points and restore a stack in place from the portal, the CLI and the API; `ankra migrate` moves its data through the same vaults. Cloning a stack with its data stays in closed beta, enabled per organisation on request. [Backups and restore points →](/guides/backups) **2026-09-20** ## Using a stack profile is a page, not a dialog A profile with two dozen inputs never fitted the dialog **Use Profile** opened. It now opens a page: the destination and the inputs take the main column, and a summary beside them stays in view while you scroll, with a checklist that ticks off as the form fills in and the **Create draft** button. A History version, a deployment update and the application wizard all land there with cluster and version already chosen. [Instantiate a profile →](/concepts/stack-profiles#instantiate-a-profile) **2026-09-19** ## A managed node pool can be handed to your own autoscaler, and Ankra refuses to change it Ankra never changed a managed pool's node count on its own, but a scale, a size replacement or a delete could still override a count your own cluster autoscaler had reached. A pool marked `externally_managed` now refuses every one of those from the portal, the CLI, the API and Ankra AI until the flag is cleared, and the pool listing shows the live count from the provider next to the last count Ankra wrote. [Externally managed node pools →](/guides/managed-kubernetes#externally-managed-node-pools) **2026-09-19** Also shipped this week **Build context** - a `build` stage with a `context` and no `dockerfile` builds that context's own Dockerfile, and a build refused before it starts says why. [→](/guides/ankra-pipelines) **Semgrep pack** - a generated scan stage leaves out the GitHub Actions pack once Ankra has confirmed your repository commits no GitHub Actions workflow. [→](/guides/ankra-pipelines) **2026-09-17** ## Merge when green has an on/off switch per GitHub installation Merge when green shipped with no control. Each GitHub installation's row now carries a **Merge when green** switch beside its AI mode, for organisation admins, and the same switch is `merge_when_green` on the binding profile, writable with a personal access token through `PUT /api/v1/org/ai-gateway/scm-bindings/{provider}/{binding_external_id}`. A request is honoured only in Agent mode, and with the switch on a `Depends-on:` line holds a pull request's merge behind the one it names. [Merge when green →](/integrations/github#merge-when-green) **2026-09-17** ## The pull request review reads the other half of a cross-repository change The cross-repository pass read default branches only, so it missed a client fix still open in the other repository, and it fired only on removals. The review now reads companion pull requests - a `Depends-on:` line, or any linked pull request in a related repository - and shows each on its own branch, with state, head and diff. Changed contracts are searched alongside removals. `@ankraai` replies see the same companions. [The other half of the change →](/platform/ai-code-review#the-other-half-of-the-change) **2026-09-16** ## Stopping a cluster keeps its state, and control planes grow without a stop On Hetzner, OVHcloud, UpCloud and DigitalOcean a stop used to lose everything stored in the cluster itself, and StatefulSets came back on empty disks. Stop now captures an encrypted snapshot of the cluster's state before the machines go, and the next start restores it, so workloads return with their Secrets and reattach their disks. Adding control planes no longer needs a stop either; the docs page explains what is kept and how to opt out. [Stop cluster](/platform/cluster-settings#stop-cluster) **2026-09-15** ## Cluster Mesh no longer moves a running cluster onto a new pod range, a move that crashed its controller manager `make-ready --pod-cidr` no longer moves a running cluster onto another pod range: that move crashed kube-controller-manager, which exits at start while any node holds a range outside `--cluster-cidr`. The request is now refused by name. `--pod-cidr 10.244.0.0/16` still puts a cluster an earlier move split back onto the range its nodes run. Two clusters sharing the kubeadm default pod range cannot mesh until the move is redesigned. [Moving a cluster onto a fresh pod range →](/platform/cluster-mesh#moving-a-cluster-onto-a-fresh-pod-range) **2026-09-15** ## An application opens as a workspace, and its Security section ranks findings by exploitability An application now opens as a workspace: Overview, Pipelines, Deployments, Previews, Security, Environment, Source & config and Settings, each its own page, with `?tab=` links redirecting. Demos are called Previews. Security ranks findings by CISA KEV, then EPSS, then severity, with a Known exploited filter and a sheet per finding carrying CVSS, EPSS, CISA's deadline and required action, the fix and the advisory. Exploitability reads Unknown until the catalog has synced. The Applications list gains a Needs attention filter. [The application workspace →](/concepts/applications#the-application-workspace) **2026-09-15** ## A stack profile carries its backup policy, so a fleet launch protects every instance Saving a protected stack as a profile now keeps its protection. The builder shows a **Protection** section - whether to protect, which backup vault, how often - and the vault travels by name, resolved against the organisation you launch into, so one profile protects a whole fleet. A launch with no ready vault says so and keeps the draft rather than starting a stateful stack unprotected. Public profiles carry no vault name. [Protection: backups travel with the profile →](/concepts/stack-profiles#protection-backups-travel-with-the-profile) **2026-09-15** ## Ankra says when a branch's runs are queueing behind each other A `concurrency.group` that varies per branch with no `cancel_in_progress` makes a new commit's run wait for the run it replaces. `ankra pipeline validate` now says so as advice - never blocking, never failing the command - naming the group and the line that changes it. A queued run's waiting reason carries the same recommendation when it is stuck behind its own branch, and a converted GitHub workflow gets a conversion note. Your existing definitions keep their behaviour. [Newest commit wins →](/guides/ankra-pipelines#newest-commit-wins) **2026-09-15** ## Protect a stack, take restore points, and restore it in place Protecting a stack schedules its databases and named volumes into a backup vault you own, and installs the backup data plane. Take a restore point whenever you like, read what it carries and what it could not, then restore the stack in place behind a typed confirmation. `ankra runs` follows every capture and restore, and a protection pill now says where data is exposed. CloudNativePG and Percona restore in place is not available yet. [Backups and restore points →](/guides/backups) **2026-09-14** ## A generated pipeline is checked against your repository, so a missing Dockerfile shows up before you merge Ankra now checks a generated `.ankra/pipeline.yaml` against your repository before proposing it, not only against the pipeline grammar. Each `build` stage's Dockerfile is resolved the way the builder resolves it and looked for in your branch plus the files the same run commits. Findings name the stage and appear on the setup run, so nothing is discovered by a push that fails after you merge. [Ankra Pipelines →](/guides/ankra-pipelines) **2026-09-14** ## Generated test stages run your project's own commands A generated test stage now follows the lock file your component commits. A `uv.lock` project runs `uv sync --locked && uv run pytest`; `pnpm-lock.yaml` and `yarn.lock` run pnpm and Yarn with a frozen lockfile instead of npm. Go components each get their own `GOCACHE` and `GOTMPDIR` inside the workspace, and a monorepo's workspace is sized by its component count. [Ankra Pipelines →](/guides/ankra-pipelines) **2026-09-14** ## Convert an application from its GitHub workflow to Ankra Pipelines with one click An application still building from a GitHub Actions workflow now shows **Convert to Ankra Pipelines** on its journey card and **Convert** in the Applications list; `ankra application pipeline convert ` does the same. The click takes effect at once: Ankra stores the converted pipeline, builds the next push through it, switches the generated workflow off on GitHub, and opens a pull request that commits the pipeline file and removes the workflow. [Migrate an existing application with one call →](/guides/migrate-from-github-actions#migrate-an-existing-application-with-one-call) **2026-09-14** ## Protect a stack with a backup block in the cluster file A stack can now declare its own backup policy in the cluster file, and Ankra takes the scheduled restore points from it. Databases are captured by default; volumes only the ones you name, so protecting a stack does not start copying every claim in its namespaces. A file that omits the block leaves protection exactly as it was, and an apply that narrows protection says what stops being covered. Closed beta, enabled per organisation. [Protect a stack →](/guides/backups#protect-a-stack) **2026-09-14** ## Hetzner clusters are priced from Hetzner's live price list Cloud Cost now reads Hetzner's price list every hour with your stored Hetzner credential and prices each server type, load balancer, IPv4 and volume per location, instead of from a built-in copy that predated Hetzner's June 2026 increase. A server ordered before that increase keeps the list it was ordered under, matching the two rates your invoice shows for the same type. Estimates read `medium` confidence, not `high`, when no live list could be fetched. [Where the prices come from →](/platform/cloud-cost#where-the-prices-come-from) **2026-09-14** ## UpCloud clusters built before the mesh overlay can now join a Cluster Mesh `make-ready` now joins an existing UpCloud node to the WireGuard overlay from its update job, so a cluster built before the overlay converges instead of looping. Service ranges may overlap across members; a second cluster on the default pod range is refused by name until `--pod-cidr auto` moves it onto a fresh range, one node at a time. `ankra cluster mesh up` runs readiness, make-ready and the joins for a set of clusters in one command. [Make an existing cluster mesh-capable →](/platform/cluster-mesh#make-an-existing-cluster-mesh-capable) **2026-09-14** ## Create your own robot accounts on the Ankra registry Ankra minted every registry login itself. You can now create robots of your own for anything that logs in without Ankra in the loop - CI you run, a laptop, a cluster Ankra does not manage. `ankra registry robots create `, the **Registry robots** page, or the API mints one with **push** or **pull** rights on your project and shows its secret once; list, rotate and revoke it the same way, and reference its managed credential `ankra-harbor-robot-` anywhere. [Robot accounts you create yourself →](/guides/harbor-registry#robot-accounts-you-create-yourself) **2026-09-14** ## An application that already builds no longer tells you to merge its setup pull request The Applications list and the application journey no longer say **Merge the setup pull request to go live** once your application builds on an Ankra pipeline or on your own workflow: that pull request only proposes committing Ankra's generated files. A push now also re-checks a setup pull request still recorded open against GitHub, and an application whose pipeline Ankra stores and runs is recorded as building on Ankra Pipelines. **Reconcile** moves an existing application over. [Ankra Pipelines →](/guides/ankra-pipelines) **2026-09-12** ## Stack READMEs, code review and background runs follow your model settings Stack README generation, AI code review with no model picked, and background agent runs now follow your organisation's model settings instead of a fixed Claude model, defaulting to the **Think** tier: GLM 5.2 on the Ankra provider. **Stack README** and **AI code review** each get their own setting, and only admins can change a function's model. Chat on the default models now counts against the free monthly AI allowance. [Models per function →](/platform/ai-provider#models-per-function) **2026-09-12** Also shipped this week **Pipeline commands** - `ankra pipeline run` and `list` take the application and commit from the checkout you are in, and say which. [→](/guides/ankra-pipelines) **Adding an application** - `ankra application add --wait` follows the analysis and prints the setup pull request's link, or why it failed. [→](/get-started/quickstart) **Unreachable repositories** - adding an application checks only the repository you add, so one the GitHub App cannot reach elsewhere no longer blocks it. [→](/integrations/github) **Credential health** - `ankra credentials get` says whether Ankra can use a credential and, for an unusable GitHub one, why. [→](/integrations/github) **2026-09-11** ## Self-managed k3s clusters on AWS EC2 Ankra now builds self-managed k3s or kubeadm clusters on EC2: a VPC across one or three zones with NAT egress (or a VPC you adopt), a bastion, nodes in private subnets with no public IP, the AWS cloud controller and EBS CSI driver on instance profiles, a Network Load Balancer for Traefik, stop and start, node groups, upgrades and teardown with a retention policy. The provisioning credential covers it. Closed beta, enabled per organisation on request. [AWS clusters →](/guides/aws-clusters) **2026-09-11** ## Cluster Mesh says up front when a cluster can never join, and retries a member whose setup stalled A cluster created with networking off has no Cilium release the platform manages, so a mesh could never reach it. Readiness now says so under a new `cilium_stack` check, and the join and make-ready refuse it instead of leaving the member in `configuring`. A member whose trust-root job never ran, or never reported, is re-dispatched by the mesh reconciler after fifteen minutes rather than waiting for a peer change that never comes. [Cluster Mesh requirements →](/platform/cluster-mesh#requirements) **2026-09-11** ## Update a stack to a newer profile version in place A deployment behind its profile used to be updated by instantiating the profile again, which produced a second stack renamed around the name collision. **Update** on the profile's Deployments tab now replaces the contents of the stack that deployment already runs, in place, so Kubernetes rolls the workloads, and records the deployment at the new version - so the "behind" counters follow. Non-secret inputs are carried forward; secret inputs are asked for again. [Versioning, diffing, and updates →](/concepts/stack-profiles#versioning-diffing-and-updates) **2026-09-10** ## Cloned stacks deploy on their own; the ones missing secrets say what they need Cloning a stack or a whole cluster no longer leaves a row of drafts to open and deploy one by one. **Deploy after cloning** is on by default and deploys every clone that has what it needs. A stack whose encrypted values had to be removed is held as a draft marked **Needs input**, and the clone result, the stack card and the builder name each member and field to re-enter, with a link straight to it. [Clone a cluster →](/guides/clone-cluster#stacks-that-need-your-input) **2026-09-10** ## Clone a cluster onto a different cloud provider **Clone Cluster** can now build the copy on a cloud provider instead of an import cluster. Choose **Build on a cloud provider**, pick Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE or HPE Morpheus, and finish on that provider's create page: the cluster is provisioned and every source stack lands on it as a draft. Stacks the provider installs itself - cloud controller, networking, DNS, Cilium - are skipped and reported. The API takes `target_provider` and `target`. [Clone a cluster →](/guides/clone-cluster#clone-a-whole-cluster) **2026-09-09** ## Import a Claude Design export as an application A canvas exported from Claude Design - artboards, `canvas.json` and images, a zip, or a saved canvas page - becomes a deployable application in one step: Ankra turns it into a static site, creates a GitHub repository with a Dockerfile, and registers the application. Use `ankra application import claude-design `, **Claude Design** under Add application in the portal, or `POST /api/v1/org/applications/imports/claude-design`. Closed beta, enabled per organisation on request. [From Claude Design to a live application →](/guides/claude-design-to-ankra) **2026-09-08** ## Compare providers prices the fleet on AWS, Google Cloud, Azure, Proxmox and Morpheus too The **Where the same fleet would bill** row now offers every provider Ankra can price. AWS, Google Cloud and Azure are priced from their own per-region catalogues, and each column says which region it was priced in. Proxmox VE and HPE Morpheus are priced per vCPU, GB and disk from your rate card, and the column names whether those rates are yours or Ankra's defaults. Unpriced is still never free. [Compare providers →](/platform/cloud-cost#compare-providers) **2026-09-07** ## A cluster's security history and scheduled report work from the CLI and MCP The daily posture snapshots behind the trend chart and the weekly or monthly emailed security report now answer to an API token, `ankra security history` and `ankra security report-schedule` (show, `set`, `send`), and to four MCP tools: `get_cluster_security_history`, `get_cluster_security_report_schedule`, `set_cluster_security_report_schedule` and `send_cluster_security_report_now`. The two writes are confirmed before they run. [Security reports →](/guides/security-reports) **2026-09-07** ## Playground sizes have new prices, and a playground you already run keeps its price New list prices, EUR a month excluding VAT: Trial free, Small €5.21 (2 vCPU / 4 GB), Medium €8.06 (4 vCPU / 8 GB), Large €15.19 (8 vCPU / 16 GB) and Extra large €28.01 (16 vCPU / 32 GB), storage bundled, pods and PVCs capped. A playground you already run keeps the price you ordered at. The billing page carries the rules: pro-rata by the second, nothing metered on top, an 8 GB organisation memory budget. [Playground environments →](/platform/billing#playground-environments) **2026-09-07** ## A package in the bill of materials now opens where it runs Every figure in an SBOM component row opens the list it counts. The package name, and the images, workloads and clusters figures beside it, open a package page listing the images that carry that exact version, the workload containers running them and the clusters they run on; each entry links onward to the image's bill of materials, the workload's live page or the cluster's security tab. On the CLI: `ankra security sbom component`. [Software bill of materials →](/guides/cluster-security#sbom) **2026-09-07** ## Compare providers: staying put is a column, and today shows its list price The cluster's own provider now appears beside the others: the same fleet re-sized in place on its own price book, marked **Stay**, so a cheaper plan family where you already run is weighed against a move. Today's tile shows what the same shape costs at list next to the metered bill. The estimate API accepts the cluster's own provider as the target for the same read. [Compare providers →](/platform/cloud-cost#compare-providers) **2026-09-07** ## Compare providers: the whole bill on every cloud, like for like A cluster's cost page gains **Compare providers**: the same fleet - node groups, control plane, volumes and load balancers - priced on every provider Ankra can build on, side by side, with today metered where it runs. Matching is like-for-like by default, so a fleet on dedicated cores is never quoted as a saving on shared ones. A target the book can only partly price keeps a floor instead of a blank. [Compare providers →](/platform/cloud-cost#compare-providers) **2026-09-07** ## Pipeline logs and scan reports are kept without a backup vault of your own A first pipeline run no longer waits on an object-storage bucket. An organisation with no ready backup vault has its step logs, scan reports and declared artifacts kept in an Ankra-managed default vault, under a key prefix of its own, with the same retention and the same per-object download links - so `scan`, `gate` and `publish` work from the start. Register a vault of your own for data residency, and it takes precedence from the next step onward. [Ankra Pipelines →](/guides/ankra-pipelines#before-your-first-run) **2026-09-07** ## Everything the Security Center shows is now on the CLI and the MCP server Dispositions, compliance frameworks and their evidence export, per-cluster benchmarks, policy violations, the policy mode switch, NetworkPolicy over-privilege, the security baseline install, add-on posture, workloads, namespaces, pods and an application's image versions each gained an `ankra security` command and an MCP tool, and every Security Center route now answers to an API token under `/api/v1`. Writes confirm first and enforce the same permissions as the portal. [MCP tool reference →](/platform/mcp-tools#security-center) **2026-09-07** ## A stack shows what it is running, not only what it declares A stack's new **Runtime** tab shows what its add-ons put on the cluster: the hosts its Ingresses publish, as links you can open; the pods it owns, with the status `kubectl` prints; its Services and their ports; and the events raised against its own objects. Pods are attributed by ownership rather than namespace, so stacks sharing a namespace never show each other's. A listing that could not be read says so instead of reading as empty. [What a stack is running →](/concepts/stacks#what-a-stack-is-running) **2026-09-07** ## Security drills down from the cluster to a container A cluster's Security page now opens on **Stacks**: one row per stack with its attribution, actionable and known-exploited findings and SBOM coverage, plus an **Outside any stack** row so the rows add up to the cluster. A stack's members expand into the workloads they deploy, then their pods, then each container's CVEs. The same path is on the API, the CLI (`ankra security stacks|stack|pod`) and Ankra AI. [Cluster security →](/guides/cluster-security#cluster-security-view) **2026-09-07** ## Licence risk on every bill of materials, and a warning when a private repository owes its source Every SBOM package now carries a licence risk tier - network copyleft (AGPL, SSPL), source-available (BUSL, Elastic), copyleft (GPL), weak copyleft, permissive or unclassified - as a filter, a summary figure, a per-image breakdown, a CSV column and a CLI flag. An application whose repository is private and whose newest scanned version links a network-copyleft package gets a **Licence obligation** notice naming the packages that oblige you to publish your source or move them behind a separate service. [Licence risk →](/guides/cluster-security#licence-risk) **2026-09-05** ## Pod security enforced by Kubernetes itself, not a policy engine Version 5 of the security baseline drops Kyverno. Pod security is enforced by Pod Security Admission, built into every API server, with a ValidatingAdmissionPolicy stopping anyone but the agent lowering a managed namespace's label. Nothing here runs as a pod. The Security Center's Policies tab now shows the full check table for every cluster, with or without a policy engine, and says which mode and standard it is in. [Security baseline →](/guides/cluster-security#security-baseline) **2026-09-05** ## Ankra Pipelines: `egress-https` reaches only public addresses The tier could reach any address on 443, and port 53 on any pod in the cluster. It now excludes RFC1918, link-local and carrier-grade NAT with their IPv6 equivalents, and restricts DNS to `kube-system`. This breaks a step that reaches a private HTTPS endpoint, so `ci_egress_allowed_cidrs` names the ranges a step may still reach. An image policy prefix ending in a separator now admits exactly its own boundary. [Ankra Pipelines →](/guides/ankra-pipelines#what-a-step-runs-as) **2026-09-05** ## Ankra Pipelines: a pull request from a fork no longer shares a cache with trusted runs A pull request from a fork resolved the same cache volume a trusted run does. A restricted fork run now resolves no shared cache at all, and every claim carries a trust scope of `default_branch` or `branch` that no exact key or prefix match crosses. Claims written before the deploy were backfilled as untrusted, costing one cold build per key, once. Fork runs are budgeted as well as restricted. [Ankra Pipelines →](/guides/ankra-pipelines#what-is-in-place-today) **2026-09-05** ## Ankra Pipelines closes seven ways a run could get more authority than an administrator approved A security review found seven ways a caller holding only `pipelines.manage` could give a run more authority than an administrator ever approved. All seven are closed: one approval route, requiring a human actor and the repository's current default-branch definition, and a dispatch that resolves its `ref` through the host and proves any caller-supplied `head_sha`. Re-approve your definitions at this release, and re-run anything that was mid-flight. [Ankra Pipelines →](/guides/ankra-pipelines#logic-is-open-authority-is-closed) **2026-09-05** ## Ankra Pipelines: a stage nothing can run is skipped, not left hanging A pipeline declaring a stage kind Ankra cannot execute yet validated cleanly, then sat `pending` forever, holding its run and its concurrency group. Those kinds now raise a validation warning and the planner skips them with reason `kind_unavailable`, so the run concludes on the stages that do execute. `uses:` is refused at validation, `scan.fail_on` tightens the gate per scanner, and `ci_run_retention_days` bounds how long concluded runs are kept. [Ankra Pipelines →](/guides/ankra-pipelines#what-is-in-place-today) **2026-09-05** ## Every container has its own CVE, SBOM and KEV reports A stack's Security tab opens on its running pods, grouped under the add-on or manifest that deploys each, with every container listed and three openers into a dedicated container view: CVE, SBOM and KEV. That view carries the image's findings, its bill of materials with the CycloneDX and CSV downloads, and the ring of links around it. An unscanned cluster reads Not scanned rather than clean. [Cluster security →](/guides/cluster-security#the-container-view) **2026-09-05** Also shipped this week **Microsoft Teams** - the welcome card's **Connect to Ankra** button opens the portal with the claim code filled in, instead of a 404. [→](/platform/ai-connections#connecting-microsoft-teams) **Playground** - the create wizard and cards say storage is local to the host and not backed up, and a plan's full storage size is yours. [→](/guides/cluster-sandbox) **2026-09-04** ## A pod's Security tab shows the vulnerabilities and bill of materials of each container A pod's detail page has a Security tab listing every container and init container with the CVEs the scanner attributes to it, through the pod's owner chain, and the bill of materials of its image. Findings opens the image's CVE list and Components its package inventory, the same views the Security Center uses. A container no report covers reads Not scanned; one without a bill of materials reads Absent. [Cluster security →](/guides/cluster-security#pod-security) **2026-09-04** ## A stack's Security tab shows every vulnerability and bill of materials behind what it deploys A stack's detail page has a Security tab reading every CVE, every CISA-listed exploited vulnerability and every bill of materials behind the workloads its add-ons and manifests deploy, as one object. An add-on is attributed by its Helm release identity, a manifest by the objects its YAML declares, both walking two owner levels down. The findings and components views carry facets computed for the stack alone. [Cluster security →](/guides/cluster-security#stack-security) **2026-09-04** ## Files you maintain by hand in your GitOps repository survive every sync The reconciler's export of stored state wrote back to Git without checking who last wrote each file, so a value maintained by hand - an image tag pinned above a chart's `appVersion` for a CVE fix - could be reverted. Every write path now applies one rule: `.ankra-owned.json` records a digest of the bytes Ankra last wrote, and a file whose bytes no longer match it is kept, never overwritten. [GitOps →](/concepts/gitops#what-ankra-writes-back-to-git) **2026-09-04** ## Ankra Pipelines: connect a repository and watch it run Connecting a repository is now self-service, and on GitHub the same call reads its committed `.ankra/pipeline.yaml` as the definition of record. A new application's setup pull request commits that file instead of a generated GitHub Actions workflow. `checkout` joins `run` as a stage that executes end to end, and a pull request changing a protected section runs with the last approved authority instead, saying so on the check run. [Ankra Pipelines →](/guides/ankra-pipelines) **2026-09-04** ## Ankra Pipelines: build, scan, gate and publish by digest A commit is now a straight line to a deployed image. `build` produces a digest in a rootless BuildKit pod and pushes it to your organisation's CI staging repository; `scan` runs Trivy, Semgrep and Checkov against it; `gate` judges them against a policy a pipeline may only tighten; and `publish` re-tags the one digest `gate` approved. A pull request from a fork runs restricted rather than refused. [Ankra Pipelines →](/guides/ankra-pipelines) **2026-09-04** ## Ankra Pipelines: the review reads the run, and you can say merge when green Ankra's pull request review answered whether a change was passing from a GitHub check run alone, so a repository on Ankra Pipelines got a review blind to its own pipeline. It now resolves the commit's newest run first and carries its facts: the failing step, findings by tool and severity, the gate verdict, the build digest. Mention `@ankraai merge when green` and Ankra merges the moment those conditions hold. [Ankra Pipelines →](/guides/ankra-pipelines#what-a-pull-request-gets) **2026-09-04** ## Migrate an existing application onto Ankra Pipelines with one call Moving an application already running its own CI meant converting the workflow by hand and opening the pull request yourself. `GET /org/applications/{id}/pipeline` now reads what builds it today and what a migration would do, and `POST .../pipeline/migrate` converts it and opens the pull request for you. Merging that pull request is what switches the application over, with no separate Reconcile click. [Migrate from GitHub Actions →](/guides/migrate-from-github-actions#migrate-an-existing-application-with-one-call) **2026-09-04** ## If you opt in, a build your CI cluster cannot run falls back to Ankra's platform builders `ci_build_fallback: platform_builders` was a setting with nothing reading it back. When the CI cluster's own agent cannot run pipeline steps and an organisation has opted in, a `build` step now runs on Ankra platform builders instead, with the same push, scan, gate and publish downstream. The step's `executor` records which lane actually built it. [Ankra Pipelines →](/guides/ankra-pipelines#what-is-in-place-today) **2026-09-03** ## Live usage, agent and GitOps posture from chat and the MCP server Nine `ankra cluster` commands the assistant and the MCP server could not answer now have tools. Ask what is eating CPU on a cluster and Ankra AI reads live usage per node or pod from metrics-server, never a cached figure. It also reads GitOps sync state including the two failures the overview hides, reports and upgrades the agent, lists every Helm release, and discovers managed clusters already running. [MCP Tool Reference →](/platform/mcp-tools) **2026-09-03** ## Raise support tickets from chat and the MCP server The assistant could diagnose a problem but not hand it to a human, and an MCP client had no way to open a ticket at all. Tell Ankra AI to raise a support ticket and it proposes one with the subject, category, severity and the cluster you were looking at; confirm, and it is filed. Ask what support said and it reads the thread; reply in chat and it lands on the ticket. [Support →](/platform/support#from-ankra-ai-and-the-mcp-server) **2026-09-03** ## One status comment per pull request Ankra used to leave several comments on a pull request as it worked, each in its own place and each edited on its own clock. It now keeps one comment with a section for each thing it has to say - the pipeline, the preview, the review, and the promotion when there is one - updated in place. An older preview card is adopted into it rather than left beside it. [Pull request reviews →](/guides/pipeline-agents) **2026-09-03** ## Convert a workflow into an Ankra pipeline from chat Ask Ankra AI to convert an existing GitHub Actions workflow, GitLab CI file, Bitbucket Pipelines file or CircleCI config into `.ankra/pipeline.yaml`, and to validate a definition you have written. It answers with the file and every diagnostic: what it mapped, what it could only carry over as a plain `run` step, what it refused. Actions with an Ankra equivalent are mapped; the rest is kept as the script it was. [Migrate from GitHub Actions →](/guides/migrate-from-github-actions) **2026-09-03** ## Ask Ankra AI to create your alerts Ankra AI could list your alerts and mute them, but asked to create one it could only point you at the New alert button. Describe the alert in chat and it now proposes a Create alert action with severity, the clusters or environments it watches, the conditions and the cooldown spelled out. It writes only conditions the evaluator implements, so an alert it creates cannot sit dead while reading as configured. [Alerts →](/guides/alerts#creating-alert-rules) **2026-09-03** ## A rollout Ankra verifies as healthy no longer sends you a message After you approve a change in chat, Ankra opens a follow-up turn once the rollout settles and verifies chart version, health and sync. That turn was treated as a reply you were waiting on, so every verified rollout sent a completion message to Slack or Teams and to the inbox. A verification that finds the rollout healthy is now quiet; one that finds a failure, or parks a fix, still reaches you. [Notification routing →](/guides/notification-routing) **2026-09-02** ## A software bill of materials for the fleet The Security Center gains an SBOM tab: every package the scanner found in every container image, where it runs, and whether a finding names it. When the next CVE lands you search the package and read the workloads, namespaces and clusters running that exact version, without a rescan. Images are keyed by digest and stored once. Generation is opt-in per cluster and off by default. [Cluster security →](/guides/cluster-security#sbom) **2026-09-02** ## Security by namespace, workload and pod A Workloads tab breaks the fleet down the way a platform team owns it: cluster, then namespace, then workload, then pod. Each namespace row carries its scanned workloads and images, the pods running there right now, the actionable findings by severity, the known-exploited count and the last scan. Show pods resolves each workload's owner chain to its live pods. A container the scanner never covered says Not scanned. [Cluster security →](/guides/cluster-security#workloads) **2026-09-02** ## A what-if draft that predicts the bill before you resize Every cluster's cost drill-down carries a what-if draft: the node groups as they run today, with the load balancers beside them, editable in place. Grow a group, shrink one, add a spot group, and the panel prices the draft and shows today's bill, the predicted bill and the change side by side. Pricing comes from the same price book the bill is metered from. Nothing is applied to the cluster. [What if the node groups changed? →](/platform/cloud-cost#what-if-the-node-groups-changed) **2026-09-01** ## Your money, in your currency The organisation sets a default display currency; you can now set your own on top, and every cost figure you see follows - the fleet overview, cluster drill-downs, credentials, workloads and savings. Provider quotes got honest at the same time: UpCloud quotes arrive in US dollars and were being shown with a euro sign. Thirteen currencies are supported, each with a live exchange rate refreshed twice a day. [Cost settings →](/platform/cloud-cost#settings) **2026-09-01** ## A Cost center that answers whose money, and what it runs The Cost page could say what the fleet costs, but not whose account the money is on, or what it is actually running. It now breaks spend down across six tabs: Clusters is the whole fleet in one table, Credentials groups the same money by the account paying for it, and Workloads answers where the money runs with names you recognise, linked through to each application. [Cloud cost →](/platform/cloud-cost) **2026-09-01** ## Cost for the capacity nobody publishes a price for If your clusters run on Proxmox, VMware or hardware you imported yourself, the Cost page had nothing to show you. You can now state what it costs: a rate card is three monthly rates in your own currency - per vCPU, per GB of memory, per GB of storage - set on the credential that provisions the capacity. Proxmox ships with defaults; other private providers read not priced rather than free. [Cloud cost →](/platform/cloud-cost#private-capacity-rate-cards) **2026-09-01** ## The overview says what every cluster is The cluster overview opens with a configuration card for every cluster kind: provider and region, the distribution actually running your API server, the Kubernetes version with the kubelet range, the CNI, and the node shape with each group's instance type and autoscaling bounds. Provider-managed clusters finally show their real version everywhere instead of a placeholder. Cells the platform has no honest data for disappear rather than reading Unknown. [Cluster nodes →](/platform/cluster-nodes) **August 2026** Cluster Mesh across providers, cost evidence and a waste scan, power schedules, audit trails, and the largest CLI release yet ## Connect clusters with Cluster Mesh **Cluster Mesh** connects your clusters into one Cilium ClusterMesh over the platform-managed WireGuard overlay - across providers, accounts and sites, with nothing exposed publicly. `ankra cluster mesh readiness` says whether a set of clusters can mesh and exactly why not; `create` and `join` do the rest: the mesh's shared certificate authority is minted and handed to every member, each member's Cilium re-renders with its identity and peer list, and every node gets an encrypted tunnel - and a route - to every node of every other member, with the `clustermesh-apiserver` dialled inside the tunnel on the peer's overlay address. Statuses (`pending / configuring / ready / degraded`) are derived from what each cluster's agent actually proved, never from intent, and a **global service** (`service.cilium.io/global`) resolves to healthy backends in whichever cluster has them. Clusters that already exist join the party with `ankra cluster mesh make-ready` - the identity and overlay range are allocated day-2 and the nodes converge in place, nothing deleted or recreated (one kubelet restart per node is the whole disruption). UpCloud and Proxmox are wired today; a Proxmox site brings one public address and a UDP port-range forward, and everything else is the platform's job. [Cluster Mesh →](/platform/cluster-mesh) ## Notifications on your phone The Ankra mobile app now receives push notifications, on iOS and Android. Nothing arrives by default, and that is deliberate: a notification reaches a phone only when a **routing rule** sends it to a push audience, the same way a rule sends one to a webhook. Set the destination type to **Push notification** and pick the audience - all cluster members, or organisation admins - then filter by kind and severity as usual. "Every critical notification" and "only when a bastion goes offline" are each a single rule. Devices add themselves. Sign in to the app, accept the prompt, and the phone appears under **Profile → Notifications**. The app holds no credential of its own: it hands the token to the signed-in page, which registers it against your session, so a device always belongs to exactly the member who signed in on it. From there you can mute one phone without signing out of it, remove it, or **Send test** - which delivers straight to that device without consulting any rule, so you can prove the path works before writing one. Severity decides whether a notification interrupts. A critical arrives time-sensitive on iOS, so it breaks through Focus, and at high priority on Android, so it is delivered even in Doze; informational ones arrive quietly and will not light the screen. On Android the three importance levels are real notification channels - *Critical alerts*, *Warnings* and *Updates* - which means Android hands them to you: set *Warnings* to silent in system settings and it stays silent through every app update. Your existing snoozes apply unchanged, so a kind you have muted is muted on your phone too, and a notification addressed to one person reaches only their devices. A device that registers but never rings is almost always a missing routing rule rather than a broken phone, which is why the devices page shows what the platform knows: the last delivery, and - when Apple or Google reports a token is dead, because the app was removed or the token rotated - the reason delivery stopped, rather than a silent gap. [Mobile push notifications →](/guides/mobile-push) ## A Cost section with one-click savings Cloud cost now has its own place in the sidebar, between Credentials and Security. **Overview** is the fleet rollup - projected month end with its pace against the run rate, month to date, run rate, priced coverage, the split by provider and the costliest clusters. **Savings** turns the same data into money: right-size idle capacity (one click opens Ankra AI's cost optimisation, whose scaling updates apply to the cluster after your confirmation), stop non-production clusters off-hours (one click writes a weekday 19:00 stop / 07:00 start pair - workers scale to zero, weekends stay down), and reduce unallocated spend - plus a per-cluster breakdown of where the money goes and the costliest namespaces fleet-wide, so every team sees the cost of what it runs. **Settings** holds the display currency - **euro by default** now - your effective discount and the network egress estimate, admin-only and applied to every figure at read time. The clusters list carries the rollup as a collapsible strip above the table. The same figures answer everywhere else: `ankra cost summary|cluster|settings` in the CLI, and `get_fleet_cloud_cost` / `get_cost_settings` / `update_cost_settings` in Ankra AI chat and over MCP. [Cloud Cost →](/platform/cloud-cost) ## Cost recommendations that show their evidence A savings recommendation used to ask for trust: *right-size this cluster* with nothing on screen proving anything was idle. Every cluster in **Cost → Savings** now opens its own drill-down page with the proof. For each node, the measured **average, p95 and peak** CPU and memory usage over a selectable window (24h / 7d / 30d) stands against what the scheduler reserved, with the node's own monthly cost and a defensible verdict: *underutilised* (with the saving from sizing to 1.5x the observed p95) only when even the busy moments fit well under the reservation on both resources, *spiky - keep headroom* when bursts make the average misleading - a bursty node is never called safe to shrink, whatever its average - and *no metrics* when usage is genuinely unknown, never "0% used". Around the node evidence: the 30-day cost trend, a component donut of where the money goes, the per-node usage charts so the pattern behind the statistics is visible, namespace allocation next to requested-vs-used efficiency, and the same one-click resolves, now with the evidence attached. The Overview gains a fleet cost trend, honestly scoped to the analysed clusters. And because some waste never shows up in utilisation at all, a six-hourly **waste scan** now inventories your cloud accounts for resources that bill while doing nothing: stopped Hetzner servers (billed fully as long as they exist), unattached volumes, unassigned primary IPv4 and floating IPs, load balancers with no targets, and Kubernetes persistent volumes stuck in `Released`. Findings land on the Savings page and each cluster's drill-down with their monthly price and a one-click **Resolve with AI** naming the exact resource; a credential that could not be listed renders as a partial scan, never as "no waste". Two new reads carry it all: `GET /org/clusters/{id}/metrics/utilisation` and `GET /org/cloud-cost/waste`. [Per-cluster drill-down →](/platform/cloud-cost#per-cluster-drill-down) ## API tokens the organisation owns An API token used to belong to whoever created it, in two ways that made it a poor fit for automation. It authenticated only while that person was still a member, so removing someone from the organisation stopped every token they had minted - the deploy pipeline they set up two years ago fails with a `401` and nothing in the log says why. And it carried their whole authority rather than a capability anybody had chosen for it. It was not really scoped to an organisation either: the same header the CLI's `--org` flag uses re-pointed any token at any organisation its owner belonged to. **Organisation → Service tokens** is the other kind. A service token authenticates as a *service account* - a principal the organisation owns, holding one role an administrator picked - so it survives offboarding, appears where any administrator can review and revoke it, and reaches its own organisation and no other. Creating one takes a name, a role and an expiry; the secret is shown once. Minting under an existing name adds a second token to that same account, which is how a credential is rotated with no gap: issue the new one, roll the deployment, revoke the old one, and the account keeps one continuous audit history across the rotation. Revoking stops a single credential and leaves the account's others alone; deleting an account's last token stands the account down, so a token minted under that name later needs its role granted again deliberately. Minting requires `members.manage`, and so does reading the list - an inventory of standing credentials and the authority each one carries is not the member list. One guard goes further than the role-assignment surface needs: you cannot mint a token carrying permissions you do not hold yourself. Granting a colleague a role hands them nothing until they sign in as themselves, but a token works the moment it exists, so without that rule "can manage members" would quietly have meant "can escalate to owner". A service token can also be scoped to `mcp:read` or `mcp:write`, reaching the hosted MCP server only. The same surface is available to automation at `/api/v1/org/organisation/service-tokens`. [Service tokens →](/guides/service-tokens) ## Move a cluster to another organisation An imported cluster can now change organisations without being re-imported. From the cluster's **⋯** menu, the cluster overview or **Settings → Danger Zone → Move cluster**, an organisation admin picks a destination organisation they also administer, types the cluster name, and the cluster - with its agent, executions, resources, findings, insights and variables - now belongs there, still connected. Access grants, cluster-scoped role assignments, kube tokens, notification routes and mutes, report schedules and cluster group memberships from the previous organisation are detached and listed in the result; billing, audit and chat history stay where they were. Ankra-provisioned and managed Kubernetes clusters stay put for now - their provider credential belongs to the organisation that created them. Both organisations record the move in their audit trails. The move is refused while operations are running, while the cluster is in a cluster mesh, while it has DNS zones bound to the current organisation, for playground and sandbox clusters, and when the destination already has a cluster with that name. The same move is `ankra cluster move --organisation ` in the CLI and the `move_cluster_to_organisation` tool in Ankra AI, both behind the same two-organisation admin check. [Move to another organisation →](/platform/cluster-settings#move-to-another-organisation) ## Security counts that open what they count A finding row in the Security Center used to say **Open · 7** and **Resolved · 2** next to **7 clusters · 7 workloads** and leave you there: none of it was clickable, and the two resolved occurrences were not even loaded, so the sheet's own **Resolved** filter answered *no occurrences match* under a chip that said two. Every number on a row is now a way in. A status chip opens the finding on that status - the live ones filter the remediation plan, **Resolved** lists the occurrences the latest scan no longer reports, most recently resolved first, with the workload, the image and when each went away. The affected cell opens a new **Affected clusters** table: one row per cluster with its live workloads, the same four counts, and when it last reported the finding, each cluster deep-linking into its own Security tab with the finding already open. The chips are now labelled **Occurrences** and say so in the sheet - one per workload, container and image - which is why seven open occurrences can sit beside seven workloads and two resolved ones. Counts and lists come from the same query, so a chip never names a row the sheet cannot show; the API carries both as `clusters` on the finding detail and a paginated `GET /api/v1/org/security/findings/{finding_id}/occurrences?status=…&cluster_id=…`. [Security Center findings →](/guides/cluster-security#findings) ## A verified email before invitations Signing in now proves the mailbox before it proves anything else. An account whose email address is still unverified can no longer pick up an invitation sent to that address: **Pending invitations** stays closed and accepting one answers *Verify your email address before acting on invitations* until the verification link has been opened - then the same account is admitted and the invitation is waiting where it was. A first sign-in with a provider that does not share an email address at all (some Microsoft Entra ID and social identities) is refused with a clear message instead of creating an organisation that could never be contacted or invited; sign in with an account that carries an address. Accounts created before this keep signing in as they did. [Roles and access →](/guides/roles-and-access) ## The Ankra registry cleans up after itself Images pushed to your organisation's private Ankra registry no longer accumulate forever. Every organisation project now carries a retention policy that Harbor runs once a day: an artifact is kept while it was pushed within the last 30 days, and the three most recently pushed tagged versions of every repository are kept regardless of age. Older tagged images beyond those, and the untagged leftovers of overwritten tags once they are 30 days old, are removed. Plan rollbacks around it - a version is reachable while it is one of the last three or under 30 days old - and redeploy long-lived workloads from a recent build so a reschedule never reaches for an image that is gone. Registries you operate yourself are not touched. [Image retention →](/guides/cicd-pipeline#image-retention) ## Move a Docker deployment into a cluster in one command Getting an application off Docker Compose used to be two jobs, and the second one was the hard one: translating the services into Kubernetes objects, and then getting the data out of the containers and into the databases you just deployed, by hand, with a laptop in the middle holding a copy of production. `ankra migrate up ./app --cluster production` does both. It converts the Compose project into a stack, applies it to the cluster, waits until each database workload has a ready pod, dumps the deployment's PostgreSQL, MySQL and MariaDB databases, and restores them in-cluster. `--plan` prints what it found first and changes nothing: the databases with their sizes read from the running containers, the estimated dump size against your free disk, and an explicit list of what will not be carried across. The run is re-runnable, so you rehearse against a live source and then cut over with `--stop-source`, which stops the source's non-database services before the final dump and prints the `docker start` command to bring them back. The dumps travel through your own [backup vault](/guides/backups): the CLI uploads to your bucket on presigned URLs, and the cluster's agent runs a Job that verifies the SHA-256 sums and restores. Ankra never holds the data, no vault credential reaches the cluster, and nothing on your machine needs `kubectl` or a database client. The individual steps stay available as `ankra migrate convert`, `export`, `data`, `restore` and `restore-status` when you want to inspect what each produced. Compose is only the format that ships with it. The conversion and the export are done by a **module**, and any executable named `ankra-module-` on your `PATH` or in `~/.ankra/modules` is one: it answers `describe`, `detect` and `convert` with JSON on stdin and stdout, adds `export` if it can dump its own databases, and can be written in any language. `ankra migrate modules` lists what you have, and a worked example in about a hundred lines of Python ships in the CLI repository. [Moving a Docker deployment →](/guides/migrate-from-docker) · [Writing a module →](/guides/migrate-modules) ## The inbox knows who a message is for A notification is now addressed either to the whole organisation or to one person. Personal messages reach only their addressee, wear a **For you** marker in the Activity feed, and count separately - so a direct message never drowns in the organisation's pile. Filter the Activity page by audience (**All messages / For you / Organisation**) to see just yours or just the shared ones. Reading, acknowledging and dismissing were already per person; the audience makes the distinction visible everywhere, API included (`audience` on every notification, an `audience` list filter, and `personal_unread` on the counts). [Activity & Inbox →](/platform/activity-inbox#organisation-messages-and-messages-for-you) ## A DevSecOps agent on every AI team Every organisation's default AI team gains a sixth member: **DevSecOps**, a security specialist whose job is to resolve the critical and high vulnerabilities in the images running on a cluster. Open it from a cluster's chat and it reads that cluster's security report, maps each vulnerable image to the connected application deployed there - or to the add-on the report attributes it to - and fixes at the source: base-image and dependency bumps opened as pull requests on the application's repository, one per application with every CVE it clears listed, and add-on upgrades proposed with a rollback. Known-exploited CVEs come first; a CVE with no fix is reported, not hidden. Its capability envelope refuses deleting a resource, an application, a stack, or an add-on, so a finding is never made to disappear by removing what runs. Organisations that already have the five-agent team get the DevSecOps agent the next time anyone opens **AI → Agents**; a default you deleted stays deleted. It arrives ready to act: it ships acting as your organisation's agent identity, which Ankra provisions on the operator role the first time the roster is opened - no board-staffing setup first - while its **Propose** autonomy keeps every cluster write behind your approval. Stand the identity down under **Settings → Autonomy → Board staffing** if you would rather it only reports. [The DevSecOps agent →](/platform/ai-agents#the-devsecops-agent) ## Default and catalog AI agents now run on the Quick model unless you chose one The six default roles and the four catalog agents (Incident Responder, Gap Scanner, Reviewer, Nightly Auditor) now ship pinned to **Quick**, and agents already seeded or hired from those templates that nobody had pinned move to Quick too. Agents you created yourself, and any agent whose model you chose, keep their setting. Raise a single agent to **Think** or **Expert** from its Edit dialog when its work needs deeper reasoning. [Model →](/platform/ai-agents#model) ## Every AI agent can run on a cheaper model and carry a daily token limit Every organisation AI agent now has two cost controls of its own. **Model** pins the agent's turns to a rung of your model catalog - **Quick**, **Think**, **Expert**, or a custom entry - instead of leaving each turn to the router; the DevSecOps agent ships on **Quick**. **Daily token limit** caps what the agent may consume in one UTC day across its runs and persona chats. An agent that reaches its limit is not dispatched again until midnight, a turn already running finishes with one wrap-up iteration rather than being cut mid-thought, and a notification - in the inbox and through your notification routes - names the agent, the tokens used, and how to raise the limit. Today's usage sits beside the limit on the agent page. The DevSecOps agent ships with a 2,000,000-token day; the other defaults stay unlimited until you set one. [Models and daily token limits →](/platform/ai-agents#model) ## Debug pods that impersonate a workload A distroless image has no shell to attach to, and a crash-looping container is gone before a terminal can reach it. **Debug** on any pod now spins up a separate pod that impersonates it - the same service account, the same node, the same volumes and volume mounts, the same environment variables and `envFrom` sources, the same tolerations and security context - under an image chosen for its tools: netshoot by default, or busybox, alpine, ubuntu, or any image you name. You land in its terminal with the workload's mounts and environment in front of you, and the workload itself is never modified. **New debug pod** on the pods list does the same from a namespace, with nothing mirrored. Two things are deliberately not copied. Labels never are, so a debug pod never sits behind the workload's Service or inside its PodDisruptionBudget; and host namespaces are left alone. Every debug pod carries a lifetime (30 minutes to 8 hours) that the kubelet enforces on its own, the agent removes expired ones, and creating or deleting one writes an audit row naming the image and what was mirrored. Creation needs `kubernetes.write` and `kubernetes.exec`. From the CLI: `ankra cluster debug create --namespace --from-pod `, plus `list`, `delete` and `images`. Needs cluster agent 2.1.1074 or newer. [Debug pods →](/platform/debug-pods) ## Every pod terminal session is recorded Opening a terminal into a pod used to leave no trace. It now writes an `open_pod_terminal` audit row, and everything typed and everything the container answered is stored with the session (up to 4 MiB; longer sessions are marked truncated). In the organisation audit log and a cluster's Audit tab, the row shows the session's facts - who, which container, which shell, how long, how it ended - and **Replay session** plays the recorded output back into a read-only terminal with the typed input listed beside it. `ankra org terminal-session --transcript` prints the same recording. Transcripts are readable with `audit.read` and never travel inside the audit row itself. [Recorded sessions →](/platform/pod-terminal#recorded-sessions) ## Multi-zone UpCloud clusters on a WireGuard mesh UpCloud has no multi-zone region - every private network, router and load balancer is bound to one datacenter - so an UpCloud cluster was a single failure domain, and the only answer to "what if Helsinki goes down" was a second cluster. A cluster can now be created across a **zone pool**: pass `zones` (at least three, with three control planes, one per zone) and Ankra builds a router, private network and NAT gateway in every zone, joins the nodes with a platform-managed kernel WireGuard mesh over UpCloud's account-wide utility network, and has Kubernetes advertise the overlay address on every node. Control planes spread one per zone, node groups spread across the pool or pin to a zone with `zone`, day-2 growth follows the pool, and `ankra cluster upcloud zones` adds zones later. Losing a zone costs capacity, not the cluster. Load balancers and volumes stay in the primary zone - both are zone-local on UpCloud - and every node carries the standard topology labels. Multi-zone placement is rolling out per organisation; ask us to enable it for yours. [Multi-zone clusters →](/guides/upcloud-clusters#multi-zone-clusters) ## GitOps repoints are atomic, and a wrong branch cannot strand a cluster Moving a cluster to a different GitOps repository or branch with `ankra cluster apply --allow-repoint` used to commit the switch first and write the cluster's state to the new source second. A target Ankra could not write to - a branch that did not exist, a branch protected against direct pushes - left the cluster bound to it with its sync baseline cleared, one sync away from applying whatever stale file sat at the target's head. The write now happens first, on the same transaction as the switch, so a failed write changes nothing: the cluster keeps syncing from its current source and the error says so. A branch that does not exist is refused before anything is attempted, together with the branches the repository does have. The refusal you see without the flag also describes what a repoint actually does - Ankra writes the cluster's current state to the new source before syncing from it - instead of predicting a prune from the target's current contents. [Repointing a cluster →](/reference/importcluster#specgit_repository) ## Find an organisation by typing its name If you belong to many organisations, the profile menu's **Switch organisation** submenu used to run off the bottom of the screen with no way to scroll it, and the **All organisations** and **Create organisation** links at its foot went with it. The submenu now opens with a search box: start typing anywhere in it to narrow the list by name, slug or id, press **Enter** to switch to the first match, or use the arrow keys to pick one. The list scrolls within the screen, and the links stay pinned below it - **All organisations** now shows how many you belong to. The **All organisations** page gained the same search, lists your current organisation first, and pages through the rest twenty at a time; its page controls, previously decorative, now work. [Switching organisations →](/concepts/organisations#switching-organisations) ## Known exploited vulnerabilities (CISA KEV) and EPSS in the Security Center A CVSS 9.8 nobody is exploiting used to outrank a CVSS 7.5 that is being exploited right now. The Security Center now cross-references every finding with the public CISA Known Exploited Vulnerabilities catalog and the FIRST EPSS exploit-prediction scores, joined platform-side by CVE id, so no image data leaves your cluster and Trivy Operator needs no extra configuration. Findings carry a **KEV** marker with CISA's date added and remediation deadline, plus the EPSS probability and percentile. Filter with the new **Known exploited (CISA KEV)** chip, sort by **Exploitability** (KEV first, then EPSS, then severity), and read a **Known exploited** tile on the fleet scorecard. Cluster, workload, and add-on posture views count known-exploited findings, remediation candidates rank them first, and the scheduled security email calls them out with their own banner. Alert rules gain two `security_findings` metrics - `kev` and `new_kev_cves` - and the new-CVE notification now flags KEV listings of any severity. The same fields ship on the security API (`known_exploited`, `kev_date_added`, `kev_due_date`, `kev_ransomware_use`, `epss_score`, `epss_percentile`, plus a `known_exploited` filter) and in the AI security context. [Exploitation intelligence →](/guides/cluster-security#exploitation-intelligence-cisa-kev-and-epss) ## Every CVE has an Ankra advisory page A finding used to hand you off to the scanner's advisory site - avd.aquasec.com, nvd.nist.gov - to learn what a CVE actually is. The Security Center now has its own **advisory page** for every CVE: the platform reads the public record from the NVD 2.0 API and OSV, parses it, and renders it in the Security Center's vocabulary. The **Advisory** link on every findings row and **Open advisory** in the finding sheet lead there. The page carries the analyst description with CWE ids and aliases; CISA's KEV entry, the EPSS probability and CISA's SSVC decision under **Exploitation**; the lead CVSS score with its vector spelled out component by component and every published assessment behind it; the affected products and version ranges from both sources, written as "2.34 before 2.39 (fixed in 2.39)"; the organisation's own findings for the CVE with a link into each; and the sources' references grouped patches-first. It tells "not read yet" (the page polls while the platform fetches the record, and opening it puts that CVE at the front of the queue) apart from "no public record", and a provenance footnote says when NVD and OSV were last read. Stored records are re-read every two weeks. The API serves the same content at `GET /api/v1/org/security/advisories/{cve_id}`. [Advisory pages →](/guides/cluster-security#advisory-pages) ## Known-exploited vulnerabilities are now impossible to miss, and the Security Center answers in milliseconds The KEV signal was easy to walk past: a small marker in a column, on a list sorted by scan time. Now, when CISA lists any of your actionable findings, the Overview, the Findings page, and every cluster's Security tab open with a red **exploited in the wild** banner that says how many vulnerabilities are being exploited, how many are past CISA's remediation deadline, and how many CISA links to ransomware campaigns - with **Review exploited findings** and **Fix with AI** one click away. Findings open ranked by **Exploitability** (KEV first, then EPSS, then severity), each listed row shows CISA's deadline in explicit tense and a **ransomware** marker, the **Known exploited** tile carries the overdue and ransomware subsets, and the finding sheet quotes CISA's own entry: vulnerability name, vendor and product, and **CISA required action**. The API adds `kev_vendor_project`, `kev_product`, `kev_vulnerability_name`, and `kev_required_action` on findings and candidates, and `known_exploited_overdue` and `known_exploited_ransomware` on the overview; the AI security context carries the deadline and required action too. Underneath, the Security Center stopped re-counting every scanner observation your organisation has ever recorded on each page load. The overview, the findings list and its filter counts, the remediation candidates, and the cluster posture now read from posture rollups the platform keeps current inside the same transaction that records a scan or a disposition change - so a findings page that took four to twelve seconds on a large fleet (and timed out under load) answers in milliseconds, and acknowledging a finding is reflected on the very next load. [Exploitation intelligence →](/guides/cluster-security#exploitation-intelligence-cisa-kev-and-epss) ## kubeadm is the default distribution everywhere A cluster created without a `distribution` now gets **kubeadm** - vanilla upstream Kubernetes with Cilium - on every surface: the API, the CLI `--distribution` flag, the AI create tools and the Terraform provider, matching what the create wizards already preselected. An omitted `cni` follows the distribution: Cilium on kubeadm, Flannel on k3s. k3s stays fully supported - pass `--distribution k3s` (or `"distribution": "k3s"`) to get it - it is just never a default. Existing clusters are untouched. ## Order playgrounds at the size you need The playground is no longer one fixed try-it size. **Create cluster** now offers **Playground** as a first-class option: pick a size - from the free Trial up to 16 vCPU / 32 GB - and Ankra provisions it on its own infrastructure, no cloud account or credentials needed. Each size shows its flat monthly price in the picker. Only the free Trial is limited to one per organisation; paid playgrounds can be many, bounded by a per-organisation memory budget of 8 GB by default. Ordering a paid size requires billing permissions, a billing plan and a payment card on file - the Trial needs none of that. When a size does not fit, the picker offers a **limit-increase request** - the new limit and a short justification, reviewed by the Ankra team, with a notification when it is decided. The free monthly AI allowance gains the same request path from the billing page's allowance card. Paid playgrounds also **resize in place** (Settings → General, or `ankra cluster playground resize`): the quota and the monthly price change, billed pro-rata across the change, and nothing you deployed is touched. Billing is fair by construction - it runs from the moment the environment is ready, never from the order that placed it - and every order, resize and destroy leaves a notification receipt. Organisations with long-overdue invoices have new paid orders refused and, after three weeks, paid playgrounds reclaimed. The price you order at is the price you keep - later catalog changes never re-rate a running environment. Sizes the shared pool cannot place right now are shown greyed out rather than hidden, and the free trial keeps its one-click path everywhere it exists today: the first-run screen, the create-cluster dialog's banner, and `ankra cluster playground create`. The whole lane now speaks every surface: alongside the portal, the `/api/v1` API and the CLI, **Ankra AI and the MCP tool catalog** can list the sizes with live availability and budget headroom, order a playground, check its provisioning, resize it, destroy it, and file a limit-increase request. The mutating actions are confirmation-gated - the approval card names the size and the monthly price before anything is ordered - and every billing gate applies identically to what a chat agent orders and what a click orders. Limit requests live under billing, and the billing page now shows it: a **Playground memory budget** card reports how much of the budget your paid playgrounds use, carries the same request path the size picker offers, and - like the AI allowance card beside it - shows a pending request under review and a declined one with its outcome, so a decision is discoverable where the request was made. Billing now opens with **This month** - what each thing has cost so far and where it is heading. Compute and playgrounds, which are charged to your invoice in EUR, sit in one group; the AI allowance included with your plan, priced in USD and never invoiced, sits in another - so the two currencies are never added together. Every row reads the same way: what you used, so far, projected, and the limit that applies. Playground spend, previously invisible on every billing surface, is now reported and split from compute, and each invoice shows its playground share as its own column. Two fixes landed with it: the playground memory budget counts only environments you actually have (a destroyed or failed environment no longer holds your budget), and reads the organisation whose billing page you are on rather than the one you last switched to. The playground memory budget now names the playgrounds it counts - plan, name, memory and when each was ordered - on the billing page's budget card and in the plan catalog (`GET /org/clusters/playground/plans`, and the `list_playground_plans` chat and MCP tool). A budget past its limit, such as a Large ordered before the budget existed, reads as what it is rather than as a miscount, with a line on what over budget means: new playgrounds and upgrades wait, what you run keeps running. The order gate now counts exactly what the catalog shows. And billing stops when the receipt says it does: destroying a playground closes its price of record at the request, not when the teardown finishes. [Playground →](/guides/cluster-sandbox) ## A Nodes section for every cluster Every cluster gains a **Nodes** section in its sidebar, directly above Kubernetes: the infrastructure view of the machines it is made of. The overview joins what the cloud provider knows about each VM with what Kubernetes knows about the node - role, group, instance type with vCPU, memory, disk and list price from the live catalog, a GPU chip from the node's feature-discovery labels, Kubernetes readiness with the kubelet version, the VM's state, location and addresses - and puts the controls on the row: cordon, uncordon and drain with progress, restart the VM, resize the bastion, open the node in Kubernetes, or the provider's record. A machine Kubernetes has never seen reads *Not joined* or *Not a node*, never as broken. Stat tiles sum the fleet - machines by role, ready nodes, vCPU, memory, GPUs, monthly run rate - and say *not reporting* rather than zero when a figure cannot be read. Node groups, the control plane and the bastion each get a tab. The node-groups tab carries everything Settings → Nodes offered plus **Max out**: Ankra reads your cloud account's remaining quota through the cluster's credential and sizes a group to the most it can hold - the tightest of remaining servers, vCPUs and memory for the group's instance type, capped at 100 - naming the bound and the added cost before you confirm, and saying *unavailable* when a provider hides its limits rather than guessing. The bastion tab shows its health as the scheduler last recorded it, with diagnose-over-SSH, restart and resize. The same quota report is on the API as `GET /org/clusters/{provider}/{cluster_id}/quotas`. Settings → Nodes redirects to the section, so older links keep working. Playgrounds carry their **size card** on the overview: a paid one names the direction and the monthly difference before a resize (*Upgrade to Medium: +€15.30/mo*), and the free trial offers **Upgrade to a paid size** instead of a card with no control. The same tab adds **standalone VMs**: a machine on the cluster's network that is not a Kubernetes node - a database host, a build box, a jump host of your own - provisioned the way the bastion is, with the cluster's SSH keys, private-network only unless you give it a public address, and kept running when the cluster is stopped because it carries your state. Add one by name and size from the live catalog, then restart, resize or remove it from its row; VMs also appear in the machines table with the role **VM**. Available on Hetzner, UpCloud, DigitalOcean, OVH and Proxmox clusters, and the bastion and every VM are now metered into the cluster's cost as **Bastion & VMs**. The UpCloud node-group picker reads the cluster's live catalog too, so GPU plans appear with their accelerator fit and price. The API carries VMs under `/org/clusters/{provider}/{cluster_id}/vms`. [Nodes →](/platform/cluster-nodes) ## Agent Skills for every AI assistant, not just two `ankra skills install` used to reach Cursor and Claude Code. It now detects the AI assistants configured on your machine and installs Ankra's curated Agent Skills into all of them: Claude Code, the Claude app, Cursor, Codex, GitHub Copilot, Windsurf, Gemini CLI, OpenCode, Cline, Zed, OpenClaw, and anything that reads `AGENTS.md`. `--client` replaces `--editor` (kept as a deprecated alias) and takes a list, `all`, or `auto`; `ankra skills clients` shows what was detected and where each assistant's skills land. Assistants with no skills mechanism of their own get the skills in a shared library plus an index in the file they always load - which is what makes the skills work there at all. The Claude app, which cannot read your filesystem, gets one uploadable bundle per skill. Assistants with slash commands also get the Ankra workflows: `/ankra-new-cluster`, `/ankra-ship-service`, `/ankra-connect-app`, `/ankra-triage`, `/ankra-promote`, `/ankra-harden` and `/ankra-profile`. The skill set grew to twenty-two, adding end-to-end coverage for getting started, building clusters (provider, region, instance family), domains and DNS, deploying applications, wiring an app to services that already run (an LLM gateway, Harbor, a database), stack profiles, troubleshooting, security, and supervising Ankra's own AI agents - and every `ankra` command the skills document is now verified against the CLI's real command tree at build time. [Agent Skills →](/integrations/agent-skills) ## A ticket now shows what its agent is doing right now, and opens the session it is doing it in A ticket said an agent was assigned and, while a run was live, showed a pulsing dot - and then nothing moved for the whole run, so there was no way to tell a busy agent from a wedged one. The timeline now ends in a live panel while a run is in flight: a spinner, the step the agent is on with what it is calling it on, and the last few steps behind it, updating as the run moves. A queued run, and one parked waiting for a person, say that instead of claiming to be working. The session behind it is now reachable, which it was not from anywhere. **Open the agent's session** on the live panel, the **is working** chip at the top of the ticket, the timeline entries recording that a run started and how it ended, and the **Agent run** entry under **Linked** all open the transcript of the run working *that* ticket - to read its tool calls and results, answer a question, decide an approval, or guide it. Every run has its own address (`/organisation/ai/agents/runs/`), so a pasted link opens the run it names; previously each of these landed on the agent's most recent run instead, which for an agent working several tickets was the wrong session. Picking a run on an agent's page puts it in the address too, and the fleet run history opens the run you clicked rather than its agent. Fixed alongside: a finished run's transcript replayed as tool calls and silence, because the agent's own prose was dropped on the way to the page. [Watching an agent work →](/platform/ai-board#watching-an-agent-work) ## The pod page is condensed too, and it shows what a container asks the GPU for Following the node page, a pod's **Overview** now opens on state chips (phase, QoS, the GPU it requests, restarts with their recency), an identity strip, and tiles for containers ready, restarts and the CPU / memory / GPU requested against limits. Each container is one compact card - state, restarts, the previous termination such as `OOMKilled (exit 137)`, image with copy, **requests → limits per resource including `nvidia.com/gpu`**, ports, probes with their inferred status and the command line - instead of a fully expanded three-column block; init containers sit in the same list. Conditions read as one row each with the kubelet's message only when something is wrong, the network, security, scheduling, topology, service-account, DNS and advanced cards fold into **Placement** and **Runtime**, volumes get a panel that names their source, and labels and annotations are collapsed and grouped like the node page's. [Pod details →](/platform/kubernetes-insights#workloads) ## A blocked ticket now asks its question as a choice - pick an option, or answer with something else When an AI board agent found two ways forward and needed a person to pick one, it wrote them into a comment, moved the ticket to **Blocked**, and the page showed a blocked ticket with prose to read and a comment box to answer in - so the decision was easy to miss and easy to answer in a way the agent then had to re-ask about. An agent that blocks on a choice now hands the options over as data (`decision_options` on `ticket_update_status`, with a `decision_prompt`), and the ticket page opens on the question: each option with its summary, the recommended one marked, an optional note, and **Something else** for an answer in your own words. **Send decision** records a `Decision:` comment on the timeline and resumes the agent, which acts on the answer without asking again. The answer is also on the API at `POST /api/v1/org/ai-tickets/{ticket_id}/decision`. The same choice now reaches you wherever the ticket does. In **AI chat** (and over MCP) `ticket_get` shows the pending question and the new `ticket_decide` tool records your answer. The CLI gains an AI board: `ankra tickets list|get|events|comment|transition|decide`, with `ankra tickets get T-8` printing the options and `ankra tickets decide T-8 --option a` (or `--answer "..."`) answering them. In **Slack and Teams** the thread reply that announces the block, and the escalation card while the ticket still waits, carry the options as buttons - the recommended one highlighted - and **Something else** asks for a thread reply starting with `Decision:`, which is recorded as the answer rather than as a plain comment. [Answering a choice →](/platform/ai-board#answering-a-choice) ## The cluster domain is your own domain once a custom DNS zone publishes it An organisation whose domain lives in its own DNS account cannot register it as its Ankra domain, so it sets it as the Preview domain and declares it as a custom DNS zone - and was still told its cluster domain was `..ankra.cc`. `${{ ankra.cluster_domain }}` now resolves to the Preview domain whenever a custom DNS zone declared for the organisation or the cluster covers it, and the same rule drives Ankra AI's hostname advice (`get_preview_domain` reports `cluster_domain` and who publishes it), `ankra cluster domain` (a new **Public domain** line), and the Preview domain's "hostnames will not resolve" warning, which no longer fires for a domain the cluster's own external-dns publishes. [Built-in variables →](/concepts/variables#built-in-variables) ## The node page is redesigned, and it knows about GPUs A Kubernetes node's page now opens on a header of readiness, role, accelerator, instance type, provider and placement chips, a fact strip, and stat tiles for what the scheduler can hand to pods - CPU, memory, GPU, ephemeral storage and the pods placed on the node against its limit. Conditions read as **Healthy** or **Problem** with reason, message and last transition rather than a raw `True`/`False`; the system, network, allocatable-resources, taints and cached-images cards replace the old key/value lists; and labels are grouped by prefix with search and click-to-copy, so a node carrying sixty discovery labels is readable. On a GPU node - `nvidia.com/gpu`, a MIG profile, `amd.com/gpu`, Intel or Gaudi - an **Accelerator** card reads the GPU Feature Discovery labels the GPU operator stamps on the node: product, family and compute capability, count and memory per GPU, CUDA driver and runtime, sharing strategy (exclusive, time-slicing, MPS, MIG with its active profile), vGPU, driver-upgrade state and the operator components deployed, plus whether the device plugin is advertising allocatable GPUs yet. With a Prometheus metrics source connected it reads the DCGM exporter too: utilisation, framebuffer, temperature, power, clocks and the pod attached to each GPU, the pods on the node requesting GPUs, and per-GPU charts on the **Metrics** tab. [Kubernetes Nodes →](/platform/kubernetes-nodes) ## AI board tickets now show what a fix actually changed - the cluster, the stack, the operation, and a bill of materials A ticket tracking a fix the AI chat or an agent applied used to show a timeline of tool names and a **Pending action** link that opened the whole Inbox - nothing on the page said which cluster or stack was touched, what changed in it, or whether the apply had actually run. The ticket now carries a **Changes** ledger, one entry per applied action like a commit log: the tool, who approved it, the risk and reversibility, the cluster and stack with the namespaces involved, the times it was proposed and settled, and the operation it created on the cluster's operations ledger with its step outcome - the entry reads **applying** while the apply is still running and **failed** if it failed after the tool returned. Expanding an entry shows the same bill of materials the approver reviewed: addons with chart and version, manifests with kind and namespace, and their diffs. A **Bill of materials** panel in the rail folds every change into the ticket's blast radius - clusters, stacks, namespaces, addons, manifests and executions, each linked to its page - and **Cluster context** gained **Stacks** and **Operations** shortcuts. The ledger is also on the API at `/api/v1/org/ai-tickets/{ticket_id}/changes`. [Changes and the bill of materials →](/platform/ai-board#changes-and-the-bill-of-materials) ## Declared registries can keep their flat component repositories, and an application can be moved to another GitHub credential Two follow-ups from wiring the first organisation onto its own Harbor. A monorepo's images were always addressed as `//`, so a registry whose pipeline had been publishing `commerce-images/backend` for months looked unpublished to Ankra; `flat_repositories` on the image registry declaration (**Settings → Image registry**, `ankra application registry set --flat-repositories`) makes publish readiness, the deploy gate and the generated workflows use `/` instead. Where the names differ altogether - `commerce-images/commerce-backend` - `component_repositories` names each component's repository outright (`--component-repository backend=commerce-backend`, or one field per component in the settings panel). And the GitHub credential an application was created with can now be changed - `ankra application credential set --credential ` - so an application bound to an App installation that cannot reach its repository no longer has to be deleted and recreated. [Using your own registry →](/concepts/applications#using-your-own-registry) ## Every application now gets its own registry robot account - on Ankra's registry and on a Harbor you operate Applications used to log in to the registry with one push robot shared by the whole organisation, and an application that publishes to a Harbor you run got no robot from Ankra at all - you created one by hand and pasted it into the repository's Actions secrets. Ankra now mints a push robot **per application** (`robot$+app-`) on the project it publishes to, stores it as a managed registry credential, and writes it into the repository as the build's login, so a leaked secret is rotated or revoked for that one application and a deleted application takes its robot with it. On your organisation's Ankra registry this happens on setup and reconcile, with no action needed; applications onboarded earlier get theirs the next time **Fix automatically** runs or when you create it from **Settings** → **Image registry**. On a registry you operate, name a credential with project administrator rights as `admin_credential_name` on the image registry declaration and Ankra mints, rotates and revokes the robot there too - without it, your robots stay yours and untouched. Create, rotate and revoke from the application's settings, with `ankra application registry robot get|ensure|rotate|revoke`, or on the API at `/api/v1/org/applications/{application_id}/registry-robot`. [Robot accounts →](/concepts/applications#robot-accounts) ## DigitalOcean GPU Droplets are now priced in the cloud cost view Fixed: a DigitalOcean cluster with a GPU Droplet worker (`gpu-h100x1-80gb`, `gpu-l40sx1-48gb`, and the rest of the `gpu-*` sizes) showed only its CPU nodes on the Cloud Cost card and in the cluster **Cost** view. The GPU size had no row in the price catalog, so the worker was priced at zero and dropped out of the compute total - the coverage counter was the only hint that the estimate understated the bill, while the create wizard, which reads DigitalOcean's live prices, showed the real figure. Every GPU Droplet size DigitalOcean sells - RTX 4000 Ada, RTX 6000 Ada, L40S, H100, H200, MI300X, MI325X, and the Spot GPU sizes - is now priced at its on-demand hourly list rate. GPU Droplets are billed per second with no monthly cap, so the hourly rate is used as published rather than fitted to a capped month. The correction lands on the next hourly metering pass; there is nothing to do. [Cloud cost →](/platform/cloud-cost) ## A zone in your own DNS account is now served from every cluster in the organisation A DNS zone you keep in your own provider account can be declared **once, for the whole organisation**, and every cluster - the ones running now and every one created afterwards - gets its own Ankra-managed `external-dns` for it. Until now the declaration was per cluster (`ankra cluster custom-dns-zones`), so a freshly created cluster came up with its Ingress hostnames on your domain dropped silently and its certificates waiting for DNS that nothing was publishing. `ankra org dns credentials create` stores your external-dns webhook credential (into the platform's secret store - it embeds the token, so no read surface returns it), and `ankra org custom-dns-zones add --zone --credential ` declares the zone; each cluster's controller is pinned to exactly that zone with its own record ownership, so it can never fight Ankra's own controller, another cluster's, or records you publish yourself. A cluster's own declaration of the same zone still wins on that cluster, `ankra cluster custom-dns-zones list` now says where each zone was declared, and withdrawing the organisation's declaration tears down only the controllers Ankra rendered. The same is available on the API at `/api/v1/org/custom-dns-zones`. [Serving a zone in your own DNS account from every cluster →](/concepts/applications#serving-a-zone-in-your-own-dns-account-from-every-cluster) ## Node groups now show their availability zones On an OVH cluster in a 3-AZ region, `ankra cluster node-group list` and `ankra cluster ovh node-group list` now end each line with `az=` and the zones the group's nodes were placed into, and the node-group listing API carries them as `availability_zones`. Until now the only way to see where a group's nodes sat was one `ovh nodes get` call per node, which is how two groups named for zones a and b came to sit entirely in zone c on a cluster built for multi-AZ without anything on the listing saying so. That placement was the balancer working as designed rather than a rendering fault: a group added without `--availability-zone` takes the zone with the fewest instances cluster-wide, so a one-node group lands wherever the cluster is thinnest. The guide now says so, the `--availability-zone` flag help says so, and the `create_node_group` audit action records the zone the request asked for, blank included, so whether a group was created pinned is answerable from the audit trail from now on. [Availability zones →](/guides/ovh-clusters#availability-zones) ## Top-level hostnames on your custom domain now publish through Ankra With a [custom Ankra domain](/concepts/applications#the-preview-url) registered, the `external-dns` Ankra installs on your clusters can now publish DNS records anywhere under the domain - top-level hostnames like `app.example.com` included - instead of only inside each cluster's `..` subzone. Existing clusters are re-credentialed automatically on the reconciler's next pass; there is nothing to do. On the shared `ankra.cc` domain the scope is unchanged: each cluster's external-dns writes only its own delegated subzone, because names directly under `ankra.cc` are shared platform namespace. ## Start and stop clusters from the cluster menu **Start cluster** and **Stop cluster** now sit in the cluster's **⋯** menu, on the cluster overview and on the clusters list — no more digging into **Settings → General → Danger Zone** to power a cloud cluster on or off. A running self-managed cloud cluster (Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE, HPE Morpheus) offers **Stop cluster**; a stopped one offers **Start cluster**, including the choice to restore everything or the control plane first. Both entries open the same confirmation dialogs as the Danger Zone — stopping still asks you to type the cluster name — and are disabled while another operation is running on the cluster. [Stopping and starting clusters →](/platform/cluster-settings#stop-cluster) ## Ask Ankra AI from a pull request comment Mention `@ankraai` in a comment on a pull request in a repository the Ankra GitHub App is installed on, or in a connected Bitbucket Cloud workspace, and Ankra AI answers on the pull request with the diff and the pull request's metadata as its context. Ask it to explain a change, to check a failing pipeline against the diff, or to review the pull request on demand. The mention is matched case-insensitively, anywhere in the comment, and is answered regardless of the draft, branch, and daily-budget rules that gate automatic reviews. On GitHub, Ankra reacts with 👀 the moment it picks the mention up, so you know it is working before the answer lands; Bitbucket Cloud has no comment reactions, so there the answer is the first sign. In an integration set to **Agent** mode the reply can go further: when the fix is clear, Ankra proposes it as ready-to-apply file changes and, on GitHub, offers to open it as a pull request. On Bitbucket Cloud, where Ankra cannot open one, it suggests a branch name and a commit message instead. **Ask** mode stays read-only. Mention replies sit next to the automatic **AI review** that runs on every opened or updated pull request, and **PR previews** for applications. All three are switched on per installation under **Organisation settings → Integrations**, with repository rules for the exceptions. [Ankra AI on pull requests →](/integrations/github#ankra-ai-on-pull-requests) ## A preview domain now tells you it needs DNS, and a demo stops claiming a URL that does not resolve Fixed: a demo published on your own **Preview domain** reported ready with an `https` URL that had never resolved, and nothing anywhere said why. Ankra publishes DNS only inside the subzones it delegates. The external-dns credential it provisions for a cluster is scoped to that cluster's own Ankra subzone, so a preview hostname on your domain is filtered out by the DNS provider before a record is written - and the sync goes on logging `All records are already up to date`, because from external-dns's point of view there is nothing in scope. Publishing those records was always yours to do, and that was written down nowhere. Saving the settings now says so when the domain answers nothing, naming the wildcard to create and the staging cluster's own ingress address to point it at. It also names the consequence for TLS, because the two preconditions were being checked independently and only one of them enforced: an HTTP-01 challenge is answered over the hostname being certified, so an unpublished preview domain costs you the certificate as well as the URL. A domain you have already pointed at the cluster says nothing, as before. A demo whose hostname does not resolve is no longer reported ready the moment its pods are up. It waits briefly for the name to answer, and is published anyway if it does not, so a domain nobody has pointed still gives you a demo rather than a failure - just not one that claims to be reachable. The docs now state outright that DNS for preview hostnames is the operator's responsibility when a preview domain is set, and what a working wildcard has to point at. ## Previews on your own domain now get certificates Fixed: setting a preview domain silently turned off TLS for demos, and there was no way to turn it back on. Demos published on a staging cluster's own Ankra subzone have always been issued a certificate per preview hostname. Setting your own **Preview domain** stopped that, on the assumption that an organisation bringing its own zone would bring its own wildcard certificate and name it in the **TLS secret** field. That assumption does not survive contact with a stock cluster: ACME issues wildcards only through DNS-01, the ClusterIssuer Ankra's networking stack installs is HTTP-01 only, and Ankra ships no DNS-01 solver for its own DNS provider. The field could not be filled, so demo ingresses carried no TLS and preview URLs quietly came back as `http://`. Nothing about your own base domain required a wildcard. Each demo's hostname is concrete when the ingress is written and resolves publicly, so the same per-host HTTP-01 challenge completes. Ankra now requests a certificate on both paths, under the same check it already ran: only when the staging cluster carries a verified ACME HTTP-01 ClusterIssuer, so a cluster without one stays on `http` rather than advertising TLS against a certificate nothing will issue. The new **Certificate issuer** field names a different ClusterIssuer, for a cluster whose issuer is not the one the networking stack installs. A **TLS secret** on its own still means you supply the certificate and Ankra requests nothing. Where previews would land on plain `http`, saving the settings now says so, in the portal and in `ankra org ai-environment`, instead of succeeding without comment. ## The preview settings are reachable from the CLI `ankra org ai-environment get|set` reads and writes the preview domain, ingress class, TLS secret and certificate issuer. These are the settings you script when standing up on-demand environments on your own domain, and until now `ankra org domain` covered only the organisation's root domain - a different setting on the same screen with a much wider blast radius. Only the flags you pass are written, and passing one with an empty value clears that field alone. `ankra org domain --help` now names the preview domain as the separate setting it is, and points to the right command for it. ## Removing a cluster domain now sticks Fixed: the documented procedure for switching an organisation's Ankra root domain could not be completed. Removing a cluster's domain worked - and then the domain came back, active and under the same name, about fifty seconds later. Since the switch is refused while any cluster domain still lives under the old root, every removal was undone before the next one could be made, and the switch was permanently out of reach. Two things put the domain back. Ankra treats being an organisation's staging cluster as a standing request for a domain, and it read "this cluster has no domain" as "this cluster needs one" - a removal and a cluster that never had one were indistinguishable. A removal is now recorded as a deliberate act and **held**: nothing re-creates the zone, including the `external-dns` Ankra runs on that cluster, until you ask for it back with `ankra cluster domain --enable`. A read reports `Opted out: yes` while the hold stands, so a domain that is gone no longer looks like one that is between passes. The second was a playground. A playground publishes a wildcard DNS record in the organisation's zone, and that record is reconciled rather than written once - so deleting it only lasted until the next pass. A refused switch now separates the records you can clear from the ones the platform re-asserts, names the writer of each, and names the playground environment to destroy instead. `ankra cluster playground destroy ` is new: the platform has served that call since playgrounds shipped, but the CLI could not reach it. The refusal also explains itself better on the cluster-domain side, and the docs now answer the two questions that decide whether the switch is safe on a domain you already use: * **Ankra confines itself to one subzone of the domain you register.** Registering `example.com` creates `.example.com` and nothing else. Records already published at the apex or under any other name are never read, written or deleted - not by the switch, and not by anything Ankra runs afterwards. * **A switch never re-labels a zone.** Labels come from the cluster and organisation ids, so a cluster keeps its label across a switch - and with it the `--txt-owner-id` of its `external-dns` and any GitOps path built from it. The TXT ownership registry stays matched to its records, which is what makes a `--policy=sync` controller safe across the change. [Switching the root domain →](/concepts/applications#the-preview-url) ## Approve or reject any ticket awaiting approval Fixed: a ticket an agent parked at **Awaiting approval** without a plan - asking for a decision rather than a plan review, such as "needs a capacity decision or an alert threshold raise" - offered nothing to decide it with. The Approve and Request-changes buttons lived inside the Plan panel, which only renders when a plan exists, and the review endpoint refused the verdict outright. Every ticket awaiting approval now carries a decision panel at the top of the page: **Approve** releases the ticket to **Executing**; **Reject** asks for a reason and sends the ticket back to **Planning**, where the agent reads the reason and re-plans. The same panel decides automation approval gates on the run (**Approve** / **Deny**) without a trip to the Inbox, and points at the **Needs your approval** card when the agent is parked on a risky action instead. ## Debugging a broken workload without handing out kubectl The CLI could read a cluster - fifteen resource kinds, pod logs, metrics, operation steps - but it could not answer the questions a broken workload actually raises. Why is this pod not ready? What did the container that just died print? Which container ate the memory? Each of those ended in a cluster access grant and kubectl through the gateway: broader standing access than the task needed, and outside the record Ankra keeps of everything else. Four commands close that gap, and none of them needs a grant. `ankra cluster logs --previous` reads the log of the container instance that terminated. For a pod in CrashLoopBackOff that is the only output with the failure in it, and it was the one log the CLI could not reach. `--previous` is always a bounded read, because the log is closed - it prints and exits rather than waiting on a stream that can never produce another line. `ankra cluster describe ` answers "why is this not ready?" in one call: the object's conditions, a pod's per-container state with the detail that explains it - CrashLoopBackOff and its exit code, ImagePullBackOff and its registry error, restart counts - and the events whose `involvedObject` is that resource. `ankra cluster events --for pod/my-pod` scopes an event listing to one object using a server-side field selector rather than filtering a namespace-wide list by name. That is the difference between "the pod is Pending" and "no node matches the nodeSelector". `ankra cluster top pods|nodes` shows live CPU and memory from the Kubernetes metrics API. `ankra cluster metrics` queries Prometheus, which is the right tool for trends but the wrong one for "which container just got OOMKilled" - and it needs Prometheus to have been installed. `top` reads metrics-server directly, so it works on any cluster that has one. `ankra cluster logs` also grew `-l ` and `--all-containers`, so a three-replica Deployment is one command instead of a pod listing followed by three invocations, and a stuck pod's init container is read alongside its app containers. Every one of these commands takes `-o json|yaml` and honours `--cluster`, so CI and AI agents parse them instead of scraping tables. [CLI reference →](/reference/cli/cluster) ## One choice that answers the rest of the form Some profile inputs only make sense together. Deploying a chat stack on a small GPU or a large one changes the model id, the context length, the memory ceiling and the size of the model store at once - and until now the form asked for each of those separately, so an operator had to know all four answers to change one decision. A profile author can now give an input **options**, and have each option **set** the others. Pick *Qwen3 32B on an L40S* and the model id, the context length, the GPU memory ceiling and the volume size move together; pick *Qwen3 8B on an L4* and they move back. The choice renders as a card of options above the rest of the form, every input an option answers shows the value it was given and which choice gave it, and typing over one marks it *customised* with a reset that hands it back to the option. A required input an option answers counts as answered. Nothing about this is specific to GPU sizing: a profile can declare any number of such inputs - model size, storage tier, ingress mode - each driving its own slice of the form. The platform resolves the choice server-side in one fixed order (a value you set, then what the option sets, then the input's own default), so `ankra stack-profiles apply --set model_size=32b` deploys the whole set from the CLI, the AI assistant and Terraform build the same stack from the same selection, and a deployment's recorded parameters are the values that actually deployed. Options are authored on the builder's Inputs tab, where the mistakes publish would refuse show up inline while you edit; `ankra stack-profiles get` lists what every choice sets before you bind anything. [Choices that set other inputs →](/concepts/stack-profiles#choices-that-set-other-inputs) ## Microsoft Teams catches up with Slack The Ankra bot has been in Teams for a while, but it could only talk. Every button Slack users had - acknowledging an alert, approving a remediation, confirming a change the AI proposed, picking up an escalated ticket - simply did not exist there, and two of the three commands the app advertised did nothing when you typed them. Teams now has all of it. Alert cards carry **Acknowledge** and **Snooze 1h**; AI remediation approvals carry **Approve** and **Deny**; a change the AI proposes arrives as a card only the person who asked can decide, and if the cluster moved underneath it the card re-renders with what changed before letting you confirm anyway. An escalated ticket's thread reply carries **Assign to me**, and **Approve plan** / **Request changes** while a plan is awaiting a verdict. A decided card is replaced by its outcome, so nobody acts on it twice. `help`, `status` and `logout` all answer. Every button runs as your own Ankra user through your linked account, so your role decides what it may do and the audit log records your name. Alert cards themselves are fixed too. A Teams channel destination with no custom template used to receive Slack's card format, which Teams cannot render - so a production alert arrived as a single line of raw text with no detail and no buttons. Teams destinations now get a proper Adaptive Card with the severity, the resource, the cluster and the actions. Ticket threads stopped rendering Slack's emphasis markers, which Teams reads as italics rather than bold. ## Connecting Teams is one click Connecting a Teams tenant took five steps: download a zip, sideload it, add the bot, read a claim code out of a chat message, and retype it into a browser. The bot's welcome card now carries a **Connect to Ankra** button. Press it and the portal opens with the claim code already filled in - press **Connect** and the tenant is bound. Pasting the code by hand still works exactly as it did. For an admin starting from the portal, the Microsoft Teams card is now a guided setup rather than a list of instructions. It leads with the path that works in every tenant - the app package - as three steps that track where you are: **Download package** (with a copy-link for handing the URL to a Teams administrator), the exact **Apps** → **Manage your apps** → **Upload an app** → **Upload to your org's app catalog** path with a callout for tenants where custom uploads are switched off, and the claim code. Downloading the package and confirming the upload advance the steps, done steps fold up and can be reopened, and **I already have a claim code** jumps straight to the end. Arriving from the bot's welcome card lands on the last step with the first two already ticked. Once a tenant is connected the guide sits behind **Add tenant**, matching Slack's **Add workspace**. [Connecting Microsoft Teams →](/platform/ai-connections#connecting-microsoft-teams) ## Routing rules you can read before you trust them An alert firing can reach a destination two ways at once: the alert's own **Notifications** → **Destinations** list, and a routing rule whose Kind covers alert firings. Ankra has always delivered that once, but nothing said so, and the safe-looking move was to detach the destination from every alert - which quietly turns off the delivery you were relying on. It is now documented as a guarantee: **a notification is delivered at most once per destination**, routing rules are netted against the alert's own destinations, and no rule - include or exclude, at any priority - can suppress an alert's own destinations. Kind is no longer one value. A rule can name a list of kinds, or invert it: "Operation failed and Reconciliation failing, nothing else" is one rule instead of two, and "everything except Alert firing, because alerts already have their own destinations" is one rule instead of ten. Existing single-kind rules and API clients on the old `kind` field keep working unchanged. And rather than reasoning it out, you can ask. **Preview** on the Routing rules page - and `ankra alerts routes preview` - resolves a hypothetical notification against your rules and lists the destinations it would reach, the ones it would not, and the reason for each, including which rules a **Stop on match** skipped and which deliveries de-duplication dropped. It is a dry run: nothing is sent. [Notification routing →](/guides/notification-routing) · [Alerts →](/guides/alerts) ## Notification cards fill in, whatever kind they are about A destination stores one payload template, and that template renders every notification the destination receives. Only alert firings ever filled it in. An operation failure, an agent update, a reconcile failure or an AI insight routed to the same Teams or Slack channel arrived as a title above a column of blank rows - Resource, Type, Job, Cluster and Condition all empty - even when the notification's own title named the failing resource. Every notification kind now resolves the same variables. `{{resource_name}}`, `{{resource_type}}`, `{{job_name}}`, `{{namespace}}` and `{{condition_type}}` are read from what the notification actually recorded, and `{{cluster_name}}` is resolved for every kind rather than for the five that happened to store it. An alert firing that reaches a destination through a routing rule keeps its condition, its job and a button that opens the firing itself. Where a kind genuinely has no value for a row - a cluster-level alert has no job, an agent update has no condition - **the row is dropped instead of shipping empty**, so the built-in Slack, Teams, Discord and PagerDuty presets are correct for every kind with no editing. If you would rather show something, `{{resource_name|not applicable}}` supplies your own fallback. The **Test** button now sends the same sample through the same renderer a real delivery uses, with every variable populated - so a template that renders under Test renders on delivery. Testing a routing rule sends the sample as that rule's kind. [Template variables →](/guides/webhooks#template-variables) · [Empty values and fallbacks →](/guides/webhooks#empty-values-and-fallbacks) ## Demo and preview links are now https Branch and pull-request demos on a cluster's delegated `ankra.cc` subdomain used to serve plain http, because nothing issued a certificate for that zone. Now, when the staging cluster runs the Ankra networking stack, every demo's ingress requests a Let's Encrypt certificate automatically - so the preview link you share is https, with a certificate browsers trust. This applies across every surface that deploys demos: the portal, the CLI, the AI assistant, and automatic pull-request previews. [Preview demos →](/concepts/applications#preview-demos) ## Public DNS is visible in the UpCloud create wizard Every Ankra-provisioned cluster can come up with its own delegated subdomain on `ankra.cc`, with external-dns installed and wired to it - no domain to buy, no DNS provider to configure. The UpCloud create wizard now surfaces this as an **Include Public DNS** checkbox (on by default) beside the networking stack option, so you can see what you are getting and opt out if you bring your own DNS. [UpCloud clusters →](/guides/upcloud-clusters) ## Clusters that power themselves back on When something outside the platform stops every server of a cluster - a cloud account running out of credit, a project-wide power action in the provider's console, a facility outage - Ankra noticed within a minute and marked the cluster offline, and then waited for you to press start. The jump host already recovered itself; the nodes doing the actual work did not. Clusters on Hetzner, OVH, UpCloud, DigitalOcean, Scaleway, and Proxmox now recover on their own. Once a cluster has been offline for five minutes, the platform asks the provider to power every stopped server back on - and only the stopped ones, so a sweep can never reboot a node that is still running. A cluster you stopped deliberately, or one mid-create or mid-teardown, is never touched: recovery only acts on clusters the platform believes should be running. Recovery is bounded the way you would want it to be. Each outage gets up to three power-on sweeps, spaced out over the first forty minutes; a cluster still dark after that - typically an account that is genuinely out of credit - is left for you, with the outage recorded so you can see exactly what was tried and when. A cluster that keeps losing power repeatedly in one day is also handed back to you, because restarting it clearly is not the fix. ## Metrics and log sources you can write in Git A cluster's `spec.prometheus_metrics` and `spec.log_source` blocks used to be write-only from the platform's side: Ankra rendered them into the cluster document, but never read them back. Hand-adding either block in Git connected nothing, and because the document is regenerated wholesale from what the platform knows, the block you wrote could later be regenerated away. Both members now round-trip. Write `spec.prometheus_metrics` or `spec.log_source` into `clusters/-/.yaml` and the next sync connects the source, exactly as the settings page would; edit a field and the next sync updates it. The accepted fields are the ones the platform stores - `endpoint`, `credential_name`, `flavor` and `verify_ssl` for metrics; `provider`, `endpoint`, `credential_name`, `tenant_id`, `index_pattern`, `field_mappings` and `verify_ssl` for logs - and anything else is refused with an error naming the key, rather than quietly dropped. Credentials stay out of the repository. A `username`, `password`, `token` or `auth_scheme` written inside a source block is refused by name, because ignoring it would leave a live secret in your Git history while you believed it was in effect. Create the credential in the platform and reference it by `credential_name`; a name that matches no credential on the cluster fails the sync instead of connecting a source that queries unauthenticated. One asymmetry is deliberate: Git can create and update a source, never disconnect one. Deleting the block does not disconnect anything - the source survives and the block is re-rendered on the next push. Disconnecting stays a platform action, in **Settings** → **Data Sources**, because inferring a destructive intent from an absent key is exactly what deleted hand-written blocks before. [ImportCluster schema →](/reference/importcluster#observability-sources-in-git) ## Add-ons that say who can see them The add-on catalog mixed everything into one wall of identical cards: charts from Ankra's global catalog, charts from registries your organisation connected, and charts that pull with a credential all looked the same. The only way to tell them apart was leaving the page to check the repository list's **Global** column. Every card now carries the answer. A **Public** badge marks an add-on from Ankra's global catalog, available to every organisation; **Organisation** marks one from a registry your organisation connected, visible to everyone in it; and a **Private** lock marks a chart whose registry pulls with a credential your organisation added - without ever naming the credential. A curated add-on that mixes public and credential-backed charts says so ("Partially private") instead of overstating either. The same badges follow the add-on onto its detail page, chart by chart. [Add-ons →](/concepts/addons) ## The stack profile deploy form asks only for what it needs Using a profile with a lot of parameters meant scrolling a long, flat list to find the few inputs that were actually blank. **Use Profile** now separates the inputs a profile left for you to fill from the ones it already answered, and folds the answered ones behind a single **Show N more inputs that already have defaults**. A profile shipping twenty-four parameters with five blanks opens showing five fields. A counter tracks how many required inputs are still empty and turns green once nothing is outstanding. Profiles with many parameters gain a search box that matches on name, description, and the addon or manifest an input belongs to. Every field now names the addon or manifest it writes into, so a profile that declares the same key on two different components - two `api-key` secrets for two different services, say - no longer shows two identical labels. Secret inputs gained the controls they were missing: **Generate** produces a strong random value for the keys nobody is meant to invent by hand, alongside a reveal toggle and a copy button for the cases where the same value belongs in more than one field. Any input changed away from its default offers a one-click **Reset**. Hostname inputs fill themselves in. A cluster's public domain is a generated slug rather than its name, so a profile asking for `chat_host` used to send you off to find it - gpu-chat's description literally told you to ask the AI. Pick a target cluster and those inputs now offer **Use cluster domain**, which writes the hostname the deploy will actually serve on. Switching cluster re-offers the button rather than rewriting what you typed, so a hostname still pointing at the previous cluster is visible instead of silent. The dialog names the profile and version it is about to deploy, pairs the target cluster and stack name on one row, and lets you search clusters by name with each cluster's state shown beside it. Stack names are checked against the naming rules as you type, and the footer says what is still standing between you and a deploy. [Stack Profiles →](/concepts/stack-profiles) ## Cluster wizards that start on kubeadm and Cilium Every self-managed create wizard - Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE and Morpheus - opened its Kubernetes step on K3s, so a cluster created with the defaults got the lightweight distribution and Flannel unless you noticed the dropdown. Vanilla upstream Kubernetes was one option down, and Cilium with it. The wizards now preselect **kubeadm** - upstream Kubernetes bootstrapped with kubeadm and containerd, Cilium (eBPF networking, L7 policies, Hubble observability) as the CNI, and a stacked etcd - and list it first. K3s is still one click away and, when you pick it, the CNI selector behaves exactly as before. The CLI `--distribution` flag and the API keep `k3s` as their default when nothing is passed, so scripted creates are unchanged. [Kubernetes distribution →](/guides/operate-ankra-managed-clusters#choices-fixed-at-create-time) ## See every place a stack profile is deployed A stack profile could tell you *that* deployments were behind - the update counter has been there for a while - but not *where*. The list of clusters running a profile lived in a small panel buried on the overview, with no links, no state, and no way in for a profile that had never been used. Profiles now carry a **Deployments** tab: every stack your organisation created from the profile, with the cluster and stack as links (a never-committed draft links to the draft so you can finish it), the stack's live state, the profile version it runs, the non-secret inputs it was instantiated with, and when it was deployed. Rows running an older version keep the one-click **Update** that pre-fills the update draft with last time's values, and the overview's stat card now summarises the fleet - how many deployments, on how many clusters, and whether anything is behind - and jumps straight to the tab. [See where a profile is deployed →](/concepts/stack-profiles#see-where-a-profile-is-deployed) ## A runnable path to your own Ankra domain Registering your own domain for Ankra's generated hostnames worked; moving an existing organisation onto it did not. The switch is refused while cluster domains or DNS records still live under the old root - correct, and fail-closed - but the refusal was a single sentence with no list, the portal replaced it with a generic "the request was rejected by the server", and nothing enumerated the cluster domains that were blocking it. Worse, the one command that could tell you a cluster's domain, `ankra cluster domain `, was a create: checking a cluster added a blocker to the very migration you were running. The refusal now names its blockers. The portal lists every cluster domain and DNS record still under the old root beside the **Custom Ankra domain** field, with a link to remove them; the API returns the same list as structured `blocking_cluster_zones` and `blocking_dns_records` members; and `ankra org domain set` prints it along with the command that clears each kind. `ankra cluster domain ` is now a read. It reports the cluster's domain, or state `none` when it has none, and creates nothing - enabling a domain is the explicit `--enable`. `ankra org dns zones` lists every cluster domain in the organisation with its state, so you can see what a switch has to clear before attempting one. And the organisation's root domain itself is finally scriptable: `ankra org domain get|set` reads and writes the same setting the portal saves, with `set --default` returning you to `ankra.cc`. [The preview URL →](/concepts/applications#the-preview-url) ## Your own domain for Ankra previews Every public hostname Ankra mints - your organisation's delegated DNS zone, each cluster's subzone under it, and the demo and PR preview URLs built on top - hung off a single platform apex: `ankra.cc`. Serving previews under anything else meant bringing your own domain, wildcard DNS, and TLS secret as a demo base domain. Organisation admins can now register **their own domain** for all of it, dedicated to their organisation alone. Point the domain's NS records at Ankra's nameservers (the ones serving `ankra.cc`), enter it in the **Custom Ankra domain** field under **AI** → **Settings** → **Workspaces**, and every zone and preview URL minted from then on hangs off it - Ankra manages the DNS the same way it does on the platform domain. The save verifies the nameservers up front and refuses a domain another organisation already holds, so a typo or an unhosted domain can never tear down a working zone. Zones you already have keep the domain they were provisioned under; after a switch, Ankra tears down the organisation zone and re-creates it under the new domain automatically. The switch fails closed on purpose: while cluster DNS zones or manual DNS records still live under the old domain, the save is refused with a message naming exactly what to remove first - nothing is torn down behind your back. To make that removal possible, clusters can now opt **out** of their generated domain as well as in: `ankra cluster domain --remove` (or `DELETE /api/v1/clusters/{cluster_id}/dns-zone`) hands the zone back for teardown, and the plain command re-enables it under the organisation's current domain. [The preview URL →](/concepts/applications#the-preview-url) ## Teams channels you pick like Slack, and alert routing you can script Slack has had it for a while: install the app, pick a channel, done. A Teams destination still meant leaving Ankra for Power Automate, building a flow, and pasting a signed URL back. The bot was right there in the AI gateway, answering chat and posting notifications - it just was not wired to alerts. Now it is. Once the Ankra AI bot is in a team, the destination form swaps the URL field for a **channel picker** listing every team the bot has been added to, and the bot posts the Adaptive Card itself - no flow, no URL, and the same picker on edit. Add the bot to a team and its channels appear; remove it and they leave. The other half of the same complaint was that alert routing lived only in the browser: no CLI, no API token access, nothing you could keep in Git. Destinations and notification routes now answer to a personal access token, and the CLI grows **`ankra alerts destinations`** (`list`, `get`, `create`, `update`, `delete`, `test`, `test-url`, `channels`) and **`ankra alerts routes`** (`list`, `create`, `update`, `delete`, `test`), all honouring `-o json` and `-o yaml`. Updates send only the flags you pass, so a route you touch keeps everything else as it was. [Connecting Microsoft Teams →](/platform/ai-connections#connecting-microsoft-teams) · [Managing destinations from the CLI →](/guides/webhooks#managing-destinations-and-routes-from-the-cli) ## A Microsoft Teams bot you can install, and Teams alert cards that keep their buttons The Microsoft Teams connection card asked for a claim code "the bot shows when it is first added" - but offered no way to add the bot. Ankra AI is not in the Teams app store, the card carried no app package, and the docs said only "add the Ankra bot". The connect flow dead-ended on its first step. The card now walks through the whole path: **Download the Teams app package** hands you the app manifest and icons as a `.zip`, built on demand with the deployment's own bot identity, ready to upload in Teams (**Apps** → **Manage your apps** → **Upload an app**) or for a Teams admin to publish to your organisation's catalog. Add **Ankra AI** to a team, paste the claim code it posts, and the tenant is bound. The webhook path into Teams got the same treatment. The built-in Teams payload template was a legacy **MessageCard** - a format Microsoft retired with O365 Connectors. Delivered through a Power Automate (Workflows) webhook, the flow reports success while Teams silently drops every button, so cards arrived with no **View Alert**, no **Acknowledge**, and a broken logo from a host that no longer resolves. The preset is now a severity-tinted **Adaptive Card** whose buttons actually render, alongside a new `{{severity_style}}` variable (Adaptive Cards cannot consume a hex colour), and pasting a `logic.azure.com` Workflows URL now auto-selects the Teams preset. Three quieter fixes ride along: `{{severity_color}}` is substituted on real deliveries (it previously resolved only for the **Test** button, so production cards shipped the literal placeholder), an alert with an empty name falls back to its rule name instead of rendering a bare "Alert:", and test deliveries no longer strip the `attachments` envelope that Teams cards live in. The template-variable list also stopped advertising a one-click snooze link that never existed. [Connecting Microsoft Teams →](/platform/ai-connections#connecting-microsoft-teams) ## A force mode that reclaims leaked cloud volumes and load balancers Stopping or terminating a cloud cluster released its servers, network, and gateway, but never the storage volumes its workloads had provisioned through the CSI driver, and a load balancer could survive teardown too. The disks sat detached in the account and kept billing: on one of our own UpCloud accounts, 76 orphaned volumes were 83% of the monthly spend before anyone noticed. OVH and DigitalOcean leaked the same way, and Hetzner had the opposite problem: it always deleted the volumes, so a stopped cluster restarted with empty storage. Stop and deprovision now take a force mode on every affected provider. `ankra cluster stop --force` and `ankra cluster deprovision --force` (or `?force=true` on the API) delete the cluster's CSI volumes and load balancers along with the rest of its infrastructure, on UpCloud, Hetzner, OVH, and DigitalOcean; on Scaleway, `--force` overrides a `retain` retention policy. Ankra records which volume IDs belong to the cluster before tearing anything down, so the sweep deletes exactly those disks and nothing else in the account, and a cluster that was stopped earlier without force can still be cleaned up by a forced terminate afterwards. Without the flag a plain stop keeps your data so the cluster can come back, and a plain deprovision leaves the volumes for you to reclaim later. On Hetzner that is a deliberate change: it used to delete the volumes on every stop. A forced stop destroys the data on those volumes, and the CLI says so before it asks you to confirm. [UpCloud clusters →](/guides/upcloud-clusters#stopping-and-starting-a-cluster) ## AI settings on every application Which Ankra AI lanes ran on a repository was an organisation-wide decision, taken in a grid of repository rules on the Integrations page. Silencing the pull request review on one noisy repository, or pointing one repository's review at a larger model, meant leaving the application you were looking at and finding its repository among every other repository the Ankra GitHub App can see. Every application now has an **Ankra AI** panel under **Settings**, with one card per lane Ankra AI can run on its repository: the pull request review, a branch code review, an Actions review, the organisation skills review, and the demo URL that comments a live preview on every pull request. Each card carries the lane's own model, branch scope, ignored paths, what it does when it finds something, and a daily run and input budget - and is labelled **Inherited** or **Overridden**, so it is always clear whether you are reading your organisation's default or this repository's own decision. Overriding is per lane and reversible. A lane you do not touch is not written at all, so an organisation admin goes on steering every application that has not opted out; **Reset to organisation default** returns one lane, and **Follow organisation for everything** drops the application's override completely. Nothing here is a second store: every write lands in the same per-repository rows the AI gateway already resolves when it gates an event, so the panel and the Integrations page cannot drift apart. Two lanes are visible but not running yet - the branch **Code review** and the **Actions review** cards describe what they will do with their switches disabled, rather than offering a toggle that silently does nothing. On the pull request review, **Propose a fix** is likewise stored and not acted on yet, and the card says so. One limit worth knowing: the settings are keyed by repository, so two applications connected to the same repository share one set of lanes. [Application AI settings →](/platform/application-ai) ## Demo routing you can declare, and a registry you can change your mind about A monorepo demo deploys every component, picks the frontend-shaped one to own the demo URL, and publishes the API-shaped one under `/api`. Good guesses - but only guesses, and the lanes with no human in the loop had no way to correct them: an automatic pull-request preview and an AI-launched demo both take the heuristic or nothing. An application whose component names read differently got a preview URL serving a UI whose API calls all 404'd, with a working API sitting unrouted in the same namespace. Demo routing is now something you declare once. Name the component that owns `/` and the path prefixes the others are published under, and every lane follows it - the launch dialog, the CLI, the automatic PR previews, and the AI tools alike. A declaration is authoritative, so the `/api` guess stops running and a component you leave out stays reachable in-cluster only instead of picking up a path it never asked for. Per-launch overrides still win, and clearing the declaration returns everything to the heuristics. The AI's `deploy_pr_demo` also stopped under-reporting what it did: its answer now names the entry component and lists every workload it provisioned with that workload's own image, container port and published path, rather than a single image describing whichever component happened to own the URL. Alongside it, **the image registry an application publishes to is no longer fixed at create time**. An organisation that runs its own registry could declare it when onboarding an application and never again - so an application already connected to Ankra kept being reported as never built while it published healthily to a registry Ankra simply could not see. Settings now carries an **Image registry** panel, and the CLI grows `ankra application registry get`, `set` and `clear`. All three report the host and project the declaration resolves to and the image repository each component is expected to publish to, so you can check the coordinates against where your builds actually push before wondering why readiness disagrees. Finally, a demo migration command now runs with your image's working directory on `PYTHONPATH`. Python resolves imports against the *script's* directory rather than the working directory, so a migration entrypoint that is a script - `python scripts/bootstrap_database.py` - could not import the application package sitting beside it and died at import. [Preview demos →](/concepts/applications#preview-demos) ## An UpCloud create wizard that can find the right plan Since the Ankra-managed UpCloud wizard started reading UpCloud's live catalogue, its plan pickers rendered all 174 plans as flat walls of tiles - three of them on one page, one each for the bastion, the control plane, and the workers - with no search, no filters, ordering that put `12xCPU` before `1xCPU`, and roughly forty discontinued series mixed in as if they were current. The 15 datacenter zones stacked as another full page of radio rows. Each picker is now the same plan browser the other provider wizards use. It opens on a one-line summary of the plan already selected; below it sit a search that spans every category, family tabs (Starter, Premium, Cloud Native, GPU, GPU Spot), vCPU quick filters, and a sortable table for the long tail. Discontinued series live behind a **Current plans / Legacy** toggle instead of dominating the page, and plans order smallest-first. Zones are grouped by region - Europe, North America, Asia-Pacific - as compact cards, and Helsinki #2 carries a badge for being the only zone stocking GPU plans today. Pick a GPU plan while another zone is selected and the wizard warns that the create is likely to fail at server creation, instead of letting you find out ten minutes in. The cost estimate also stopped pricing plans it has no published figure for at \$0 - it shows a dash and notes the total covers priced items only. ## An AI chat that acts on your defaults Agent-mode chat used to interrogate you: which credential, which zone, what name, which sizing, in what order - round after round of multiple-choice before any work happened, for choices that all had sensible defaults. Agent mode now acts on those defaults and states each assumption in one line ("Using namespace `monitoring` - say the word to change it"), reserving questions for decisions that are genuinely yours: destructive changes, real money, or an ambiguous goal. When it does ask, it asks once - every needed decision batched into a single question block. The same overhaul grounds the answers better. AI Insights the assistant cites now carry a freshness marker, so a diagnosis no scan has confirmed recently gets re-verified against live state instead of being repeated as fact. Chart-configuration lookups always return readable, navigable data - when a chart's options run long, the assistant gets a trimmed view with named paths it can drill into, never cut-off text. And the assistant can look up your organisation's preview domain from any conversation, so the public URL it proposes for a new service is on a domain that will actually resolve. [AI Assistant →](/platform/ai-assistant) ## Additive deploys that can run themselves With the chat's autonomy setting on **Auto**, a stack deploy whose change set removes nothing - new add-ons, new manifests, configuration changes to what is already there - now executes immediately with a receipt carrying its full bill of materials, instead of stopping at a confirmation card. The bill of materials is generated and validated before anything runs; if validation fails, or the same deploy would remove or replace a deployed add-on or manifest, the confirmation card comes back exactly as before. Cluster create, scale, upgrade, stop, and delete are pinned to always ask, whatever the setting - cost stays a human decision - and writes into protected system namespaces such as `kube-system` remain refused outright. Autonomy is off unless you turn it on. Every conversation starts on **Manual** - every write confirmed, exactly as before - the setting is per conversation, and it appears once AI autonomy is enabled for your organisation. It is also not the **Auto** in the model picker, which only routes between models. Every auto-approved action lands in the conversation as a receipt and in the audit log with its autonomy attribution, so what ran, and why it was allowed to, is always reconstructable. > [!WARNING] > > On **Auto**, a validated additive stack deploy applies to your cluster without a human click in between. Switch it on for conversations where you want that speed; stay on Manual where every change should be gated. [AI Assistant →](/platform/ai-assistant#autonomy-manual-or-auto) ## Chats that follow the cluster they created Ask the AI to build a cluster and the conversation now follows it: when the create you approved goes through, a conversation that wasn't already scoped to a cluster is scoped to the new one - so once provisioning finishes, designing stacks, deploying workloads, and reading live state carry on right there, with no more "no cluster selected" from the conversation that did the selecting. Long provisioning runs also stopped crying wolf: the live tracker now gives a cluster build the time it actually takes - 40 minutes, up from 15 - and when it does hand off, it says the build is still in progress rather than calling it a failure. And a failed action no longer strands the conversation mid-flight: the portal now waits for the session to finish its turn before delivering the follow-up that explains what happened, instead of dropping it. [AI Assistant →](/platform/ai-assistant) ## Drafts that are never replaced in silence Designing a new stack while another draft was in progress used to overwrite the previous draft without a word - finished work, gone. A replaced draft is now retired with a full audit trail instead of silently overwritten, and the chat tells you exactly what happened and which draft it replaced, with guidance to deploy or discard a finished draft before starting the next one. Namespaces behave too: the namespace you ask for is the namespace you get, per-chart overrides are supported, and moving a deployed add-on between namespaces from chat now works - the AI can supply the migration strategy the engine requires instead of dead-ending. [AI Assistant →](/platform/ai-assistant) ## Stack profiles you can run before you deploy A stack profile could only be tried by instantiating it onto a real cluster. Reviewing a new version, checking a public profile before adopting it, or showing a colleague what a stack actually deploys all meant putting it somewhere real first. A profile now has a **Demos** tab of its own. Launch one and Ankra installs the profile's add-ons and manifests into a throwaway, quota-bounded namespace on your organisation's staging cluster - the same lane branch demos have always used - on a timer that deletes it when it runs out. The details view reads the namespace live: how far the stack got, every object it installed with its manifest, the namespace's Kubernetes events, and a log tail from any pod. A chart that fails to install reports its own reason ("no matching PVC storage class"), not a generic failure. A profile demo deploys no application of its own, so there is no image or routing step to sit and wait on - the stack finishing is what makes it ready. One demo runs per profile: launching again replaces it rather than stacking a second copy on the staging cluster, so relaunch is how you pick up a new version or changed parameters. The two demo lists are now tied together. An application whose demo settings designate a stack profile shows that profile in its **Demos** tab, along with the profile's own live demos and a link straight into each - the stack is often already running, and launching a whole application demo just to look at it wastes staging capacity. The profile's tab lists the applications designating it, from the other side. Everything is available to the CLI too, under `/api/v1/org/stack-profiles/{profile_id}/demos`. ## Availability zones for OVH clusters An OVH cluster in a 3-AZ region such as `EU-WEST-PAR` put every node in the same availability zone, control plane included, and there was no way to ask for anything else. Nothing in the create path could carry a zone, so OVH chose one and used it for the whole cluster. You can now spread a cluster across the zones of a 3-AZ region, and pin a node group to one zone: ```bash theme={null} ankra cluster ovh regions --credential-id --with-zones ankra cluster ovh create --name my-cluster --region EU-WEST-PAR \ --availability-zones eu-west-par-a,eu-west-par-b,eu-west-par-c \ --control-plane-count 3 ankra cluster ovh node-group add --name database-par-a \ --instance-type r3-128 --availability-zone eu-west-par-a ``` Control planes and etcd spread per role, workers per node group, and every day-2 path - node group add, node group scale, worker scale, control plane growth - balances new instances around where the existing ones already are. Spreading needs at least 3 control planes, because fewer cannot hold etcd quorum through the loss of a zone; the create is refused rather than promising fault tolerance it cannot deliver. Every OVH node now also carries the standard `topology.kubernetes.io/zone` and `/region` labels, taken from the zone OVH reports for the live instance rather than the one that was requested. That makes them correct on clusters created before any of this existed too - Ankra records the zone the next time it reads the instance, and `ankra cluster reconcile ` applies the labels without waiting for a change to trigger a sync. > [!WARNING] > > OVH High Speed block storage is zonal: a volume is replicated within one zone and cannot attach from another. A single database pod with one PersistentVolume is not zone-fault-tolerant however the nodes are spread. Zone tolerance needs replication at the application layer, one replica and one volume per zone. The default StorageClass on OVH clusters is now `csi-cinder-sc-topology`, which binds `WaitForFirstConsumer` so a volume is not bound before its pod is scheduled. > > Placing that volume in the pod's own zone is a separate mechanism, and it is not live: the Cinder CSI provisioner's Topology gate is off on OVH, because OVH Block Storage rejects the compute zone names the gate forwards. Volumes are created in Cinder's default zone with no zone affinity on the PersistentVolume, on every class. Keep each stateful replica on a node group pinned to one zone until that changes - see [zone tolerance needs more than node spread](/guides/ovh-clusters#zone-tolerance-needs-more-than-node-spread). New OVH clusters also stop installing k3s's bundled local-path provisioner, which marked itself the default StorageClass alongside the cinder one. Two defaults left the class an unqualified PersistentVolumeClaim resolves to undefined, and resolving to local-path put the volume on a single node's disk with no replication. Existing clusters keep local-path untouched, since removing a StorageClass would orphan any volume already bound to it - name the class you want explicitly there rather than relying on the default. [Availability zones →](/guides/ovh-clusters#availability-zones) ## A publish flow that explains itself Publishing to a catalog was three different screens depending on what you were publishing, and each of them failed late. Publishing an application was four unlabelled text boxes; if the version was already taken, or the application was not publishable at all, you found out after submitting. Every publish flow is now the same shape: check, review, confirm. Before anything is submitted you see what will be published, where it lands, and - the useful part - **why it cannot be published, if it cannot**. Publishing an application now starts from Ankra's readiness check, which has always existed but never had anywhere to appear: a missing image registry declaration is reported up front, with a **Fix automatically** button where Ankra can resolve it for you. A version that is already published is refused inline, before anything is snapshotted, instead of coming back as a conflict after submitting. [Publishing applications as add-ons →](/platform/application-addons) · [Publishing add-ons →](/platform/addon-publishing) ## Version history for published applications A published application listed its versions as a flat row of numbers. It now has a history: each version carries a **changelog** you write at publish time, who published it, and a **Compare** against the version before it. Comparing shows which manifest files were added, removed or modified with their contents side by side, and - separately - which **deploy options** changed. That distinction matters: options are the add-on's install-time contract, so changing one is a breaking change for anyone who already installed it. A version that starts or stops referencing an environment `Secret` is called out for the same reason. Existing versions keep working unchanged; those published before changelogs simply have none. [Version history →](/platform/application-addons#version-history) ## Catalog publishing now checks who is asking Proposing an add-on to the global Ankra Catalog puts your organisation's name on a pull request against a public repository, and once merged the entry is visible to every organisation. It now requires the **Manage Helm registries** permission, held by organisation admins and owners. Members and operators can still open the flow and review exactly what would be proposed - they just cannot submit it, and the flow says so on the first step rather than failing at the end. Ankra also screens the rendered manifest before it can be proposed, refusing credentials embedded in a URL and recognisable provider tokens, naming the field so it can be corrected. A pull request is effectively permanent: a credential that reaches a public repository is in its history even if the branch is later closed. [Publishing add-ons →](/platform/addon-publishing#who-can-publish) ## Applications can publish to a registry you already run An Application's container images went to your organisation's private Ankra registry, and there was no way to say otherwise. If you already operate a registry - often the same Harbor whose OCI charts Ankra indexes for you - your images existed, they were simply not where Ankra looked. Publish readiness reported "no image has been pushed yet" indefinitely while the application ran healthily from the registry it actually uses, and the deploy gate stayed shut behind an artifact that was never going to appear. An Application can now declare the registry it publishes to, pointing at a registry credential Ankra already holds: ```json theme={null} "image_registry": { "url": "oci://artifact.example.com/commerce-images", "credential_name": "example-harbor-pull", "pull_secret_name": "harbor-registry", "username_secret_name": "HARBOR_USERNAME", "password_secret_name": "HARBOR_PASSWORD" } ``` Everything downstream follows it: the generated workflow pushes there and logs in with the secret names you gave, publish readiness verifies the tag **in that registry** with the credential you named, the deploy gate and image listings probe it, container scanning reads its tags, generated Deployments reference the pull Secret you named, and Ankra creates that Secret in the deploy namespace from the same credential. One credential serves chart pull, image pull, and readiness. On a monorepo, a component may declare its own registry - apps that live in different projects with their own robots and retention rules are addressed individually. **Ankra does not mint robots for a registry you operate.** Setup names the two Actions secrets the workflow reads and leaves them to you, rather than writing a credential Ankra happens to hold over a push robot you administer. Readiness reports whether they exist; `manage_actions_secrets: true` opts back in. [Using your own registry →](/concepts/applications#using-your-own-registry) ## Pull request builds no longer publish images The generated workflow ran on `pull_request` with `push: true`, so a build from unmerged code published an image - into a registry whose retention and tag-immutability rules are governed elsewhere, tagged as though it were a real build. Pull request runs now build and scan the source without pushing. The image scan follows the push, so it runs on branch builds, where there is an artifact to pull back and scan. ## GitOps status now shows the repository it actually read `ankra cluster gitops status` and the cluster's GitOps settings reported the repository Ankra has **stored** for a cluster. A sync now records the repository, branch and provider it actually read, and the status answer carries it as `observed_source` alongside a `source_mismatch` flag. If the two ever disagree, you can see it rather than having to infer it by comparing commits across repositories. Underneath, the cause of that disagreement is gone. A cluster could end up carrying more than one GitOps repository record, and different parts of the platform picked different ones - so a cluster could sync from one repository while reporting another. Applying a cluster definition now leaves exactly one active record, and the sync loop and the status view resolve it the same way by construction. Repointing a cluster through `spec.git_repository` works as documented, including onto a **non-default branch**: one repository with a branch per environment is a supported layout. > [!WARNING] > > Repointing prunes what the new source does not define, PersistentVolumeClaims included. Diff the two sources over the cluster's directory before you apply. [GitOps →](/concepts/gitops) · [ImportCluster reference →](/reference/importcluster) ## A credential check that counts every cluster Before replacing or deleting a GitHub credential, `get_github_credential_usages` tells you what depends on it. It was only counting **imported** clusters, so any cluster Ankra provisioned - on DigitalOcean, Hetzner, OVH, UpCloud, GKE, EKS, AKS and the rest - was missing from the answer. Acting on that count, you could revoke a credential that a live GitOps sync was riding. Every cluster is now counted, whoever provisioned it. The response also separates `usage_count` (clusters) from `application_count` (Applications built from the credential's repositories) and adds `dependent_count` for the total, and a credential name shared by more than one credential now returns the full superset instead of an empty answer. ## Repository pickers that follow the credential you chose In a cluster's **Settings → GitOps**, changing the credential left the repository selection pointing at the previous credential's repository, and **Sync Repositories** refreshed the credential you were replacing rather than the one you had just picked. Moving a cluster to a repository under a different GitHub organisation could not be completed. Choosing a different credential now clears the repository so you pick one the new credential can actually see, and syncing follows your current selection. ## Prerelease chart versions now validate `ankra cluster validate` could reject a `chart_version` that was deployed and running - typically a CI-published prerelease such as `0.1.6-ci-ankra-pipeline`. Prereleases were never excluded from indexing. The real cause was tag pagination: Ankra stopped reading an OCI repository's tags after the first page unless the registry sent a `Link: rel="next"` header, which not every registry does. Charts with many tags therefore lost their newest versions from the index, and a pin `validate` was asked about could never resolve. Ankra now pages through every tag, and reports a truncated listing instead of indexing a partial one. The error also stops pointing at the wrong thing: it now says the version is not **indexed**, and suggests re-syncing the registry. ## A playground for every organisation The **playground** - a real, writable Kubernetes environment Ankra provisions for you - is now available to every organisation. There is nothing to enable and nothing to install: click **Start playground** in the create-cluster dialog, and a couple of minutes later you have a cluster with the Ankra agent already connected. It is a genuine cluster, not a simulation. Each playground is a [vcluster](https://www.vcluster.com/) with its own API server, isolated per organisation, running on Ankra's infrastructure. Deploy stacks through the normal pipeline, browse live resources, stream logs, open a terminal, and ask the AI to build things - all before you connect a cluster of your own. One per organisation, expiring after a few days of inactivity, with a heads-up before it goes. Organisations created before this change may still see a `sandbox-demo` cluster. It keeps working, but its deploys are simulated rather than run on real infrastructure - start a playground for the real thing. From the terminal: ```bash theme={null} ankra cluster playground create ankra cluster playground status ``` [Playground →](/guides/cluster-sandbox) ## An agents page that shows who needs you The **AI Agents** overview now tells you where your fleet stands without opening every agent. Each row carries a status chip for the agent's latest run - **Needs your input**, **Blocked**, **Failed**, **Working**, **Goal met**, or **Never run** - plus the run's outcome summary, and a call to action that matches the state: **Respond** jumps straight into the chat conversation a parked agent is waiting in, **Review** opens a blocked or failed run's transcript, **View live** follows a run in flight, and **Run now** dispatches an agent that has never run. Agents waiting on a person sort to the top, a summary strip counts and filters them, and finished one-off missions fold away under **Completed missions** so old dispatches stop burying the live ones. The sidebar's **AI Agents** item now badges the number of agents waiting on your input - an unanswered agent question is no longer something you discover by accident. [AI Agents →](/platform/ai-agents) ## A new default AI model Ankra AI now runs on **Kimi K3** by default, with **GLM 5.2** as the Think tier. Both are served through Ankra's managed access - there is nothing to configure, and existing conversations carry on unchanged. | Tier | Model | Context | | - | - | - | | **Expert** (default) | Kimi K3 | 1M tokens | | **Think** | GLM 5.2 | 1M tokens | | **Quick** | Claude Haiku 4.5 | 200k tokens | Both new tiers carry a **1M-token context window**, up from 200k, so long troubleshooting sessions and large manifests fit without compaction. Expert accepts images and reasons before it answers; Think reasons at high effort on text. **Auto** still picks per question, and sends anything with an attachment to Expert. Reasoning is now visible on these models the same way it always was on Claude - you see the thinking as it streams, not just the answer. None of this is a limit. Under **AI** → **Settings** → **Models** you can still edit any tier, point it at your own model, or switch the whole organisation onto your own Anthropic, OpenRouter, or self-hosted OpenAI-compatible key - which serves every tier from your provider instead. [AI Provider →](/platform/ai-provider) ## Monorepo demos deploy every app A demo of a monorepo used to run exactly one of its components - so a two-app repository demoed its API where the frontend belonged, and the preview URL answered with a bare JSON 404. Demos now deploy **every component of the application by default**, each as its own pod in the demo namespace, and the demo only reports ready once all of them accept connections. The **web entry** - the frontend-shaped component - owns the demo URL, and the API component is published under `/api` on the same host, routed straight to its own service, so the browser reaches both halves without the app needing environment-specific proxy configuration. Components also reach each other in-namespace by name (`http://crm-api:8090`), and demo env values can reference `${{ ankra.demo_component..url }}` placeholders that resolve at deploy time. The launch dialog now shows each component's build status and tag side by side, lets you include or exclude components, and lets you move the entry. The demo detail page groups the bill of materials per component and lists each component's public path next to the preview URL. Automatic PR previews and the AI's `deploy_pr_demo` tool deploy every component the same way - and PR previews of monorepos, which previously always reported "build missing", now resolve their images correctly. [Applications → Monorepo demos →](/concepts/applications#monorepo-demos-every-component-one-url) ## Demos that run any codebase Launching a demo of a real application used to fail in three quiet ways: the readiness probe watched a port the app never listened on, a database-backed app crashed with no database, and a fresh database had no schema. All three are now handled end to end. **Ports resolve themselves - twice.** The launch resolves the container port from what the repository actually declares (recorded component ports, the runtime Dockerfile's `EXPOSE`, the generated Deployment manifest, the analysed target port), and the launch dialog no longer overrides that resolution with a default. And when the artifacts themselves are wrong, the platform reads the running container's own startup banner - `Accepting connections at http://localhost:8001` - and repoints the demo at the announced port on the fly, once, recording the correction so the next launch is right from the start. A demo that still times out now tells you exactly what happened: *"Nothing accepted connections on port 3000. The container's logs say it listens on port 8001."* **Databases arrive configured.** Analysing a repository now detects a database dependency - a Postgres client, an ORM config, a composed database - and seeds the demo configuration: throwaway database on, the connection variables the code actually reads wired to `${{ ankra.demo_database.* }}`, and the repository's migration command. Demos run migrations as an init container inside your own image, after the database accepts connections, so a fresh demo database provisions its schema before the first request. Schemas needing Postgres extensions declare them (`database_extensions: ["vector"]`) and the database creates them at initdb. **And the CLI can drive all of it.** `ankra application demo detail` shows the provisioning steps and failure detail, `demo logs` fetches a bounded log tail, `demo config get/set` edits the saved defaults - environment, database, migration command, extensions - and `demo fix` dispatches the pre-setup agent, which now also repairs failed migrations and wrong ports. [Applications → Preview demos →](/concepts/applications#preview-demos) ## Create applications on a branch that actually exists The **Create application** form no longer asks you to type a branch name into a free-text field and hope. Once you pick a repository, the branch field becomes a searchable list of the repository's real branches with its default branch preselected - and **Enter a different branch** stays one click away for a branch you have not pushed yet. If your organisation has a single GitHub App installed, it is selected for you, and repository search now ranks an exact name first instead of last. The form also spells out what **Create** will do before you commit to it: an AI agent analyses the repository and generates a Dockerfile, CI/CD pipeline, and Kubernetes manifests where they are missing, opens a setup pull request for you to review and merge, and publishes container images to your organisation's private registry. [Applications →](/concepts/applications) ## Publish your add-ons to the whole platform An add-on your organisation curated can now be proposed to the global Ankra Catalog - the add-on catalog every organisation sees. **Publish to Ankra Catalog** on the add-on's page shows exactly what would be proposed: the rendered `kind: Addon` manifest, a diff against the currently published version when one exists, the target repository, and a version-history link into the catalog's Git log. Confirming opens a pull request against the public [ankraio/ankra-global-resources](https://github.com/ankraio/ankra-global-resources) repository on your behalf - review and merge is the approval gate, so nothing ships silently. A **Publish history** section then tracks each request through its life: pull request open, merged and awaiting sync, live in the catalog, or closed - with the pull request linked at every step. Once merged, the platform sync ingests the manifest and the add-on appears for **all organisations**; your own organisation's copy keeps shadowing the global one, so publishing never changes what you see. The catalog repository itself gained the new `Addon` manifest kind alongside its Helm registries, and removing a manifest retires the global add-on again. [Publishing Add-ons →](/platform/addon-publishing) ## Every manual cluster action, on the record Cluster **Settings** grew an **Audit** tab: a chronological trail of every manual action performed on that cluster - who did it, what it was, and when. Starting, stopping, upgrading, or terminating a cluster, scaling workers or the control plane, creating, editing, or deleting node groups, restarting a node, resizing the bastion, deploying, updating, redeploying, retrying, or deleting stacks, and agent-side Helm upgrades and rollbacks all now write attributed entries - the acting member's email, the resource touched, the timestamp, and expandable structured details such as the previous and requested counts of a scale. The tab is the per-cluster view of the organisation [Audit Log](/guides/audit-log) and shares its permission gate: audit read, held by owners and admins by default and grantable to custom roles. Entries are append-only, so the answer to "who scaled this cluster last Tuesday?" is one click away instead of an archaeology exercise. And when a stack edit results in a GitOps commit, the entry links straight to it - the platform action and the repository change it produced, side by side. [Cluster Settings → Audit →](/platform/cluster-settings#audit) ## The cluster trail and the organisation trail now join up The organisation [Audit Log](/guides/audit-log) and a cluster's **Audit** tab always read the same record, but you could not travel between them. Now every organisation entry names the cluster it belongs to, and that name links straight into the cluster's own tab; organisation-level events such as sign-ins and invitations show no cluster, and a cluster you have since deleted keeps its name marked deleted, because the record outlives the cluster. A **cluster** filter joins the existing user, action, and date filters, and it lives in the page URL, so "everything anyone did to this cluster" is a link you can share. From the other side, the cluster tab gained **View in organisation audit log** - the full trail with that cluster preselected - plus **Export CSV** and **Export JSON** for the cluster's own entries. Exports run through the organisation export narrowed to the cluster, so a per-cluster download and the filtered organisation view can never disagree, and every exported row now carries the cluster next to the actor and action. [Audit Log →](/guides/audit-log) ## Stop and start clusters on a schedule Every self-managed cluster - Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE, and HPE Morpheus - can now park itself. A new **Power schedules** card in cluster **Settings** → **General** holds up to 20 schedules per cluster, each one a stop or a start that fires either once at a chosen date and time or repeatedly on a 5-field cron expression in the timezone you pick. The classic pair - a `0 19 * * 1-5` stop and a `0 7 * * 1-5` start - shuts a development cluster down on weekday evenings and has it back before the team arrives, so it only accrues compute cost while someone is using it. Schedules are careful about cluster state: a stop is skipped if the cluster is already stopped, a start only fires when it is stopped, and if the cluster is busy with another operation the schedule retries every 5 minutes for up to 24 hours before recording a failure. Each schedule shows its next run and last outcome, and a toggle pauses it without deleting it. [Power Schedules →](/platform/cluster-power-schedules) ## Credentials that show what you have left Open a cloud credential and the detail page now reads capacity straight from the provider - and it works for every cloud provider Ankra supports. A Proxmox VE credential shows the cluster behind it: every node with used and total CPU and memory, storage pools with shared pools counted once, and how many guests are running with the vCPUs and memory allocated to them. An UpCloud credential shows the account's real resource limits - cores, memory, storage, IPs, networks, load balancers - as usage bars, next to remaining credits. OVHcloud shows its true per-region project quotas the same way: vCPUs, instances, memory, and volume storage per region, each with how much of the quota is spent. Azure shows the subscription's virtual machines and AKS clusters with regional vCPU and VM quota bars, and DigitalOcean shows droplet and floating IP limits. HPE Morpheus shows each cloud on the appliance with used and total memory and storage. Hetzner and Scaleway expose no limits over their APIs, so their views are live usage reports: servers with combined vCPUs, memory, and storage, plus the networking resources around them. The numbers come from the provider at view time, so they include everything in the account, also what was created outside Ankra. Capacity questions like "will another three workers fit?" get answered on the credential page instead of in the provider console. [Credentials →](/platform/credentials#capacity-and-usage) ## A calmer credentials page The credentials surface got a visual and structural cleanup. The list page now has a single quiet toolbar - search, provider filter, sort, and paging in one row - and flatter cards that keep to the essentials: provider, type, health, and when the credential was last updated. The credential detail page, which had grown into one long scroll of identity, capacity, connection details, rotation, and usage all at once, is now a compact identity header with tabs: **Overview** for connection details and the verify check, **Capacity** for the provider report, **Used by** for the clusters and operations on the credential, and **Rotation** for in-place secret rotation. Each tab loads its own data, so opening a credential no longer fires every provider query at once, and the layout now matches the GitHub credential cockpit, so every credential detail page reads the same way. [Credentials →](/platform/credentials#view-credentials) ## The Ankra CLI adds Proxmox, Morpheus, and control over agent runs [Ankra CLI v0.9.0](/integrations/ankra-cli-changelog) is out, and it is the largest CLI release so far: * **Proxmox VE and HPE Morpheus clusters** - `ankra cluster proxmox` and `ankra cluster morpheus` cover the full lifecycle, node groups, control plane, SSH keys, autoscaling, and upgrades, plus discovery of what each platform offers and their own credential commands. * **Managed Kubernetes parity** - provider-native stop and start, `node-pool update` for in-place node count and autoscaling changes, autoscaling bounds at create time, and Scaleway Kapsule as a new provider. * **See and stop what your AI agents are doing** - `ankra agents` lists dispatched agent runs, shows one in full, reads the session transcript of what the agent actually said and did, and cancels a live run. * **Agent-mode chat asks before it writes** - every mutating tool halts the turn and proposes the write. The stream now shows each proposal with its risk and parameters, an interactive session prompts to run or discard it, and `ankra chat actions confirm | reject | list` drives the same decision from a script. (One exception has since joined: a conversation switched to the new **Auto** autonomy setting executes validated additive stack deploys with a receipt instead of halting - see [Additive deploys that can run themselves](#additive-deploys-that-can-run-themselves) above.) * **Node restarts and bastion resize** - restart any provisioned node as a tracked operation across Hetzner, OVH, UpCloud, DigitalOcean, Scaleway, and Proxmox, and resize the bastion in place. * **Secrets encrypted in a single commit** - `ankra cluster encrypt manifest --set` applies the new value in memory before encrypting, so the plaintext never reaches git history. The release also fixes a batch of commands that were decoding response shapes the API never sent, and therefore showed nothing: manifest and member listings, chat health, and stack history all return real data again, and `ankra cluster encrypt -f` and `cluster clone` no longer strip fields and comments from your cluster YAML. [CLI Changelog →](/integrations/ankra-cli-changelog) | [Ankra CLI →](/integrations/ankra-cli) ## Ask the AI to build you a Proxmox cluster Cluster provisioning in chat now covers Proxmox VE. Ask the assistant for a new cluster on your Proxmox credential and it drives the real flow: it reads the credential, lists your host nodes with their CPU and memory alongside the px-\* instance presets, and - once a node is picked - that node's storage pools, network bridges (SDN vnets included), and clone-source templates, then proposes the create for your confirmation like every other provisioning action. Credentials that reach Proxmox through an SSH jumphost work too - the chat reads ride the same tunnel the rest of the platform uses. Hetzner, OVH, UpCloud, and Scaleway were already there; Proxmox VE joins them, so the platform chat and the [MCP tool surface](/platform/mcp-tools) now agree on what can be built. [Ankra AI Assistant →](/platform/ai-assistant) ## Demos that fix their own configuration An application image that validates its environment on boot used to make a hopeless demo: deployed bare, it crash-loops, and the preview never comes up. Now, when a demo fails with a startup crash, Ankra dispatches a **pre-setup agent** automatically. It reads the crashed container's logs, works out which variables the application demands, and generates the demo configuration: database URLs become references to the demo's own throwaway Postgres, secrets get fresh random values, and mode flags get the value without external side effects. It saves the result as the application's demo defaults - every future demo inherits them - and redeploys the failed demo to prove it boots. The agent's run is visible on the AI agents page like any other mission, it never weakens the application's own validation, and it runs at most once per demo per day. You can also trigger the same fix on demand, or ask the AI assistant to repair a failed demo in chat - the same diagnostics, configuration, and redeploy tools back both. Two supporting fixes ship alongside: demo redeploys switch to a Recreate rollout, so a configuration change can never wedge against the namespace resource quota, and the throwaway demo database now runs a pgvector-capable Postgres, so migrations using `CREATE EXTENSION vector` work. [Preview demos →](/concepts/applications#automatic-pre-setup-when-a-demo-crashes) ## A full page for every demo Inspecting a demo used to mean a dialog stacked over the Demos tab - cramped, unlinkable, and gone the moment it closed. Every demo now has its own page, opened from a demo row's status pill or **Details** button. A header carries the demo's identity - branch or pull request, status, full-stack and database badges, namespace, image tag, and the time left - with **Open preview** and **Stop demo** alongside. Below it, the view reads live from the staging cluster and refreshes itself: the provisioning timeline, the namespace's bill of materials with each resource's manifest a click away, the Kubernetes events that explain a stall, and on-demand pod logs. Side cards cover how to reach the demo - the preview URL or the exact port-forward command - the demo's facts, and the staging cluster it runs on. Because it is a page, it has a URL: share a provisioning demo with a teammate and they land on the same live view, breadcrumbs and all. [Preview demos →](/concepts/applications#preview-demos) ## Monorepos onboard as applications, one component per app An application used to mean one deployable app per repository: a single image, a single build workflow, a single container port. Point Ankra at a monorepo and the generated pipeline built each app correctly and pushed each image to its own place - but everything that read the result looked somewhere none of them published to, so a repository whose builds were green reported "No built images were found for this application yet." and the demos tab had nothing to launch. Applications now model a repository's **components**. An ordinary repository has one, covering the root; a monorepo has one per app, each with its own Dockerfile under its directory, its own manifests, its own workflow carrying a `paths:` filter so touching one app does not rebuild the others, and its own image at `//`. Build state, published images, publish readiness, deploys, demos, and container scanning are all reported per component, so an application whose API component built and whose frontend did not says exactly that instead of collapsing the two into one wrong answer. Ankra works out the components from what the repository already declares first - a repository it has onboarded before keeps the component names its live workflows use, so re-running analysis never renames an app and orphans the images it has published - then from what the analysis proposes, then from the repository's structure. A Dockerfile at the repository root always means a single app. Nothing needs migrating: an application with no recorded components still resolves to exactly the coordinates it always used, and re-running analysis records the components it finds. The launch dialog gained a **Component** picker for monorepos, with the container port following your pick. That was the sharpest edge of the old behaviour: a component inherited whichever port appeared first among the repository's generated files, so an API component on 8090 could be probed on port 3000 of a frontend and the demo sat in *provisioning* until it timed out. [Monorepos →](/concepts/applications#monorepos) ## Application setups that rescue themselves Adding an application kicks off an AI setup that analyses the repository and generates the Dockerfile, Kubernetes manifests, and CI/CD pipeline. Until now that screen could over-promise: before any agent had actually picked the task up it already said "AI Agent assigned to task", and if the setup stalled or failed there was no agent anywhere in the product to look at. The first step now reads **Waiting for an AI agent to pick up the task** until real progress arrives, so the screen tells you what is actually happening. More importantly, stuck setups no longer depend on you noticing them. A setup that sits silent for fifteen minutes, or fails its analysis, now dispatches a **rescue mission** automatically: a one-shot agent that appears on the AI Agents page with a full transcript, diagnoses what went wrong, retries the setup when that is the fix, and reports exactly where things are stuck when it is not. The setup screen links straight to the AI Agents page from its stall warning and its failure state, and dispatch is bounded to one rescue per application per day. Alert delivery got the same discipline behind the scenes: a notification whose webhook can never deliver - the webhook was deleted, or never had a URL - now records its failure and stops retrying instead of piling up as pending work, and stale undelivered notifications are swept automatically. One organisation's undeliverable-webhook backlog was what starved new application setups in the first place. [Applications →](/concepts/applications) ## Every chart version shows its license Every chart version's overview in the chart browser now carries a **License** field with the license's SPDX identifier - `Apache-2.0`, `MIT`, `AGPL-3.0-only`, and so on. Ankra reads the chart's `artifacthub.io/license` annotation from `Chart.yaml`, and falls back to detecting the license from the `LICENSE` or `COPYING` file inside the chart package when the annotation is missing. Copyleft licenses - the AGPL and GPL family - are visually highlighted, so a chart whose license carries obligations for your own code stands out before it lands in a stack. [Helm Charts →](/platform/addon-catalog) ## Gateway resizes that wait for the cloud Resizing a cluster's gateway could be reported as failed even though it was proceeding normally: the platform timed out while the cloud provider was still completing the resize. Resize operations now wait for the provider to finish, so a resize that succeeds on the provider side succeeds in Ankra too. ## Registry creation tells you what is wrong Creating a Helm registry with a malformed spec used to answer with a bare 500 and no hint at the cause. The API now validates the spec first and answers with a clear 422 naming exactly what is wrong. The CLI side got friendlier too: registry creation accepts both flat and YAML spec files, plus `--name` and `--url` flags for the simple case where a file is overkill. The 422's example now shows both accepted shapes - the raw API body and the pre-wrap file that `ankra helm registries create -f` expects - so copying the example works from either caller. [Registries →](/guides/registries) ## Cluster validation that names the real problem Validating a cluster definition whose add-on could not be resolved used to give one undifferentiated error. Validation now distinguishes the three cases: the registry is not connected in your organisation, the chart does not exist in that registry, or the chart exists but the pinned version does not. `chart_version` itself is validated too, so a typo in a version pin is caught at validation time instead of at deploy time. Version pins compare the way Helm resolves them: `1.20.0` matches a chart published as `v1.20.0` and vice versa, so a pinned production config validates no matter which spelling of the version your chart's publisher uses. `ankra cluster validate` now also runs the per-resource field checks that apply enforces - add-on namespace rules, group names, empty manifest bodies - so a rule that would reject your file at deploy time is caught at validation time, in CI, before anything touches a cluster. And the namespace rule itself got saner: add-on namespaces now accept everything Kubernetes accepts (1-63 characters, lowercase letters, digits and dashes), so short names like `ory`, `ops`, or `dev` are valid - the old 4-character minimum is gone. [ImportCluster YAML Schema →](/reference/importcluster#validation) ## Secret detection that reads the value, not just the key The plaintext-secret scan used to flag anything whose key looked secret-like, so `timeout: 30s`, port numbers, and references to already-encrypted values all raised warnings. Detection is now value-shape aware: durations, ports, booleans, and references no longer count, while values that actually look like credentials still do. High-confidence findings can also gate CI - enable `strict_secrets` and a definition carrying a likely plaintext secret fails instead of warning. [SOPS Secrets →](/guides/sops) ## CLI listings built for scripting List commands across the CLI now page properly: pagination flags control the window, `--all` fetches everything, and a listing with more results than it shows says so instead of silently truncating. JSON output uses a consistent envelope across commands, so scripts can parse every listing the same way. And `ankra cluster encrypt` grew batch ergonomics: `--key` is repeatable and `--all-data` encrypts every data field in one pass. [Ankra CLI →](/integrations/ankra-cli) ## See what GitOps is actually syncing `ankra cluster gitops status` shows the cluster's live GitOps source: the connected repository, the branch Ankra reads from, the credential in use, and the last synced commit with its sync state. When a cluster is not picking up changes, the first question - is it even looking at the right branch? - is one command away. [GitOps →](/concepts/gitops) ## Preview a chart's manifests before anything deploys `ankra charts template` renders an add-on's chart with your values server-side, from the same mirrored package Ankra deploys, and prints the resulting Kubernetes manifests - or the exact rendering error a deploy would have hit. Wrong secret key names, surprising Service ports, and values that leak into a ConfigMap all surface on your laptop instead of against a production cluster. `ankra charts values` prints a chart version's default values document, the baseline you diff your overrides against. Neither command needs direct registry access on your side. [Stacks and add-ons →](/concepts/stacks) ## Declare Helm registries in the cluster file The ImportCluster file can now carry a top-level `spec.helm_registries` block. Registries declared there are connected automatically before any add-on deploys - same name and URL is an idempotent no-op, private registries reference an existing credential by name so secrets stay out of Git - which makes a GitOps repository genuinely self-contained: clone, apply, and the declared state is reachable on a fresh organisation with no side-channel setup commands. [ImportCluster reference →](/reference/importcluster) **July 2026** # More Clouds, Vulnerability Scanning, and Team Roles ## Whoever creates a cluster can reach it with kubectl Creating or importing a cluster now grants you cluster-scoped `cluster-admin` Kubernetes access to it, so `ankra cluster kubeconfig add` works as soon as the cluster is up instead of answering "You do not have access to this cluster" until an admin granted you access by hand. The grant is an ordinary Cluster Access grant: it shows up in the cluster's **Access** view and in `ankra cluster access list`, reconciles into native RBAC, and can be revoked. Clusters created before this change have had the same grant seeded for their creator, so kubeconfig access there starts working without any operator action. [Cluster Access →](/guides/cluster-access) ## Branch and PR demos detect your application's port Launching a demo no longer assumes your application listens on port 8080. Every demo lane - the Launch demo dialog, AI-deployed PR demos, and the automatic pull-request previews - now reads the port from your application's generated Dockerfile (`EXPOSE`) and deploys with it, so a Next.js app on 3000 or a Flask app on 5000 comes up ready instead of sitting in "provisioning" forever behind a readiness probe on the wrong port. The launch dialog pre-fills the detected port, and the advanced override still wins when you need something different. ## Security Center overview and findings, modernised The Security Center's **Overview** and **Findings** tabs now share the visual language the Compliance tab introduced. The overview leads with a fleet security scorecard: a posture ring showing the share of scanned clusters with no actionable critical or high findings, a verdict at a glance, and tiles for critical, high, fixable-severe, and scan coverage. Findings swaps its second tab bar and boxed filter panel for pill filter chips with live counts pulled from your fleet's data. Ankra AI is now one click away from both tabs: **Fix top findings with AI** on the overview hands the top remediation candidates to the AI with a fleet-wide remediation ask, and every finding's detail sheet gains **Ask Ankra AI**, grounded in the finding and the cluster it was observed on. [Security Center →](/guides/cluster-security) ## The Security Center overview shows which application is generating the risk The overview no longer stops at fleet totals. A new **Risk breakdown** row splits your observed occurrences three ways: a **Severity mix** with a bar per band, a **Review state** split across open, acknowledged, and accepted risk, and a **Fix readiness** number telling you what share of your critical and high occurrences actually have a published fix to move to. The **Observed fleet trend** chart grew up too - gridlines, a filled area, the observed range on the axis, a change chip, and a tooltip on every snapshot. Most usefully, **Application hotspots** now ranks the workloads generating the most actionable risk across your whole fleet, weighted so criticals outrank a pile of lows. Each row names the workload, the namespace and cluster it runs in, the add-on it was attributed to, how many of its findings already have a fix, and links straight into that cluster's findings filtered to it. Alongside it, **Risk by cluster** ranks your clusters by actionable occurrences, so "where is the heat" and "who owns it" are both answerable without leaving the overview. The overview also lays itself out based on the space it actually has rather than the size of your screen, so reading it beside the docked AI panel no longer squeezes the scorecard tiles into unreadable slivers. [Security Center →](/security/overview) ## Ankra AI now runs on Claude Opus 5 The platform's highest-capability AI tier moved from Claude Opus 4.8 to Claude Opus 5. Everything that escalates to the Expert model - plan mode, image analysis, high-risk and multi-resource changes, complex architectural questions, deploy analysis, and CI/CD pipeline generation - now runs on Opus 5, at the same price and with the same 1M-token context window. Organisations that customised their model catalog keep their pinned models; the built-in Expert entry upgrades automatically. ## Application images now build to your private Ankra registry Application CI/CD no longer publishes to GitHub Container Registry. When Applications is enabled, Ankra provisions a private project for your organisation in its own registry, together with the GitHub Actions credentials and the Kubernetes pull secret each cluster needs. The generated pipeline builds your source, scans the image, and pushes it to that private registry; the generated manifests already reference the image and its pull secret, so a merged pull request flows straight through to a running workload. The Ankra GitHub App now requests the **Secrets** permission to install that registry login, replacing the old GHCR make-public flow. [Applications →](/concepts/applications) ## AI Insights gets out of the way when your cluster is healthy The cluster AI Insights page no longer fills the screen with empty charts and zeroed counters when there is nothing wrong. A healthy cluster now shows a single compact "All quiet" summary confirming that monitoring is active, with one-click access to the resolved-fault history and to alert creation. The full analytics dashboard returns automatically the moment new activity appears - and the Faults tab no longer suggests your filters are hiding results when the cluster is simply healthy. ## Fleet overview: every Ankra-created cluster on the globe, plus a new fleet world The dashboard's fleet globe now places every cluster Ankra created for you - self-managed (Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway) and managed Kubernetes (DOKS, UKS, GKE, AKS, EKS, OVHcloud MKS, Kapsule) alike. When a cluster's nodes don't carry cloud topology labels (common on k3s and fresh clusters), the platform falls back to the region you picked at create time, so "not on the globe" is now reserved for genuinely unlocatable clusters. Proxmox and Morpheus clusters keep resolving through agent GeoIP. Next to the globe, the fleet overview gains a **fleet world**: an isometric 3D view where every cluster is a tower - height tracks node count, colour and glow track live status - so the whole fleet is readable at a glance, including clusters with no geolocation. Click a tower to jump into the cluster. The section layout was also rebalanced so the globe, fleet world, vital signs, utilisation, and security posture fill the page consistently. ## DigitalOcean clusters: reliable DNS and LoadBalancers that just work DigitalOcean's default DNS resolvers are flaky enough to intermittently break name resolution on nodes and inside pods. Ankra-provisioned DigitalOcean clusters now pin node DNS to Cloudflare (`1.1.1.1` / `1.0.0.1`) with a self-healing setup that re-applies within a minute if anything reverts - on new clusters at provision time, and on existing clusters automatically through the platform's drift detection. LoadBalancers on private-node DigitalOcean clusters also work out of the box now: the DigitalOcean cloud controller ships at v0.1.68+ (private-IP-only droplet support), is wired with the cluster's VPC UUID automatically (no more 422 "target droplets don't belong to the same VPC"), and the bundled Traefik ingress carries the `REGIONAL` load-balancer type its Service needs on private-only topologies. Existing clusters converge without any operator action. ## Chat turns survive disconnects An AI chat answer no longer depends on your browser staying connected. Every interactive turn is now written to a durable server-side event log as it streams, and the turn keeps running even if your tab closes, your network blips, or the platform restarts mid-deploy - tool calls finish, the answer completes, and nothing is lost. A new resume endpoint (`GET /chat/conversations/{conversation_id}/events`) replays the missed frames from where you left off, with live tailing and `Last-Event-ID` support. Provider hiccups were already covered by automatic failover; now the conversation itself is just as durable. ## Proxmox host-spread clusters now provision across nodes Control planes and workers placed on different Proxmox hosts previously stalled at the first cross-host node - the private SDN network is node-local, so the bastion had no path to a VM on another host. When a Proxmox VE credential carries Tailscale/Headscale settings, every VM now joins the tailnet at create time (through the Proxmox guest agent, before it has any other network path), and Ankra uses that tailnet address for SSH, kubeadm join, etcd peering, and haproxy upstreams. Enabling this requires the credential's Proxmox API token to hold `VM.GuestAgent.Unrestricted`, in addition to the `VM.GuestAgent.Audit` privilege address discovery already needs. [Proxmox VE credentials →](/platform/credentials/proxmox#tailscale-and-headscale-optional) ## Stack Profiles now show who you can trust Saving a stack as a profile now carries its existing description into a cleaner, modern save flow. Categories are searchable and reusable, tags are suggested as interactive chips, and you can choose whether the profile stays private to your organisation, becomes public, or is shared with specific organisation slugs. Profile cards and details now show the publisher's organisation name, logo, and verified status. Verified Ankra profiles appear first, so official profiles are easy to identify before you use them. Organisation admins can upload or replace their publisher logo from organisation settings. [Stack Profiles →](/concepts/stack-profiles) ## Profile capture understands every secret path you can declare Saving or publishing a stack profile no longer fails when a secret sits inside a YAML list - think fluent-bit's `config.pipeline.outputs[0].http_passwd` - or when an encrypted path is declared by its SOPS leaf key alone (`api-key`), the same shorthand `ankra cluster encrypt` uses. Both spellings now redact cleanly into required secret parameters. Values that are runtime environment references like `${OPENSEARCH_PASSWORD}` are no longer mistaken for plaintext secrets, so stacks that already inject credentials from Kubernetes Secrets capture without forcing a bogus secret parameter. [Stack Profiles →](/concepts/stack-profiles) ## Breadcrumbs everywhere you are Every page in the portal now carries a breadcrumb trail derived live from where you are - `Home / Clusters / prod-eu / Stacks / loki` instead of guessing your way back with the browser button. Dynamic segments show real names, not identifiers: the cluster crumb reads the cluster's name, operations show their display name, stack profiles and AI agents theirs. Each crumb that corresponds to a real page is clickable (the cluster crumb jumps to its overview), structural URL noise is hidden, and the trail stays out of the way on phones where the back button already does the job. ## Organisation menu goes straight to your organisation **Organisation** in the profile menu (and `/organisation`) now lands directly on your current organisation's overview instead of a list. Switching moved closer to hand: a **Switch organisation** submenu in the profile menu lists all your organisations with your current one marked - one click to switch, plus shortcuts to pending invites, the full organisation list, and creating a new organisation. The full list with per-organisation switching lives on **All organisations**. [Organisations →](/concepts/organisations) ## Your AI team, preloaded Every organisation now starts with a five-agent **AI team** - AI Team Lead, Developer, Database Admin, SRE, and DevOps - waiting under **AI → Agents**. Each one is chattable directly: open an agent and the conversation carries its persona, so the SRE talks evidence and the Database Admin checks backups first. Creating your own agent is now an **AI-guided interview** - pick a role, shuffle a name, choose focus areas and guardrails, and the wizard writes the persona for you (the classic form is one click away). Defaults are safe by construction: propose-only autonomy, hard budgets, and nothing runs until you ask. [AI Agents →](/platform/ai-agents) ## Demos now run real applications - env vars, secrets, and a throwaway database Demos used to run the container image bare, so anything needing configuration crash-looped on boot. Now every application carries **demo environment defaults** (the gear on the Demos tab) that all lanes inherit - manual launches, `deploy_pr_demo`, and automatic PR previews - with **per-launch overrides** in the launch dialog. Values marked secret ride Vault-backed secret slots and reach the demo as a Kubernetes Secret; plaintext never persists. Need a database? Toggle **Attach a throwaway Postgres** and an ephemeral instance spins up inside the demo namespace with a random per-demo password, wiped with the demo - your env values reference `${{ ankra.demo_database.* }}` placeholders, so any codebase's configuration naming just works. [Preview demos →](/concepts/applications#environment-variables-and-a-throwaway-database) ## Application demos get a shareable URL Deploy a throwaway demo of an application's pull request or branch build and Ankra now hands back a **public preview URL** automatically - no manual ingress or DNS wiring. When your organisation has a demo base domain configured, the demo is served under it; otherwise, if the staging cluster has an active Ankra DNS zone, it gets a `*.ankra.cc` host that resolves on that cluster. Without either, the demo still deploys and stays reachable in-cluster. The same URL is returned everywhere you deploy a demo - the portal Demos tab, `ankra application demo deploy`, the `deploy_pr_demo` AI/MCP tool, and the automatic preview posted on a connected application's pull requests. [Preview demos →](/concepts/applications#preview-demos) ## A home for Ankra Pick a **home channel** and Ankra starts the conversation on its own. When something important happens - a failed deployment, an agent going offline, a new severe CVE - and no routing rule already handles it, Ankra posts to your home Slack channel. Set it under **Organisation Settings → AI → Workspaces** ("Ankra's home"); only critical and warning events reach it, and it never double-notifies what a routing rule already sent. [Home channel →](/guides/webhooks#home-channel) ## Managed Kubernetes and three more clouds Ankra now provisions and operates **provider-managed Kubernetes** - DOKS, UKS, GKE, OVHcloud MKS, AKS, and EKS - with node pools, autoscaling (all but UKS), upgrades, and discovery/import of existing clusters. Self-managed provisioning also gained **DigitalOcean**, **Proxmox VE**, and **Morpheus** alongside Hetzner, OVH, and UpCloud. [Managed Kubernetes →](/guides/managed-kubernetes) · [DigitalOcean →](/guides/digitalocean-clusters) · [Proxmox VE →](/guides/proxmox-clusters) · [Morpheus →](/guides/morpheus-clusters) ## Proxmox VE zero-config bootstrap Saving a Proxmox VE credential now sets up everything a cluster needs on an empty host: VM storage, the management bridge, a private NAT network (`ankra`, 10.20.0.0/16) with DHCP built on Proxmox SDN, and an Ubuntu 24.04 cloud-init template. Cluster VMs on the private network get their addresses from Proxmox IPAM automatically - no guest agent required in the image - and reach the internet through the host's public interface. [Proxmox VE →](/guides/proxmox-clusters) ## Tailscale for Proxmox VMs Attach a Tailscale/Headscale login server and a reusable auth key to a Proxmox VE credential, and every VM Ankra provisions with it installs Tailscale and joins your tailnet on first boot - each VM getting its own tailnet address, reachable across Proxmox hosts and from your own networks. You can add, change, or remove it on an existing credential without recreating it. [Proxmox VE →](/platform/credentials/proxmox) ## Cluster vulnerability scanning with Trivy Install the Trivy Operator add-on and Ankra reads its vulnerability reports into a dedicated **Security** console: severity breakdowns, a blast-radius-ranked priority queue, remediation campaigns, and a phased burn-down plan. The AI assistant can summarise and prioritise CVEs with the same data. [Cluster Security →](/guides/cluster-security) ## Cluster compliance is now actionable Selecting a cluster from **Security → Compliance** now opens a full cluster Security page instead of a narrow drawer. The Compliance tab shows failed and passed CIS, NSA/CISA, and Pod Security Standards checks, configuration-audit hotspots, affected counts, remediation guidance, and filters for the exact result you need. Every failed check has **Fix with AI**. The cluster-scoped agent receives the benchmark and remediation context, can inspect the affected Kubernetes resources, apply safe fixes with the usual confirmation guardrails, and verify the result. [Cluster compliance →](/guides/cluster-security#cluster-security-view) ## Team roles, cluster groups, and scoped access Access is now governed by **roles** you assign at a scope - the whole organisation, a single cluster, or a **cluster group** (static or label-selected). A role can bundle Kubernetes access that Ankra provisions on every cluster in scope, and every change is captured in an organisation audit log. [Roles & Access →](/guides/roles-and-access) ## Pipeline agents Build CI-native infrastructure agents from plain pipeline jobs and the Ankra CLI - PR review, deploy watching, and scheduled health checks - with no framework or orchestration layer. [Build pipeline agents →](/guides/pipeline-agents) ## Proxmox automatic setup supports DHCP and provider-managed interfaces The "Set up storage and network automatically" bootstrap now works on dedicated servers where the management interface is configured via DHCP or marked `manual` (the provider assigns the IP outside Proxmox's network config, common on Scaleway and similar bare-metal hosts). Previously only statically addressed interfaces were accepted. [Proxmox VE credentials →](/platform/credentials/proxmox) ## Restart a node or resize the bastion, from the UI, CLI, or chat The **Nodes** tab now has a **Restart** action for every control plane node, worker, and bastion/gateway (Hetzner, OVH, UpCloud, DigitalOcean, Proxmox), and a **Resize** action for the bastion/gateway (those five plus HPE Morpheus). Both run as tracked operations, and the node table now shows each node's live status straight from the cloud provider's API so a crashed or externally-stopped VM is visible before you act on it. The same actions are available from `ankra cluster nodes restart` / `bastion resize`, and by asking the Ankra AI assistant - in the portal, Slack, or Teams - to restart a node or the bastion by name. [Nodes Settings →](/platform/cluster-nodes) · [Restarting a Node →](/guides/operate-ankra-managed-clusters#restart-a-node) ## Managed networking stack on Proxmox VE and HPE Morpheus Proxmox VE and HPE Morpheus clusters now ship the same Ankra-managed networking stack as every other self-managed provider: Traefik and cert-manager are installed as Ankra stacks after bootstrap (visible and manageable in the platform) instead of relying on the k3s-bundled Traefik. The **Include Networking Stack** option in the create wizard is on by default; ingress is exposed through the built-in k3s service load balancer on node IPs, since these providers have no cloud load-balancer API. Storage still comes from add-ons such as Longhorn - install one from the add-on catalog for persistent volumes. Both providers also reached full surface parity: `ankra cluster proxmox` and `ankra cluster morpheus` CLI commands (create plus catalogs for hosts, storages, bridges, templates, and plans, with stop/start and the generic scale, upgrade, node-group, and SSH-key verbs), and the Ankra AI assistant can now create Proxmox VE and HPE Morpheus clusters in chat with the same guided, confirmation-gated flow as the other providers. ## Lifecycle parity across self-managed clouds The canonical self-managed provider set is now **Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE, and HPE Morpheus**. All seven can be stopped and started from the portal, CLI, API, and Ankra's AI or any MCP client. The new `ankra cluster scaleway stop|start` commands close the remaining CLI gap for Scaleway. ## Stack draft editor reliability Three draft-editor bugs are fixed: drafts no longer disappear after a few minutes of editing (live edits now keep the draft's idle timer fresh, so the cleanup job only removes genuinely abandoned drafts), saving no longer fails with a spurious "modified by another request" conflict (a background sync task stopped invalidating the editor's save token), and renaming a stack inside the editor no longer detaches the draft from its deployed stack - the draft stays linked under the stack's current name and the rename is applied when you deploy. ## Managed Kubernetes catches up on day-2 Managed clusters closed three long-standing gaps. **AKS clusters can now be stopped and started** from the portal, CLI (`ankra cluster managed stop|start`), API, and Ankra's AI or any MCP client - Azure deallocates the compute while every setting is preserved, and Ankra advertises the capability per provider so each surface only offers it where it works. **Node pool machine sizes can finally be changed**: since no provider allows in-place size changes, Ankra performs a blue/green replacement - a new pool with the new size is created and brought to ready before the old one is deleted, with labels, taints, and autoscaling carried over. And the **CLI reached managed parity**: node pools take autoscaling bounds at create and via the new `node-pool update` command, and pool counts and autoscaling can be changed without touching the portal. [Managed Kubernetes reference →](/reference/managed-kubernetes) ## The AI Assistant runs your clusters' day-2 operations The AI Assistant (and every MCP client) can now do far more than create clusters. Seventeen new tools cover the full day-2 surface for Ankra-provisioned clusters - **scale workers and node groups, add, update, and delete node groups, change the control plane, stop, start, upgrade, and even deprovision a cluster** (with a type-the-name-to-confirm guardrail) - plus a **managed Kubernetes family**: create and operate **EKS, AKS, GKE, DOKS, UKS, OVHcloud MKS, and Scaleway Kapsule** clusters and their node pools from chat. DigitalOcean cluster creation from chat is also live. Every mutating tool goes through the same confirmation card and drift-checked approval flow as before. [MCP Tool Reference →](/platform/mcp-tools) ## Start a stopped Hetzner cluster Hetzner clusters can now be **started** again after a stop, like every other self-managed provider - previously only stop was available, and bringing a cluster back meant adding a fresh node group. Stopping releases the Hetzner compute while keeping the cluster's configuration, stacks, and credentials; starting re-provisions the saved topology and reconciles the cluster back online. Also fixed: managed **GKE**, **DigitalOcean (DOKS)**, and **OVHcloud MKS** clusters now correctly advertise node-pool autoscaling support in the create wizard. The portal now derives **Stop** and **Start** from one canonical self-managed provider set, so Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE, and HPE Morpheus stay in sync. Scaleway also gains `ankra cluster scaleway stop|start`, closing the remaining CLI lifecycle gap. [Stopping and Starting →](/guides/hetzner-clusters#stopping-and-starting-a-cluster) ## The full platform lifecycle over MCP and chat The Ankra MCP server and the AI assistant now cover the core lifecycle of **stacks, add-ons, applications, and credentials** - around 35 new tools. From Cursor, Claude, in-product chat, or chat over Slack, Teams, and SCM comments (all backed by the same tool registry), you can list, inspect, create, update, redeploy, clone, and delete stacks; read and update add-on settings and values; run the application lifecycle end to end including deploys and workflow runs; and manage credentials - list and inspect them (secrets always redacted), delete unused ones, and plan and apply a GitHub credential rotation. Creating credentials stays deliberately off-limits to the AI: it would mean handing it the secret itself, which the platform refuses. Reads need `mcp:read`; mutations need `mcp:write` and stay confirmation-gated in chat. A new reference page lists every tool with its scope. Alongside, the CLI gained `ankra credentials proxmox` and `ankra credentials morpheus` command groups (list and create, including SSH keys), and `ankra application delete` and `ankra application make-public` now ask for confirmation before acting. [MCP Tool Reference →](/platform/mcp-tools) · [MCP Server →](/platform/mcp-server) ## The sandbox demo is now fully editable The seeded "sandbox-demo" cluster is no longer read-only. You can now add, configure, move, clone, and delete stacks, add-ons, and manifests, work with drafts, and hit Deploy - the full stack-editing experience, before importing a cluster of your own. Deploys in the sandbox are simulated: they complete instantly and mark resources green without running anything on real infrastructure. Cluster-level operations that need a live agent (archive, agent tokens, add-on sync) remain unavailable there. [Playground & sandbox →](/guides/cluster-sandbox) ## Custom DNS records under your organisation's ankra.cc subdomain Organisation admins can now manage their own **CNAME, A, and TXT records** under their organisation's delegated `ankra.cc` subdomain - handy for aliasing an add-on's auto-generated hostname to something memorable. The **DNS records** console (under **Organisation Settings**) shows your zone and its status, a searchable, type-filterable records table, and lets you add, **edit in place**, and delete records with a chosen **TTL**. New and edited records provision asynchronously and show their state (pending, active, or failed, with the reason) in the list. [DNS Records →](/platform/organisation-settings#dns-records) ## Approving a plan on the AI board now starts the work Fixed: approving an agent's plan on an AI board ticket left the ticket waiting indefinitely - the assigned agent was never re-dispatched, and nothing happened until someone manually moved the ticket to "Executing". A human approval now releases the ticket to **Executing** (and "Request changes" sends it back to **Planning**), and the dispatcher picks it up on the next cycle instead of waiting out its re-dispatch backoff. Assigning a ticket to an agent right after approving its plan now behaves the same way. ## Reply to a board ticket and the agent replies back The AI board is now conversational. Commenting on a ticket assigned to an AI agent - from the ticket page, or by replying in its mirrored Slack or Microsoft Teams thread - re-dispatches the agent, which reads your comment and answers on the timeline. This works even while the ticket sits in **Awaiting review** or **Awaiting approval** (exactly where questions about a plan come up - previously a comment there was never seen), and the agent is instructed to respond and revise only, never to start executing before the plan is approved. Slack and Teams replies now also appear in the agent's work brief. Closing or cancelling a ticket now cancels any agent runs still working it, so nothing keeps burning tokens on dead work. ## Inspect and cancel AI agent runs - from MCP, the CLI, and the API Every dispatched agent run is now visible and controllable outside the portal. Three new MCP tools - `list_agent_runs`, `get_agent_run` (optionally with the session transcript tail), and `cancel_agent_run` (organisation admins only) - let the AI assistant and any MCP client watch and stop the AI team's work. The same surface ships in the CLI as the new **`ankra agents`** family (`runs`, `run`, `transcript`, `cancel`) and as PAT-authenticated API routes under `/api/v1/org/ai-agent-runs`. Cancelling a run flips its session and the platform interrupts the in-flight turn within seconds, without pausing the agent itself - and run transcripts are now readable by any organisation member through the run, not just the agent's creator. [MCP Tool Reference →](/platform/mcp-tools) ## Archived clusters are off the fleet map Fixed: archiving a cluster removed it from the cluster list but left its pin on the Dashboard's world map and, when it had no resolvable region, in the "not on the globe" tray. Archived clusters are now excluded from the map entirely - the same treatment they already get in the health summary - so the globe shows only the fleet you still run. [Dashboard →](/platform/dashboard) **June 2026** # A Go-Native Agent, Fleet Dashboard, Cloud Cost, kubectl Access, Stack Profiles, Applications, Support, and a Provider-Agnostic CLI June was a big month across the whole platform: a rebuilt cluster agent, a new fleet dashboard with cost visibility, scoped cluster access, reusable stack templates, application delivery, in-product support, and a CLI that caught up with the web UI. ## A faster, leaner cluster agent, rebuilt in Go The [Ankra Agent](/concepts/cluster-agent) has been rebuilt as a single, statically compiled Go binary and is now the production agent on the new `2.0.x` version track. It is a drop-in replacement: the same Helm chart, values, install command, NATS connectivity, and behaviour as before - resource streaming, log streaming, Helm management, add-on deployment, and `kubectl` proxying all work exactly the same. Nothing changes in how you install or operate it. What you get for free: * **A smaller footprint.** The default memory limit drops from `1536Mi` to `512Mi` and the request from `512Mi` to `256Mi`, alongside lower CPU usage and faster startup. * **Automatic rollout.** Existing clusters are upgraded to the new agent automatically by the platform, a few clusters at a time - no action required. You can still upgrade on demand from **Cluster Settings → Upgrade Agent**. * **Identical operations.** Same outbound-only connectivity, same `/livez`, `/readyz`, and `/metrics` endpoints on port `8080`, and the same `extra_env` tuning knobs. See the [Ankra Agent](/concepts/cluster-agent) docs for the full configuration reference. ## A fleet dashboard with a world map The organisation [Dashboard](/platform/dashboard) now opens on a world map of every cluster, alongside health and cost rollups. Clusters are placed by cloud region, or by the agent's public egress IP when [public IP reporting](/concepts/cluster-agent) is enabled. ## Cloud cost estimates [Cloud Cost](/platform/cloud-cost) estimates per-cluster and fleet-wide spend from your live node and volume inventory plus cloud pricing - broken down by compute, storage, network, and per-namespace allocation. Connect an [AWS or GCP credential](/platform/credentials) to get started. ## Scoped kubectl access through Ankra Grant teammates SSO-backed, scoped Kubernetes access with [Cluster Access](/guides/cluster-access), then reach the cluster with your own `kubectl` through Ankra's [API proxy](/guides/kubeconfig) - short-lived credentials, no inbound network access, works with private clusters. ## Reusable Stack Profiles Capture a working stack as a versioned, parameterised template with [Stack Profiles](/concepts/stack-profiles), then instantiate it on any cluster - with diffing, update tracking, and IaC export. ## Applications: from repo to running workload [Applications](/concepts/applications) connect an app's Git repository, generate the Dockerfile, Helm chart, and CI/CD via a pull request, then build and deploy the result as a stack. ## In-product support Raise and track [support tickets](/platform/support) without leaving Ankra - with AI triage, attachments, and threaded replies - and keep an eye on everything that needs you in your new [Activity & Inbox](/platform/activity-inbox). ## More reliable apply for large manifest stacks Applying a stack with many manifests at once - and reconciling stacks on clusters with lots of Custom Resource Definitions - is now far more reliable. Manifests are given more time to apply before they're considered stalled, and the agent no longer re-runs expensive API discovery on every single apply, so a large stack reconciles cleanly instead of failing partway through with a timeout. The change applies automatically: existing stacks pick it up on their next deploy with no action required. Any per-manifest timeout you set previously is honoured as long as it's at least the new, more generous default, and custom timeouts above the default are kept as-is. [Manifests →](/concepts/stacks#manifests) ## The Ankra CLI caught up with the platform Most of what you can do to a cluster in the web UI is now scriptable from the [Ankra CLI](/integrations/ankra-cli): * **Provider-agnostic cluster verbs** - `ankra cluster upgrade | scale | node-group | deprovision | k3s-versions` detect Hetzner, OVH, or UpCloud automatically from the cluster. * **Cluster access and kubectl** - grant scoped RBAC with `ankra cluster access` and write short-lived, SSO-backed `ankra-*` kubeconfig contexts with `ankra cluster kubeconfig add`. * **Cluster metrics** - run PromQL against a cluster's Prometheus with `ankra cluster metrics query` and `query-range`. * **Full OVH management** - stop/start, SSH access info, control-plane and node-group changes, region discovery, and node labels/taints. * **Config safety** - gate changes before they go live with `ankra cluster validate` (structure, chart existence, plaintext-secret detection) and stage them with `ankra cluster draft`. * **Live operations** - follow a deploy or reconcile with `ankra get operations --watch` and `ankra get jobs`. * **Self-update** - `ankra upgrade` updates or rolls back the CLI in place with checksum verification. See the [CLI Changelog](/integrations/ankra-cli-changelog) for the full, versioned detail. ## Removed: Add-on Subscriptions Add-on subscriptions have been retired. Add-ons are now managed directly on the cluster or stack that uses them - through your GitOps repository, `ankra cluster apply`, or the cluster editor - which keeps a single, declarative source of truth for what runs where, rather than a separate subscription that has to be kept in sync. The legacy add-on subscription endpoints now return `410 Gone`. If you relied on subscriptions, move those add-ons into the relevant stack definition; existing deployed add-ons are unaffected. **May 2026** # Editable Stopped-Cluster Topology, Self-Healing Hetzner Updates, Live Operations, and a Big CLI Wave May focused on cloud-cluster reliability - editable topology while stopped, self-healing Hetzner updates, cleaner offline reconciliation, and a live-updating operations page - alongside a large batch of CLI commands for variables, secrets, dependencies, and in-place upgrades. ## Edit cluster topology while stopped Stopped cloud clusters (Hetzner, OVH, UpCloud) now expose the full Nodes settings tab. While the cluster stays offline you can: * Switch the control plane between **1** and **3** controllers (only 1 or 3 - etcd needs an odd number of voting members to form quorum). * Change the **controller instance type**. Because the cluster is recreated fresh on next start, downgrades are safe here - there is no live disk resize. * Add and remove **node groups**, scale them, change their worker instance type, and edit labels and taints. Changes are saved against the stopped topology and applied the next time you start the cluster from the General settings tab. Topology edits now also serialize against each other and against start/stop, so two operators clicking at the same time can't end up with a half-applied change. [Cluster Settings →](/platform/cluster-nodes) ## Hetzner: Resilient Cluster Updates Hetzner cluster update jobs are now self-healing. The reconciler persists install-time addresses (`bind-address`, `advertise-address`, `node-ip`) into `/etc/rancher/k3s/config.yaml` so k3s always restarts bound to the correct private IP, and version bumps now swap the k3s binary in place instead of re-running the upstream installer - which previously rewrote the systemd unit and wiped `K3S_URL` / `K3S_TOKEN` on joined nodes. Clusters that had drifted into authentication-error loops after an upgrade recover on the next reconcile. ## Offline Cloud Clusters: Cleaner Reconciliation When a Hetzner, OVH, or UpCloud cluster is in the **stopped** state (after Delete Kubernetes), the reconciler now only acts on provider-owned resources - servers, networks, SSH keys, the k8s control plane. Addons, manifests, and other in-cluster resources are held until the cluster is back online, so you no longer see repeated failures for resources that have no API server to talk to. [Hetzner Cluster Lifecycle →](/guides/hetzner-clusters#cluster-lifecycle) ## Operations Page: Live Updates The cluster **Operations** page now refreshes itself in the background. Newly-triggered operations appear without a manual reload, and the status, duration, and finish time of in-flight operations update as they progress. The page polls faster while something is actively running and slows down once everything is idle, so you get near-real-time feedback while a deploy is happening without hammering the backend when nothing is going on. ## The Ankra CLI: variables, secrets, dependencies, and in-place upgrades A large batch of stack-management commands landed in the [Ankra CLI](/integrations/ankra-cli) this month: * **Variables at every scope** - `ankra org variables`, `ankra cluster variables`, and `ankra cluster stacks variables` give full CRUD over the values substituted into manifests and addon values (resolution is stack > cluster > organisation). * **Live-cluster SOPS** - `ankra cluster encrypt` / `decrypt` run directly against a running cluster's manifests and addon values, with no local `cluster.yaml` needed. * **Dependencies, reads, and deletes** - edit deployment-ordering parents (`--add-parent` / `--remove-parent` / `--set-parent`), print current addon values and manifest YAML, and delete a manifest from its stack. * **Surgical upgrades** - `ankra cluster addons upgrade` and `manifests upgrade` change a single Helm value or manifest field in place with helm-style `--set` (field selectors like `containers[name=app]`), `--from-file`, and `--dry-run`. * **Any organisation** - a global `--org` flag (and `ANKRA_ORG`) runs a command against any organisation you belong to without switching your selection. * **Curated AI skills** - `ankra skills install` drops Ankra's Agent Skills into Cursor or Claude Code (`--editor claude-code`), embedded in the binary so it works offline. See the [CLI Changelog](/integrations/ankra-cli-changelog) for the full, versioned detail. **April 2026** # Cluster Lifecycle: Delete Kubernetes & Scale Back Up, Multi-Stack Cloning ## Delete Kubernetes & Scale Back Up Cloud provider clusters (Hetzner, OVH, UpCloud) now support a **Delete Kubernetes** action that destroys all cloud infrastructure - servers, networks, SSH keys - while keeping the cluster registered in Ankra. The cluster moves to a stopped state and you stop incurring infrastructure charges. When you're ready to bring the cluster back, simply **add a node group**. Ankra automatically restores the full control plane infrastructure (bastion, network, SSH keys, control plane servers), installs Kubernetes, creates your new worker nodes, and begins reconciling stacks and addons. The restored cluster gets entirely fresh infrastructure with new servers and IPs. Previously existing worker node groups are not restored - the node group you add is the only worker pool. Add additional node groups as needed after the cluster is online. The **Terminate Cluster** action permanently deletes all infrastructure and removes the cluster from Ankra entirely. This is irreversible. Both actions are available in cluster **Settings → Danger Zone**. [Hetzner Cluster Lifecycle →](/guides/hetzner-clusters#cluster-lifecycle) | [Cluster Settings →](/platform/cluster-settings#danger-zone) ## Clone Multiple Stacks to Another Cluster You can now select multiple stacks and clone them all to another cluster in a single operation. On the **Stacks** page, click any stack card's avatar to start selecting, or click the **Clone Stacks** button next to the search bar to select all. A floating action bar appears showing the selection count with a **Clone to Cluster** button. The clone dialog shows a live progress bar as each stack is cloned sequentially. Name conflicts are auto-resolved with suffixes. After completion, click **Go to Target Cluster** to review the drafts. Cross-organisation cloning is fully supported. This is useful for replicating an entire cluster's stack configuration to a new environment, promoting multiple stacks from staging to production, or setting up disaster recovery clusters. [Clone Stacks Guide →](/guides/clone-stack#clone-multiple-stacks-at-once) **March 2026** # Automatic Hetzner Cloud Integration, Ingress & GitOps, Node Groups, UpCloud & OVH Cloud, AI Insights Overhaul, Kubernetes Node Cordon & Drain, Light Mode, Billing & Platform Performance ## Kubernetes Node Cordon, Uncordon & Drain The **Kubernetes → Nodes** view now supports full node lifecycle management for workload migration scenarios. Cordon, uncordon, and drain nodes from both the node detail page and the node list with multi-select bulk actions. **Cordon & Uncordon**: Mark nodes as unschedulable or restore scheduling. Available as a single-node action in the node detail header or as a bulk action by selecting multiple nodes in the list. Cordoned nodes display a "SchedulingDisabled" label. **Drain**: Cordon a node and evict all running pods in one operation - equivalent to `kubectl drain --ignore-daemonsets --delete-emptydir-data`. Real-time progress tracking shows each phase: cordoning, fetching pods, and evicting pods. Available for single nodes and in bulk. **Workload Migration**: Use cordon and drain together to safely move workloads between node groups. Cordon old nodes to stop new scheduling, drain them to evict existing pods, and let Kubernetes reschedule onto your new node group. [Kubernetes Nodes →](/platform/kubernetes-nodes) Hetzner clusters now automatically deploy the Cloud Controller Manager and CSI driver during provisioning - no manual setup. An optional ingress stack adds ingress-nginx, cert-manager, and Let's Encrypt. GitOps integration pushes cluster state to GitHub. Node groups bring per-group instance types, scaling, labels, and taints. UpCloud and OVH Cloud support across the platform. AI Insights gets a dedicated detail page with analytics dashboard, adaptive scanning, and platform-aware remediation. The Command Palette now navigates directly to every Kubernetes resource type, and the Log Viewer is rebuilt for large clusters. ## Hetzner: Automatic CCM & CSI Provisioning Hetzner clusters now automatically deploy a complete **hcloud stack** during provisioning: a dedicated `hcloud` namespace, API token secret, the [hcloud-cloud-controller-manager](https://github.com/hetznercloud/hcloud-cloud-controller-manager) (3 replicas, PDB), and the [hcloud-csi](https://github.com/hetznercloud/csi-driver) driver (3 replicas, PDB, `hcloud-volumes` default StorageClass). Previously this required manual setup via the AI Assistant or Stack Builder. Now it's fully automated using the same Hetzner API credential used to create the cluster. ## Hetzner: Optional Ingress Stack Enable `include_ingress` during cluster creation to deploy ingress-nginx with a Hetzner Load Balancer, cert-manager, and a pre-configured `letsencrypt-prod` ClusterIssuer for automatic TLS. Toggle in the UI wizard or pass `"include_ingress": true` in the API. ## Hetzner: GitOps Integration Push cluster stack state to a GitHub repository on creation. Provide `gitops_credential_name`, `gitops_repository`, and `gitops_branch` to enable version-controlled infrastructure for Hetzner clusters. ## Node Groups Per-Group Instance Types, Labels & Taints A new **Nodes** tab in cluster settings lets you manage node groups for Hetzner, OVH, and UpCloud clusters. Each node group has its own instance type, node count, Kubernetes labels, and taints giving you fine-grained control over your worker fleet. **Full Lifecycle Management**: Add, scale (0–100 nodes), upgrade instance type, edit labels/taints, and delete node groups from the UI, CLI, or API. Node groups can be defined at cluster creation time in the wizard or API. **Instance Type Upgrades**: Upgrade any group to a larger instance type. Each node is powered off, resized, and powered back on. Downgrades are not possible due to Hetzner disk resize limitations. To use a smaller type, create a new node group and delete the old one. The UI shows a confirmation dialog warning that upgrades are irreversible. **Labels & Taints**: Attach Kubernetes node labels and taints to node groups. Labels and taints are applied to all nodes in the group and can be edited at any time. Useful for workload scheduling with nodeSelectors, affinities, and tolerations. **No Online Requirement**: Node group operations work regardless of cluster state. No need to wait for the cluster to be online. **CLI Support**: New `ankra cluster {hetzner|ovh|upcloud} node-group` commands for list, add, scale, upgrade, and delete. [Hetzner Node Groups →](/guides/operate-ankra-managed-clusters#node-groups) | [Cluster Settings →](/platform/cluster-nodes) ## Hetzner: Multi-SSH-Key Support & Access Settings Attach multiple SSH key credentials to Hetzner clusters at creation or post-creation. A new **Access** tab in cluster settings provides copy-pasteable SSH jump and kubectl port-forward commands, a network topology view, and SSH key management. [Hetzner Clusters Guide →](/guides/hetzner-clusters) ## UpCloud Clusters Provision and manage Kubernetes clusters on UpCloud with managed SDN Routers and NAT Gateways for private networking. **Full Lifecycle Management**: Create, scale, upgrade, and deprovision UpCloud clusters through the UI, CLI, or API. Clusters deploy with a router, private network, NAT gateway, bastion server, and k3s distribution. **Zone Support**: Deploy to any UpCloud zone including Helsinki (Finland), Frankfurt (Germany), Chicago (United States), Amsterdam (Netherlands), London (United Kingdom), Singapore, Sydney (Australia), and Warsaw (Poland). **Worker Scaling**: Scale worker nodes between 1 and 10. Scaling up provisions new instances and installs Kubernetes. Scaling down removes workers from the highest index. **Kubernetes Upgrades**: Upgrade k3s versions across all nodes with one command. Control planes are upgraded first, then workers. **DAG-Based Deprovisioning**: Cluster deletion creates a tracked operation with individual delete jobs that run in dependency order. Multiple server deletions run in parallel, reducing deprovision time. [UpCloud Clusters Guide →](/guides/upcloud-clusters) ## UpCloud API Credentials Securely store UpCloud API credentials (API token) with automatic validation against the UpCloud API. **Credential Validation**: Credentials are validated on creation by calling the UpCloud API. Clear error messages for invalid tokens (401) and permission issues. **SSH Key Support**: Create and manage SSH key credentials for UpCloud cluster provisioning. Generate new keypairs or bring your own public key. [Credentials Guide →](/platform/credentials/upcloud) ## OVH Cloud Clusters Provision fully managed Kubernetes clusters on OVH Cloud with configurable control planes, workers, and networking. **Full Lifecycle Management**: Create, scale, upgrade, and deprovision OVH clusters through the UI, CLI, or API. Clusters deploy with a private network, gateway for SSH access, and k3s distribution. **Region Support**: Deploy to any OVH Cloud region including Gravelines (France), Strasbourg (France), Beauharnois (Canada), Warsaw (Poland), Frankfurt (Germany), London (UK), Singapore, and Sydney. **Worker Scaling**: Scale worker nodes between 1 and 10. Scaling up provisions new instances and installs Kubernetes. Scaling down removes workers from the highest index. **Kubernetes Upgrades**: Upgrade k3s versions across all nodes with one command. Control planes are upgraded first, then workers. [OVH Clusters Guide →](/guides/ovh-clusters) ## OVH API Credentials Securely store OVH Cloud API credentials (application key, application secret, consumer key, and project ID) with automatic validation against the OVH API. **Credential Validation**: Credentials are validated on creation by calling the OVH API. Clear error messages for permission issues (403) and invalid credentials (401). **SSH Key Support**: Create and manage SSH key credentials for OVH cluster provisioning. Generate new keypairs or bring your own public key. [Credentials Guide →](/platform/credentials/ovh) ## AI Insights Overhaul A major upgrade to Proactive AI Insights with a dedicated detail page, analytics dashboard, advanced filtering, adaptive scanning, and platform-aware remediation. **Insight Detail Page**: Each insight now has a full-page view with four tabs Overview (root cause analysis with summary, severity, confidence score, affected resources, and conversation starters for AI chat), Remediation (copy-pasteable commands and platform-aware actions), Health (snapshot and anomaly data at time of detection), and History (previous resolutions with effectiveness tracking). Navigate to related Kubernetes resources directly from the affected resources list. **Analytics Dashboard**: New analytics tab with configurable time ranges (7, 30, or 90 days). Tracks mean time to resolution (MTTR), shows severity trends by day, breaks down issues by namespace and category, and indicates whether cluster health is improving, degrading, or stable. **Dashboard View**: A new dashboard tab on the insights page with a severity donut chart, mini trend chart, and category breakdown at a glance. **Advanced Filtering**: Filter insights by namespace, category, and date range. Three view modes cards, compact list, and grouped (by category or namespace). Root cause summary shown inline on insight cards. **Related Insights**: Insights sharing the same namespace or category are linked on the detail page, helping you spot patterns across related issues. **Adaptive Scanning**: The maintenance scheduler now adapts scan frequency based on cluster health clusters with critical issues are scanned every 60 seconds, clusters with active issues every 3 minutes, and healthy clusters every 10 minutes. **Platform-Aware Remediation**: AI analysis now suggests platform-native actions (stack changes, add-on configuration, resource navigation) alongside kubectl commands. Remediation commands and platform actions are stored per-insight and displayed on the detail page. **Resolution Tracking**: Resolving an insight now captures resolution type, resolution context, and a health snapshot at resolution time. When an issue recurs, previous resolutions include effectiveness data so you can see if a past fix held. **RAG Learning Loop**: Marking an insight as "helpful" indexes its root cause analysis and recommendations for RAG, improving future AI analysis quality. **Keyboard Shortcuts**: Navigate and manage insights entirely from the keyboard `j`/`k` to navigate, `Enter` to open detail, `p` to quick peek, `a` to acknowledge, `r` to resolve, `d` to dismiss, `c` to ask AI, `/` to search, `1`–`4` to switch tabs, `v` to cycle view mode, and `?` for help. [AI Insights Docs →](/platform/ai-insights) ## CLI v0.2.0 Helm management, Kubernetes resource access, direct cluster selection, and client hardening. * `ankra cluster helm releases` / `ankra cluster helm uninstall` - Manage Helm releases in the cluster * `ankra helm registries` / `ankra helm credentials` - Manage Helm registries and credentials * `ankra cluster pods` / `ankra cluster resources` / `ankra cluster logs` - Access Kubernetes resources and stream pod logs * `ankra cluster select ` - Select a cluster directly by name without the interactive picker * `ankra cluster info [name]` - Show cluster details (defaults to selected cluster; replaces `cluster get`) * HTTP client timeouts, response body limits, URL encoding, config file permission hardening [CLI Changelog →](/integrations/ankra-cli-changelog) | [Ankra CLI →](/integrations/ankra-cli) ## CLI v0.1.129 Node group management commands and new UpCloud/OVH commands for cluster and credential management. * `ankra cluster node-group` - List, add, scale, upgrade, and delete node groups for Hetzner, OVH, and UpCloud * `ankra credentials upcloud` / `ankra cluster upcloud` - UpCloud credential and cluster management * `ankra credentials ovh` / `ankra cluster ovh` - OVH credential and cluster management [CLI Changelog →](/integrations/ankra-cli-changelog) | [Ankra CLI →](/integrations/ankra-cli) ## Node Group API Endpoints Available for all three cloud providers (Hetzner, OVH, UpCloud): | Endpoint | Method | Description | | - | - | - | | `/api/v1/clusters/{provider}/{id}/node-groups` | GET | List node groups | | `/api/v1/clusters/{provider}/{id}/node-groups` | POST | Add a node group | | `/api/v1/clusters/{provider}/{id}/node-groups/{name}/scale` | PUT | Scale a node group | | `/api/v1/clusters/{provider}/{id}/node-groups/{name}/instance-type` | PUT | Upgrade instance type | | `/api/v1/clusters/{provider}/{id}/node-groups/{name}/labels` | PUT | Update labels | | `/api/v1/clusters/{provider}/{id}/node-groups/{name}/taints` | PUT | Update taints | | `/api/v1/clusters/{provider}/{id}/node-groups/{name}` | DELETE | Delete a node group | [Hetzner Node Groups →](/guides/operate-ankra-managed-clusters#node-groups) ## UpCloud API Endpoints Full REST API coverage for UpCloud clusters and credentials: | Endpoint | Method | Description | | - | - | - | | `/api/v1/clusters/upcloud` | POST | Create an UpCloud cluster | | `/api/v1/clusters/upcloud/{id}` | DELETE | Deprovision a cluster | | `/api/v1/clusters/upcloud/{id}/worker-count` | GET | Get worker count | | `/api/v1/clusters/upcloud/{id}/scale-workers` | POST | Scale workers | | `/api/v1/clusters/upcloud/{id}/k8s-version` | GET | Get Kubernetes version | | `/api/v1/clusters/upcloud/{id}/upgrade-k8s-version` | POST | Upgrade Kubernetes version | | `/api/v1/credentials/upcloud` | GET | List UpCloud credentials | | `/api/v1/credentials/upcloud` | POST | Create an UpCloud credential | | `/api/v1/credentials/upcloud/ssh-keys` | GET | List SSH key credentials | | `/api/v1/credentials/upcloud/ssh-key` | POST | Create an SSH key credential | [API Reference →](/api-reference/introduction) ## OVH API Endpoints Full REST API coverage for OVH clusters and credentials: | Endpoint | Method | Description | | - | - | - | | `/api/v1/clusters/ovh` | POST | Create an OVH cluster | | `/api/v1/clusters/ovh/{id}` | DELETE | Deprovision a cluster | | `/api/v1/clusters/ovh/{id}/worker-count` | GET | Get worker count | | `/api/v1/clusters/ovh/{id}/scale-workers` | POST | Scale workers | | `/api/v1/clusters/ovh/{id}/k8s-version` | GET | Get Kubernetes version | | `/api/v1/clusters/ovh/{id}/upgrade-k8s-version` | POST | Upgrade Kubernetes version | | `/api/v1/credentials/ovh` | GET | List OVH credentials | | `/api/v1/credentials/ovh` | POST | Create an OVH credential | | `/api/v1/credentials/ovh/ssh-keys` | GET | List SSH key credentials | | `/api/v1/credentials/ovh/ssh-key` | POST | Create an SSH key credential | [API Reference →](/api-reference/introduction) ## Command Palette Direct Links to All Kubernetes Resources The Command Palette (`⌘+K` / `Ctrl+K`) now navigates directly to every Kubernetes resource type in your cluster not just Ankra pages. Type any resource kind and jump straight to it. **33 Kubernetes resource types** organized by category: Workloads (Pods, Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs, PodDisruptionBudgets, HorizontalPodAutoscalers), Networking (Services, Ingresses, Ingress Classes, Endpoints, NetworkPolicies, CSI resources), Configuration (ConfigMaps, Secrets, Namespaces, Events), Storage (PersistentVolumes, PVCs, Storage Classes, VolumeAttachments), RBAC (Roles, RoleBindings, ClusterRoles, ClusterRoleBindings, ServiceAccounts, LimitRanges), and more (CRDs, Helm Releases, Nodes). **New cluster pages**: Logs, Metrics, Resources, General Settings, and GitOps Settings are now accessible from the palette. **New actions**: Base64 Encode/Decode utilities, Keyboard Shortcuts reference, Documentation link, and Switch Organisation. [Command Palette →](/platform/command-palette) ## Log Viewer Redesign Rebuilt the Log Viewer for large clusters and high-volume streams. **Scalable Pod Loading**: Pods are fetched on-demand per namespace when you select a workload, replacing the previous 500-pod cap that caused "0 pods" for large clusters. **Accurate Matching**: Pod-to-workload matching now uses Kubernetes label selectors instead of name prefix heuristics. **Scroll Stability**: Scrolling up to investigate older logs now holds your position even under high-throughput streams. Auto-follow only re-engages on explicit user action. **Live Filtering**: Search and log level filters apply to incoming lines in real-time, not just existing logs. **Performance**: Batched rendering (150ms flush interval) with virtualized display handles tens of thousands of log lines without UI freezes. [Log Viewer Docs →](/platform/logs) ## Light Mode Full theme customization with Light, Dark, and System modes - the platform is now suitable for bright or dark developer caves. Set your preference from **Profile → Appearance** or use the sidebar theme toggle to cycle modes instantly. Your choice syncs across all devices. [User Profile →](/platform/user-profile) ## Billing & Subscription Management Stripe integration with vCPU-based metering, automated invoice generation, and subscription auto-provisioning for new organisations. The billing pipeline covers plan management, usage tracking, invoice history, and Stripe webhook handling. A billing analytics dashboard provides visibility into resource consumption and cost trends. ## Distributed Tracing OpenTelemetry tracing across the API, maintenance scheduler, and work scheduler services. Request-level observability with trace propagation, structured logging integration, and circuit breaker improvements provide end-to-end visibility into platform operations. ## Registry Performance & Reliability Registry listing, detail views, and sync operations significantly optimised. Cache invalidation after frontend changes, Harbor edge case handling, and backoff logic for failing registries improve reliability across all registry types. ## Database & Platform Performance Database index audit with unused index removal via pg\_stat\_statements, improved connection pool management, and reduced database pressure under load. Listing performance improvements across clusters, stacks, and AI insights result in faster page loads throughout the platform. ## Helm Charts Version Tracking New charts version comparison and a clusters tab showing which clusters run which chart versions, making it easier to track Helm chart drift across your fleet. **February 2026** # Hetzner Cloud Provisioning, AI Goes Agentic, Full Observability & Secrets at Scale Hetzner Cloud provisioning lands across the platform, AI goes agentic with 58 tools, full observability arrives with log streaming and pod terminals, and secrets management scales up with SOPS and variables. ## Hetzner Cloud Provision Clusters from Ankra Ankra now provisions and manages Hetzner Cloud clusters end-to-end. Create k3s clusters across five Hetzner locations (Falkenstein, Nuremberg, Helsinki, Ashburn, Hillsboro), scale workers, and upgrade Kubernetes all from the dashboard, CLI, or API. The full provisioning pipeline handles networking, SSH keys, bastion servers, control planes, workers, k3s installation, and Ankra Agent setup automatically. Deprovision in one click when you're done. [Hetzner Clusters Guide →](/guides/hetzner-clusters) ## Hetzner Cluster Provisioning in the Dashboard A guided wizard walks you through creating a Hetzner cluster select credentials, pick a location, choose server types (cost-optimized, regular, or dedicated vCPU), set control plane and worker counts, and launch. A live provisioning progress view tracks every step from network creation to Ankra Agent installation. Once running, scale workers and upgrade k3s versions directly from cluster settings. ## Hetzner CLI Commands Full Hetzner lifecycle from your terminal. `ankra cluster hetzner create` provisions a cluster with configurable network ranges, server types, and k3s versions. Scale workers with `ankra cluster hetzner scale`, upgrade Kubernetes with `ankra cluster hetzner upgrade`, and tear everything down with `ankra cluster hetzner deprovision`. Manage Hetzner API tokens and SSH keys via `ankra credentials hetzner` including automatic keypair generation. [CLI Reference →](/integrations/ankra-cli) ## Hetzner REST API Every Hetzner operation is available via the REST API with Bearer token authentication. Create and deprovision clusters, scale workers, upgrade k3s, and manage Hetzner credentials and SSH keys programmatically. Integrate Hetzner cluster provisioning into your CI/CD pipelines or custom tooling. [API Reference →](/api-reference/introduction) ## Sandbox Cluster Try Ankra Instantly No Kubernetes cluster? No problem. Spin up a sandbox cluster in one click and explore the full Ankra experience: pre-populated nodes, workloads, services, stacks, and live-looking metrics. It's read-only, always online, and requires zero setup. The fastest way to see what Ankra can do. [Sandbox Docs →](/guides/cluster-sandbox) ## AI Agentic Mode & 58 Tools The AI Assistant is now a fully autonomous agent. It reads your cluster state, chains up to 10 tool calls per response, and proposes infrastructure changes with a built-in confirmation system. 58 tools span Kubernetes read/write, stack management, and addon lifecycle. [Learn more →](/platform/ai-assistant) ## AI Incident Reports & Root Cause Analysis When an alert triggers, Ankra automatically generates a detailed AI incident report with root cause analysis, severity assessment, affected resources, and actionable recommendations. Reports are delivered to Slack, Teams, PagerDuty, Discord, and Opsgenie alongside alert notifications. [Configure Alerts →](/guides/alerts) | [AI Incidents →](/platform/ai-incidents) ## Alerting Built Into All Ankra Resources Alerts now monitor every resource type clusters, stacks, addons, manifests, and GitHub repositories. Define custom rules with AND/OR logic, set severity levels, and get notified via six built-in integrations plus generic webhooks. [Configure Alerts →](/guides/alerts) ## Log Explorer Stream logs in real-time from up to 20 pods simultaneously with full-text and regex search, auto-detected log levels, adjustable display settings, and export to clipboard or file. [Learn more →](/platform/logs) ## Terminal to Any Pod Open an interactive terminal directly into any running pod container from the dashboard. Choose your shell, select the target container, and debug issues without leaving the browser. ## Stack Cloning Clone any stack between clusters with automatic name conflict resolution, SOPS-encrypted value handling, and parent relationship preservation. Available in the dashboard and via the CLI. [Clone Stack Guide →](/guides/clone-stack) | [Clone Cluster Guide →](/guides/clone-cluster) ## SOPS Integration with ArgoCD End-to-end secret encryption using SOPS with AGE keys, fully integrated with ArgoCD. Encrypt specific YAML keys while keeping the rest readable. Automatic decryption during GitOps sync no manual steps. [SOPS Guide →](/guides/sops) ## Variables: Org, Cluster & Stack Levels Define reusable variables at three levels with automatic precedence resolution. Use `${{ ankra.variable_name }}` in manifests and Helm values. Mark variables as secrets for masked display in the UI. [Variables Guide →](/concepts/variables) ## Metrics Revamp Redesigned metrics with area charts for CPU and memory by node, network RX/TX rate charts, disk I/O, pod restarts by namespace, summary cards, and AI-powered health insights surfaced in dashboard widgets. [Cluster Metrics →](/platform/cluster-metrics) | [Prometheus Integration →](/integrations/prometheus) ## Multi-Select Delete for Kubernetes Resources Bulk select and delete Kubernetes resources across 20+ resource types with checkbox selection, confirmation dialog, sequential deletion with progress tracking, and per-item error reporting. ## CLI v0.1.125 & API Updates New SOPS encryption commands replace the old `ankra cluster sops` command. Granular encrypt/decrypt for manifests and addon values. Full REST API with Bearer token auth, alerts API with AI analysis endpoints, and interactive OpenAPI docs. [Ankra CLI →](/integrations/ankra-cli) | [API Reference →](/api-reference/introduction) **December 2025** # Dashboard V2 A completely redesigned dashboard with real-time health monitoring, AI insights, and guided onboarding. ## Health Overview Grid Visual cluster cards sorted by health status with critical issues surfaced first. **Status Indicators**: Color-coded health status (healthy, warning, critical, offline) with animated pulse for critical issues. **Resource Breakdown**: Running, pending, and failed resource counts with visual progress bars. **Quick Actions**: Create stacks directly from cluster cards. GitOps connection and agent update indicators visible at a glance. ## Operations & Alerts **Alerts Summary**: Real-time alert statistics with severity breakdown and quick navigation to alert management. **Operations Activity**: Timeline of recent operations across all clusters with status, progress, and timestamps. ## GitOps & AI Insights **GitOps Sync Status**: Repository connection overview showing connected vs disconnected clusters with direct links to configuration. **AI Insights**: Summary of recent AI-powered analyses including incident investigations and cluster health assessments. ## Stacks & Registries **Stacks Overview**: Stack deployment statistics per cluster with status indicators. **Helm Repositories**: Connected registry summary with sync status and navigation to registry management. **Recently Accessed**: Quick access to your most recently viewed resources across all clusters. ## Onboarding Experience **Guided Setup**: Step-by-step checklist for new users with video tutorials and documentation links. **Progress Tracking**: Floating progress widget showing completion percentage. Dismissible once core setup is complete. **Quick Actions Bar**: Common actions accessible from the dashboard including import cluster and command palette shortcuts. ## Stack Builder Improvements **Node Position Management**: Improved automatic layout calculations and smoother drag-and-drop interactions. **Performance**: Faster stack loading with reduced API calls and improved caching. **Bug Fixes**: Fixed manifest rename inconsistencies and improved Bill of Materials calculations. ## Performance & Stability **Backend Optimizations**: Optimized database queries and reduced API response times. **WebSocket Support**: Real-time updates via WebSocket for faster event propagation. **Resource Limits**: Increased API limits for clusters with larger resource counts. ## AI Assistant **Mobile View**: Improved responsive layout and touch interactions for mobile devices. ## Agent Updates **ArgoCD SSL Handling**: Fixed SSL verification issues and improved certificate handling. **Label Selector**: Enhanced resource filtering and ArgoCD relationship handling. **November 2025** # AI Takes Center Stage This month we've revamped our documentation to put the AI Assistant front and center, added comprehensive guides for building production stacks, and expanded integrations. ## Stack Builder The visual Stack Builder is now a first-class citizen with comprehensive documentation. **ReactFlow Canvas**: Drag-and-drop interface powered by ReactFlow for composing add-ons and manifests into deployable stacks. **Visual Dependency Management**: Connect components on the canvas to define deployment order. Arrows show the flow source deploys before target. **Draft Stacks**: Work-in-progress configurations with auto-save, validation, and preview before publishing. **Node Operations**: Click any component to edit values, view deployment status, or configure namespaces. Safe node deletion prevents dangling edges. **Stack Cloning**: Clone any Stack to quickly create variations. Deploy the same Stack across dev, staging, and production with environment-specific tweaks. **AI-Powered Assistance**: Describe what you need and the AI recommends add-ons, suggests configurations, and provides guidance on dependencies. **CLI Integration**: Manage Stacks from the terminal with the Ankra CLI: * `ankra get stacks` List all stacks in the active cluster * `ankra get stacks ` View stack details with manifests and addons * `ankra clone ` Clone stacks between clusters * `ankra applyf stack.yaml` Apply stack configurations Perfect for CI/CD pipelines and automation. [Explore Stack Builder →](/concepts/stacks) ## Insight Metrics Real-time cluster metrics powered by Prometheus integration. **Resource Usage Cards**: At-a-glance CPU and memory utilization across your cluster with current values and totals. **Time Series Charts**: Interactive graphs for CPU by node, memory by node, disk I/O, and network traffic (RX/TX). **Pod Restarts**: Bar chart showing restart counts by namespace to quickly identify unstable workloads. **Configurable Time Ranges**: View metrics from 15 minutes to 7 days with automatic refresh intervals. **Node-Level Drill-Down**: Click any series to isolate and focus on specific nodes. ## Helm Charts & Registries Completely revamped Helm chart discovery and registry management. **Chart Explorer**: Browse, search, and filter Helm charts from all connected registries. View chart metadata, versions, and dependencies. **HTTP & OCI Registries**: Full support for both HTTP-based Helm repositories and OCI registries (GHCR, GAR, ECR, ACR, Docker Hub, Harbor). **Auto-Sync**: Registries sync automatically to keep chart catalogs up to date. Configure sync intervals per registry. **Credential Management**: Securely store and manage registry credentials with support for tokens, basic auth, and cloud provider authentication. [Explore Helm Charts →](/platform/addon-catalog) | [Registry Setup →](/integrations/helm-registries) ## Alerting New alerting system to stay on top of cluster issues. **Slack Webhooks**: Send alerts directly to Slack channels via incoming webhooks. Configure per-cluster or organisation-wide. **Alert Rules**: Define custom alert conditions based on cluster events, resource states, and thresholds. **Event-Driven Notifications**: Get notified on deployments, failures, scaling events, and health changes. [Configure Alerts →](/guides/alerts) ## AI Assistant Enhancements The AI Assistant is now the centerpiece of the Ankra experience. **Page-Aware Context**: The AI automatically knows what you're looking at. Open a pod and it already has the logs, manifest, events, and related resources. No copy-pasting or explaining. **Incident Triangulation**: Root cause analysis across multiple data sources: * Container logs with error detection * Prometheus metrics and anomalies * Kubernetes manifests and configurations * Stack deployment history and changes * Resource relationships and dependencies **Stack Building**: Describe infrastructure in natural language and the AI helps you build it: * *"I need a monitoring stack with Prometheus and Grafana"* * AI recommends add-ons, suggests values, and explains dependencies * You add the components to your Stack based on the recommendations * Review and deploy with one click **Unified Context Layers**: The AI combines all context for comprehensive understanding: | Layer | What AI Sees | | - | - | | Logs | Container stdout/stderr, error patterns | | Metrics | CPU, memory, network, custom metrics | | Manifests | Current and desired state, diffs | | Events | Kubernetes events, warnings, errors | | History | Deployment timeline, who changed what | **New Shortcut**: Press `⌘+J` (Mac) or `Ctrl+J` (Windows/Linux) from anywhere. **Claude Opus 4.5**: The AI Assistant now runs on Claude Opus 4.5 Anthropic's most capable model by default. No configuration needed. [Learn more →](/platform/ai-assistant) ## New Guides **Build a Monitoring Stack**: Step-by-step guide to deploy kube-prometheus-stack with Loki for logs. [Read the guide](/guides/monitoring-stack) **Cloudflare Tunnel**: Expose services without public IPs or open firewall ports. [Read the guide](/guides/cloudflare-tunnel) ## New Integrations **Prometheus**: Connect your Prometheus instance as a metrics data source. Powers Insight Metrics and AI analysis. [Learn more](/integrations/prometheus) **Helm Registries**: Expanded documentation for HTTP and OCI registries with provider-specific setup. [Learn more](/integrations/helm-registries) ## New Documentation **Kubernetes Resources**: Complete pages for Workloads, Networking, Storage, Configuration, and RBAC. **Command Palette Guide**: Full reference for `⌘+K` navigation, shortcuts, and commands. [Learn more](/platform/command-palette) ## Documentation Improvements **Restructured Navigation**: Reorganized into logical groups with AI Assistant prominently featured in Get Started. **Consistent Formatting**: Standardized formatting across all pages. **June 2025** # Supercharging Stack Builder & Workflows! Safety, speed, visibility & brand-new integrations-everything you need to build, manage, and share infra. ## Stack Builder **Safe Node Deletion**: Prevent new nodes linking to those being removed-no dangling edges or race conditions. **Drag-and-Drop Layout**: Manually reposition nodes, then snap them back into a tidy grid with "Reorder." **Reconcile Quick-Jump**: Click a resource's badge to dive directly into the underlying operation. **Unsaved-Changes Prompt**: Real-time draft indicators highlight pending edits-and you'll be warned before leaving. ## Agent & Import Flow **Upgrade Available**: Get notified when a new agent version is available-no more missing out on improvements. **Pending-Upgrade Fix**: Agents no longer stall in "pending-upgrade"-rollbacks are a thing of the past. ## AI & Automation **In-Context AI Chat**: AI Assistant now lives on every page-get guidance right where you work. It's like having a personal DevOps expert at your fingertips. **Chat History**: View past conversations in the AI Assistant sidebar-no more lost context. **Anthropic Models**: Use some of the most advanced AI models available today. Choose between: Claude 3.5 Haiku, Claude 3.7 Sonnet, Claude Sonnet 4 and Claude Opus 4. ## Resource & CRD Management **CRD Explorer**: Browse all CRDs in-app, with filters to zero in on exactly what you need. **IaC Source View**: Click "View Source" on any generated resource to inspect the GitHub-hosted files. ## Search & Shareability **Shareable URLs**: Bookmark or share any filtered list-teammates see exactly what you see. **Enter-Key Power-Up**: Press Enter in ⌘+K to immediately run searches or actions. ## Integrations & Ecosystem **Terraform Provider (Alpha)**: Define clusters, stacks, and add-ons as code-live on Terraform & OpenTofu. [Read Docs](https://docs.ankra.ai/integrations/terraform) **Monitoring Stack Walk-Through**: Step-by-step video guide with GitOps examples. [Watch Video](https://www.youtube.com/watch?v=__EQEh0GZAY\&t=4s) **GitOps Enhancements**: Bidirectional syncing & auto-cleanup of stale files. [Learn More](https://docs.ankra.ai/essentials/cluster-gitops-multiple) **Ankra CLI**: Scriptable imports, builds, and add-on management right in your terminal. [Get the CLI](https://docs.ankra.ai/integrations/ankra-cli) ## Usability & Productivity **Sidebar State Saver**: Hide or show the sidebar without losing your custom panel layout. **Download & Copy**: One-click download or copy of cluster add-on configs for backups and sharing. **Improved Error Messages**: Actionable hints (e.g. invalid URL format) instead of cryptic failures. **OCI-Backed Add-on Sources**: Support for OCI Helm charts now live-private & credential-based OCI coming next month. **May 2025** # Stack Builder, Supercharging Infrastructure! The new Stack Builder update is packed with features to speed up your workflows and make infrastructure building easier than ever. Here's what's live: ## Stack Builder: Go from 0 to 100 in 2.3s **Unified Stacks**: Build with manifests and add‑ons side by side. Apply your organisation's custom configs and get add‑ons working out of the box. **Cluster Manifest List & View**: Instantly browse and inspect all manifests in your cluster. **Edge Management**: Delete and manage edges in your stack. Nodes now require at least one incoming edge for safety. **Live Node Status**: See the status of each node directly from the builder review. Watch it go from planned to live from within the interactive builder. **Cluster Stack Deletion**: Instantly remove entire stacks when you need to reset. ## Repository & GitOps Integration **Repo Sync**: Keep your infrastructure and code always in sync. **Create GitHub Repo**: Launch new repositories directly from Ankra, ready for GitOps. **GitHub Credential Banner**: Get notified if your integration needs attention. Every cluster needs a GitHub repo for full GitOps integration and to build your first stack. ## AI & Automation **AI Stack Recommendations**: Get AI-powered recommendations for building infrastructure stacks. **AI Assistant**: When you select an add‑on or manifest, the assistant guides you through creating and understanding manifests. ## Team Collaboration **Multi‑Org & Multi‑User**: Easily invite your team and collaborate across multiple organisations. ## Add‑on Experience **Stack Add‑on Cards**: Visual add‑on cards for fast browsing. **Add‑on Listing Toggle**: Switch between only subscribed add‑ons and all available for easier management. ## Usability & Productivity **LocalStorage Drafts**: Your changes are auto‑saved as you work. **Background Stack Refresh**: See instant, real‑time updates to your stacks. **Parent Validation**: Prevents duplicate names and parent mismatches automatically. **Live Documentation**: Comprehensive documentation is now available across the platform. **March–April 2025** # Enhanced import cluster flow with automated drafts, agent improvements, and expanded Kubernetes resource insights ## Upgraded Import Cluster Flow **Cluster Drafts Saved Automatically**: Drafts persist in your browser session so you never lose your work if you navigate away. Resume drafts by clicking the island at the top center of any page. **Real‑Time Stack Building**: When you have a draft, click the floating Add button in the top "island" panel to watch your full YAML manifest assemble live. Once you're done looking around the add‑ons and profiles, click the import icon to return to the draft and submit it for completion to get the command and import your cluster. ## Import Cluster Agent Enhancements **Seamless Local‑to‑Cloud Parity**: Use the `helm install ankra-agent` command from the import cluster on your laptop or on your Kubernetes clusters. **Expanded Resource Insights**: Live access to Kubernetes resources. **ARM & macOS Native Support**: Prebuilt binaries for Apple Silicon (M1/M2) and generic ARM64 servers. Easy helm install from the import cluster: `helm install ankra-agent`. ## Roadmap Preview: Kubernetes‑Native Integrations Upcoming support for streaming capabilities that will allow real‑time exploration of the Kubernetes environment: * Expand ArgoCD integration to simplify troubleshooting * Log streaming of pods * Creation of Kubernetes resources with add‑ons ## Cluster Add‑on Management **Add Multiple Add‑ons**: When you click add‑ons of an existing cluster, you can now add a stack of add‑ons in a single form. Just keep selecting add‑ons from the options. ## Bug Fixes & Improvements **General Fixes**: Resolved an issue where multi‑line strings were not being preserved. **Quality of Life Improvements**: General improvements in the UI for a better user experience. # Add-on Settings Source: https://docs.ankra.ai/concepts/addon-settings Configure sync policies, retry behaviour, health checks, and ignore rules for your add-ons. Once an add-on is deployed, you decide how Ankra keeps it that way: whether drift is corrected automatically, what is pruned, how failures are retried, and which fields to ignore. This page lists every one of those settings and what it does. They apply to standalone add-ons and to add-ons inside Stacks. ## Overview On the native engine, the default for new clusters, the Ankra agent applies these settings with Helm. A cluster still on ArgoCD uses the same fields for the add-on's ArgoCD Application. The [Deployment Engines](/concepts/deployment-engines#feature-parity) parity table shows how each engine honours them. You can change the settings from: * **Stack Builder** - click any add-on and open the **Settings** tab * **Standalone Add-ons** - open the add-on and click **Settings** Changes made in the Stack Builder are applied when you save the stack. Changes to standalone add-ons are applied immediately. *** ## Settings Profiles Profiles are pre-configured bundles of settings optimized for common scenarios. Select a profile to quickly apply recommended values, or choose **Custom** to configure each setting individually. | Profile | Automated Sync | Auto Prune | Self Heal | Health Check | Revision Limit | Best For | | - | :-: | :-: | :-: | :-: | :-: | - | | **Default** | On | On | On | Off | 10 | Most add-ons | | **Development** | On | On | On | Off | 5 | Fast iteration, dev/staging clusters | | **Production Critical** | Off | Off | Off | On | 20 | Critical workloads requiring manual control | | **Stateful** | On | Off | On | Off | 10 | Databases, message queues, stateful apps | Changing any individual setting automatically switches the profile to **Custom**. *** ## Sync Policy The sync policy determines how Ankra responds to changes in the desired state and to drift in your cluster. ### Automated Sync When enabled, your add-on is synced automatically whenever its desired state changes. When disabled, you must trigger syncs manually. ### Auto Prune When enabled alongside Automated Sync, Kubernetes resources that are no longer part of the add-on's desired state are deleted automatically. This keeps your cluster clean but means removing a resource from your stack will delete it from the cluster. > [!WARNING] > > Auto Prune can delete resources that were removed from your Git repository. Make sure you intend to remove them before enabling this option. ### Self Heal When enabled alongside Automated Sync, manual changes made directly to the cluster are reverted automatically. If someone modifies a resource with `kubectl`, the drift is detected and the resource is restored to match the desired state. *** ## Sync Options Sync options are flags that control specific behaviours during synchronization, named after their ArgoCD equivalents. You can select multiple options. > [!NOTE] > > Sync options apply only to add-ons on clusters that still use the [ArgoCD engine](/concepts/deployment-engines). The native engine, the default, does not read them: it always creates the target namespace, and it handles drift with the settings on this page that it does honour - Auto Prune, Self Heal, the sync window, Ignore Differences, the retry policy, the revision history limit and rollback on failure. | Option | Description | | - | - | | **Create Namespace** | Automatically create the target namespace if it does not exist | | **Server-Side Apply** | Use Kubernetes server-side apply instead of client-side. Recommended for large resources and CRDs | | **Apply Out-of-Sync Only** | Only apply resources that are out of sync, skipping unchanged resources. Reduces sync time | | **Respect Ignore Differences** | Honor ignore difference rules during sync. Automatically enabled when ignore rules are present | | **Prune Propagation: Foreground** | Wait for dependent resources to be deleted before removing the parent | | **Prune Propagation: Background** | Delete the parent immediately and let Kubernetes garbage-collect dependents | | **Prune Propagation: Orphan** | Remove the parent but leave dependent resources in place | | **Prune Last** | Prune resources only after all other sync operations complete | | **Replace** | Use `kubectl replace` instead of `kubectl apply`. Useful when apply fails due to field conflicts | | **Fail on Shared Resource** | Fail the sync if a resource is already managed by another ArgoCD Application | > [!TIP] > > **Server-Side Apply** and **Create Namespace** are enabled by default in most profiles. Server-Side Apply is recommended because it handles large objects and field conflicts better than client-side apply. *** ## Health & Revisions ### Strict Health Check When enabled, Ankra requires **all** resources created by the add-on to report a healthy status before considering the deployment successful. This is useful for production-critical add-ons where partial deployments are unacceptable. When disabled (default), the sync is considered successful as soon as resources are applied, regardless of their health status. ### Revision History Limit Controls how many previous revisions are retained (1-100; Helm's `--history-max` on the native engine). Higher values consume more storage but make it easier to audit changes and understand deployment history. The default is **10**. *** ## Retry Policy The retry policy controls automatic retry behaviour when a sync operation fails. | Field | Description | Default | | - | - | - | | **Retry Limit** | Maximum number of retry attempts (0-100) | 5 | | **Backoff Factor** | Multiplier applied to delay between retries (1-10) | 2 | | **Initial Delay** | Wait time before the first retry (e.g. `5s`, `1m`) | 5s | | **Max Delay** | Maximum wait time between retries (e.g. `3m`, `10m`) | 3m | Retries use exponential backoff: each retry waits longer than the previous one, up to the max delay. For example, with the defaults, retries happen at approximately 5s, 10s, 20s, 40s, and 80s (capped at 3m). *** ## Sync Window Sync windows restrict when your add-on may be synchronized. This is useful for enforcing change-management policies, such as only allowing deployments during business hours or maintenance windows. | Field | Description | Example | | - | - | - | | **Schedule** | Cron expression defining when the window opens | `0 22 * * *` (daily at 10 PM) | | **Duration** | How long the window stays open | `1h`, `30m`, `2h` | | **Timezone** | Timezone for the cron schedule | `UTC`, `Europe/Berlin`, `US/Eastern` | When a sync window is active, the add-on is only synced during the defined window. Outside the window, changes accumulate and are applied when the next window opens. *** ## Ignore Differences Ignore Differences lets you skip specific fields when comparing the desired state with the live state (cluster). This prevents false drift reports caused by fields that are mutated at runtime by Kubernetes controllers, webhooks, or operators. ### Why Ignore Differences? Ankra continuously compares the desired state with the live cluster state and reports drift when they differ. However, some fields are legitimately modified after deployment: * **Webhook controllers** inject `caBundle` values into webhook configurations * **Kubernetes** assigns `clusterIP` to Services automatically * **Operators** update status fields and annotations on custom resources * **Controllers** like `kube-controller-manager` set default values on resources Without ignore rules, these runtime mutations cause constant OutOfSync status, triggering unnecessary syncs (with auto-sync) or masking real configuration drift. ### Resource Selectors Each ignore rule can be scoped to specific resources using optional selectors: | Field | Description | Example | | - | - | - | | **Kind** | Kubernetes resource kind | `ValidatingWebhookConfiguration` | | **Group** | API group of the resource | `admissionregistration.k8s.io` | | **Name** | Specific resource name | `istio-validator-istio-system` | | **Namespace** | Namespace of the resource | `istio-system` | All selectors are optional. Omitting a selector means the rule matches all values for that field. For example, a rule with only `Kind: Service` and no name or namespace applies to every Service in the add-on. ### Matching Methods Each rule must include at least one of the following methods to specify which fields to ignore. #### JSON Pointers JSON Pointers ([RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901)) are path-based references into a JSON document. They use `/` as a separator and numeric indices for arrays. **Syntax:** `/path/to/field` or `/path/0/arrayElement` **Examples:** | Pointer | What it targets | | - | - | | `/spec/clusterIP` | The `clusterIP` field on a Service | | `/webhooks/0/clientConfig/caBundle` | The `caBundle` of the first webhook | | `/metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration` | An annotation (note: `/` in keys is escaped as `~1`) | | `/status` | The entire status subresource | > [!TIP] > > Use JSON Pointers when you need to target a specific array index or a straightforward field path. They are simple and well-suited for most use cases. #### JQ Path Expressions JQ path expressions use the [jq](https://jqlang.github.io/jq/) query language syntax to target fields. They are more flexible than JSON Pointers and support wildcards and array iteration. **Syntax:** `.path.to.field` or `.path[0].arrayElement` **Examples:** | Expression | What it targets | | - | - | | `.spec.clusterIP` | The `clusterIP` field on a Service | | `.webhooks[0].clientConfig.caBundle` | The `caBundle` of the first webhook | | `.webhooks[].clientConfig.caBundle` | The `caBundle` of **all** webhooks (wildcard) | | `.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]` | An annotation with special characters in the key | | `.status` | The entire status subresource | > [!TIP] > > Use JQ Path Expressions when you need wildcards (e.g. `.webhooks[]` to match all array elements) or when targeting fields with special characters in their keys. #### Managed Fields Managers Instead of targeting specific fields, you can ignore all fields managed by a particular controller or process. Kubernetes tracks which controller "owns" each field through its [Server-Side Apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) mechanism. **Examples:** | Manager | What it covers | | - | - | | `kube-controller-manager` | Fields set by the Kubernetes controller manager (e.g. default values, status updates) | | `clusterrole-aggregation-controller` | Aggregated rules on ClusterRoles | | `cert-manager-cainjector` | CA bundle fields injected by cert-manager | > [!WARNING] > > Managed Fields Managers is the broadest matching method. It ignores **all** fields owned by that manager, which may include fields you want tracked. Use it only when you trust the controller's mutations entirely. ### Common Examples > [accordion] Webhook caBundle (ValidatingWebhookConfiguration) > > Webhook controllers like cert-manager inject a `caBundle` into webhook configurations after deployment. This is one of the most common causes of false OutOfSync status. > > | Field | Value | > | - | - | > | **Kind** | `ValidatingWebhookConfiguration` | > | **Group** | `admissionregistration.k8s.io` | > | **JSON Pointer** | `/webhooks/0/clientConfig/caBundle` | > > Or to cover all webhooks in the configuration using JQ: > > | Field | Value | > | - | - | > | **Kind** | `ValidatingWebhookConfiguration` | > | **Group** | `admissionregistration.k8s.io` | > | **JQ Path Expression** | `.webhooks[].clientConfig.caBundle` | > > This also applies to `MutatingWebhookConfiguration` resources. > [accordion] Service clusterIP and clusterIPs > > Kubernetes assigns `clusterIP` and `clusterIPs` automatically when a Service is created. These values are not in your Helm chart but will appear in the live resource. > > | Field | Value | > | - | - | > | **Kind** | `Service` | > | **JSON Pointers** | `/spec/clusterIP`, `/spec/clusterIPs` | > [accordion] Aggregated ClusterRole Rules > > When using ClusterRole aggregation, the `kube-controller-manager` merges rules from multiple ClusterRoles. The aggregated `rules` field will differ from what is defined in Git. > > | Field | Value | > | - | - | > | **Kind** | `ClusterRole` | > | **Group** | `rbac.authorization.k8s.io` | > | **Managed Fields Manager** | `clusterrole-aggregation-controller` | > [accordion] Status Fields on Custom Resources > > Operators and controllers frequently update the `.status` subresource on CRDs. Since status is managed by controllers and not by your Helm chart, these changes cause false diffs. > > | Field | Value | > | - | - | > | **Kind** | Your CRD kind (e.g. `Certificate`, `IngressRoute`) | > | **JQ Path Expression** | `.status` | > [accordion] Annotation Mutations by Controllers > > Some controllers add or modify annotations on resources. For example, `kubectl.kubernetes.io/last-applied-configuration` is added by client-side apply. > > | Field | Value | > | - | - | > | **JSON Pointer** | `/metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration` | > > Or with JQ (no escaping needed): > > | Field | Value | > | - | - | > | **JQ Path Expression** | `.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]` | > [accordion] StatefulSet VolumeClaimTemplates > > Kubernetes mutates `volumeClaimTemplates` on StatefulSets after creation, adding defaults like `storageClassName`, `volumeMode`, and `status`. These fields are not in your Helm chart but appear in the live resource. > > | Field | Value | > | - | - | > | **Kind** | `StatefulSet` | > | **Group** | `apps` | > | **JQ Path Expression** | `.spec.volumeClaimTemplates[]` | > > For more targeted ignoring, you can narrow to specific subfields: > > | Field | Value | > | - | - | > | **Kind** | `StatefulSet` | > | **Group** | `apps` | > | **JSON Pointers** | `/spec/volumeClaimTemplates/0/spec/volumeMode`, `/spec/volumeClaimTemplates/0/status` | > [accordion] Fields Managed by kube-controller-manager > > The Kubernetes controller manager sets default values on many resources (e.g. default service account tokens, pod tolerations). Use a Managed Fields Manager rule to ignore all of them at once. > > | Field | Value | > | - | - | > | **Managed Fields Manager** | `kube-controller-manager` | > > This applies broadly, so only use it on resources where you want to ignore all controller-managed fields. ### Important Behaviors > [!NOTE] > > **Rules need a diff specifier.** Each ignore rule must include at least one JSON Pointer, JQ Path Expression, or Managed Fields Manager. Rules with only resource selectors (Kind, Group, Name, Namespace) and no diff specifier are silently ignored. > [!NOTE] > > **RespectIgnoreDifferences is automatic.** When you add ignore rules, the `RespectIgnoreDifferences=true` sync option is automatically enabled. You do not need to add it manually. > [!NOTE] > > **Custom profile.** Adding or modifying ignore rules automatically switches your settings profile to **Custom**. *** ## Backup Override An add-on's `backup` settings override the [backup policy of the stack it belongs to](/guides/backups#protect-a-stack), for the one member of a protected stack that has to be captured differently - a cache nobody wants copied to object storage every night, or a database that needs a longer tail than the rest. **An add-on with no `backup` block follows its stack, and that is the right answer for almost every add-on.** The override is not a second place to turn protection on: a stack that is not protected captures nothing, whatever its add-ons say. | Field | Meaning | | - | - | | `enabled` | Whether this add-on's data assets are captured under the stack's policy | | `consistency` | How the capture is taken: `transactional`, `application`, `crash` or `logical`. **Leave it empty** unless you have a reason - empty keeps the engine's own answer, and an override claiming more than the engine delivers is a lie about the restore point | | `schedule` | `hourly`, `daily`, `weekly` or a cron expression. Empty inherits the stack's | | `retention.hourly` / `.daily` / `.weekly` / `.monthly` / `.yearly` | How many restore points of each period to keep for this add-on. Absent inherits the stack's | | `retention.minimum_count` | Keep at least this many, however the tiers fall out | | `retention.minimum_age` | A duration (`24h`, `720h`). Nothing younger is expired | ```bash theme={null} ankra cluster addons settings get cnpg-cluster ankra cluster addons settings set redis-cache --backup-enabled=false ankra cluster addons settings set cnpg-cluster \ --backup-enabled=true --backup-consistency=transactional \ --backup-retention-monthly=12 ``` > [!NOTE] > > **The Stateful profile turns it on.** Choosing **Stateful** for an add-on enables its backup override, because an add-on somebody chose stateful settings for is an add-on with data. As with every other setting, changing one by hand switches the profile to **Custom**. *** ## Related > [card] [Backups and restore points](/guides/backups) > > Protect a stack, take restore points, and restore one. > [card] [Add-ons](/concepts/addons) > > Learn about add-on architecture and deployment. > [card] [Stacks](/concepts/stacks) > > Bundle add-ons into reusable stacks. > [card] [GitOps](/concepts/gitops) > > Understand the GitOps workflow. > [card] [SOPS Encryption](/guides/sops) > > Encrypt sensitive add-on values. # Add-ons Source: https://docs.ankra.ai/concepts/addons Manage and deploy Helm add-ons that Ankra installs and keeps reconciled on your clusters. > [!NOTE] > > Add-ons are Helm charts that Ankra deploys to your clusters and keeps reconciled. They provide a standardized way to extend your clusters with monitoring, networking, security, and custom applications. ## What is an Add-on? An add-on is a packaged application or tool that extends the functionality of your Kubernetes cluster. Add-ons are distributed as Helm charts and can include anything from monitoring tools and ingress controllers to security solutions and custom integrations. An add-on could even be your own application, integrated into the Ankra ecosystem for easy deployment and management. *** ## Architecture Overview Understanding how add-ons flow from configuration to deployment helps you manage them effectively. ### Deployment Pipeline ```mermaid theme={null} flowchart LR Stack[Stack Builder or Git] --> Platform[Ankra platform] Platform --> Agent[Ankra agent] Agent --> Helm[helm upgrade --install] Helm --> K8s[Kubernetes Resources] ``` **How it works:** 1. **Stack Builder or Git**: You configure add-ons with values and dependencies in the Stack Builder, or in a connected [GitOps repository](/concepts/gitops) 2. **Ankra platform**: Stores the desired state of the stack and dispatches the deploy 3. **Ankra agent**: Receives the deploy inside your cluster 4. **Helm release**: The agent runs `helm upgrade --install` with your values; the step succeeds once the release is healthy 5. **Kubernetes Resources**: The rendered manifests are applied to your cluster This is the **native** engine, the default for new clusters. Clusters created before it may still use the ArgoCD engine, where an ArgoCD installation in the cluster applies the chart instead; the stack you build is the same either way. See [Deployment Engines](/concepts/deployment-engines). ### Add-on Status An add-on's status in the dashboard shows whether its latest deploy succeeded and whether the release has drifted from the desired state. The agent reports drift to the platform, and an add-on whose settings enable self-heal is reconciled back. See [Deployment Engines](/concepts/deployment-engines) for how each engine detects and heals drift. *** ## Resource Hierarchy Each add-on creates a hierarchy of Kubernetes resources. Understanding this hierarchy helps with troubleshooting and resource management. ### Typical Add-on Resource Structure ```mermaid theme={null} flowchart TD App[Helm release] --> NS[Namespace] App --> Deploy[Deployments] App --> SVC[Services] App --> CM[ConfigMaps] App --> Secret[Secrets] Deploy --> RS[ReplicaSets] RS --> Pod[Pods] SVC --> EP[Endpoints] ``` **Resource types commonly created by add-ons:** | Category | Resources | | - | - | | **Workloads** | Deployments, StatefulSets, DaemonSets, Jobs | | **Networking** | Services, Ingresses, NetworkPolicies | | **Configuration** | ConfigMaps, Secrets | | **Storage** | PersistentVolumeClaims | | **RBAC** | ServiceAccounts, Roles, RoleBindings | | **Custom** | CRDs and custom resources | Navigate to any installed add-on and open its [Resource Map](#resource-map) to see this hierarchy live for your cluster. *** ## Continuous Delivery Flow **1. Configuration Change** You modify add-on values in the Stack Builder, or update values in your connected Git repository. **2. Change detected** A change made in the Stack Builder is deployed when you deploy the stack. A change pushed to Git is picked up through the repository's webhook (or periodic polling). **3. Deploy** The agent applies the difference with Helm on the native engine, or through ArgoCD on a cluster still using that engine. **4. Health Check** The deploy is not marked successful until the release's resources are healthy. ### Sync Settings Each add-on's settings control how it is kept in sync: | Setting | Behavior | | - | - | | **Automated sync** | Changes to the desired state deploy to the cluster automatically | | **Self-Heal** | Manual changes made in the cluster are reverted to match the desired state | | **Prune** | Resources removed from the desired state are deleted from the cluster | Both engines support these; see [Add-on Settings](/concepts/addon-settings). *** ## Manage Add-on Sources An add-on source in Ankra is either a Helm repository URL or an OCI (Open Container Initiative) registry URL. These sources contain the Helm charts you want to use for deploying add-ons to your clusters. Helm charts are a standard way to package Kubernetes applications, making it easy to manage dependencies and versioning for complex workloads. By connecting both public and private Helm repositories or OCI registries, you can access a wide range of Kubernetes add-ons. This lets you integrate open-source tools, vendor solutions, or your own custom charts - all from a single interface. Managing sources centrally ensures your team always has access to the latest and most secure versions of your preferred add-ons. Setup steps live in one place: see [Registries](/guides/registries) for connecting a source, and [Create a Registry](/guides/create-a-registry) for hosting your own charts on GHCR. *** ## Install Add-ons Add-ons are installed through [Stacks](/concepts/stacks), next to the [manifests](/concepts/stacks#manifests) they depend on, so they deploy in the right order as one unit. **1. Navigate to Stacks** Go to the **Stacks** section in your cluster and click **+ Create** (or edit an existing stack). **2. Add an Add-on** In the Stack Builder, click **Add Add-on**. Browse or search for charts from your connected registries. **3. Configure Values** Select the chart version and configure the values. Pin an exact chart version in production to avoid unexpected upgrades. You can use the AI Assistant (`⌘+J`) to help customize the configuration. **4. Deploy** For a new stack, click **Create Stack** to save and deploy it, installing the add-on on your cluster. *** ## Encrypting Sensitive Values with SOPS When an add-on's values carry a password, an API key or a webhook URL and the cluster is connected to Git, list those key names under **Encrypted Keys (SOPS)** in the add-on's configuration, for example `adminPassword`. Ankra encrypts their values with SOPS before it writes the values file to Git, keeps the keys readable for diffs, and decrypts them when it deploys. SOPS needs a one-time setup for the organisation - see [SOPS](/guides/sops). *** ## Operational Learnings with AGENTS.md Every add-on can carry an **AGENTS.md** - a markdown document of operational learnings (upgrade gotchas, safe rollout order, values that must stay pinned) that teammates and Ankra's AI build up over time. Edit it from the **AGENTS.md** tab on the add-on in the Stack Builder; editing it never triggers a redeploy. See [Teach Your Agents with AGENTS.md](/platform/agents-md). *** ## Viewing Installed Add-ons Once deployed, you can monitor and manage add-ons from the Add-ons page. ### Add-ons List Navigate to your cluster → **Add-ons** to see all installed add-ons with: * **Name and Version**: The Helm chart name and deployed version * **Status**: Current sync and health status * **Namespace**: Where the add-on is deployed * **Last Updated**: When the add-on was last modified ### Add-on Details Click any add-on to view detailed information: | Tab | Description | | - | - | | **Overview** | Chart info, status, metadata, and deployment state | | **Resource Map** | Visual hierarchy of all Kubernetes resources created by the add-on | | **Values** | Current Helm values configuration | | **Events** | Recent events related to the add-on's resources | ### Resource Map The Resource Map provides a visual representation of all resources deployed by an add-on: * **Hierarchical View**: See parent-child relationships (e.g., Deployment → ReplicaSet → Pod) * **Quick Navigation**: Click any resource to view its details * **Status Indicators**: Identify unhealthy or pending resources at a glance * **Resource Types**: View all Kubernetes resource kinds in one place > [!TIP] > > Use the Resource Map when troubleshooting to quickly identify which resources are failing and their dependencies. *** ## Updating Add-ons ### Changing Values 1. Navigate to your stack in the Stack Builder 2. Click the add-on you want to modify 3. Update the Helm values as needed 4. Save and deploy the stack ### Upgrading Chart Versions 1. Edit the stack containing the add-on 2. Click the add-on and select a new chart version 3. Review any breaking changes in the chart changelog 4. Save and deploy to upgrade > [!WARNING] > > Major version upgrades may include breaking changes. Review the chart's release notes and test in a non-production environment first. ### Rollback If an upgrade causes issues: 1. Edit the stack and revert to the previous chart version 2. Or use GitOps: revert the commit in your Git repository 3. Ankra deploys the previous state again - from the stack when you deploy it, or from Git on its next sync Add-on settings and Helm values are also readable and updatable through Ankra's AI and MCP clients, alongside sync, rollback, and uninstall - so routine add-on operations can run straight from chat or your editor. See the [MCP Tool Reference](/platform/mcp-tools#add-ons). *** ## Explore the Add-ons API Want to automate add-on management or integrate with your CI/CD workflows? Check out the [API Reference](/api-reference/introduction) for endpoints to list, install, and manage add-ons programmatically. *** ## Related > [card] [Stacks](/concepts/stacks) > > Build stacks with multiple add-ons. > [card] [Add-on Catalog](/platform/addon-catalog) > > Browse available Helm charts. > [card] [GitOps](/concepts/gitops) > > Understand the GitOps workflow. > [card] [SOPS Encryption](/guides/sops) > > Encrypt sensitive add-on values. # Applications Source: https://docs.ankra.ai/concepts/applications Point Ankra at an application's source repository and it generates the Dockerfile, Kubernetes manifests, and CI/CD to build and deploy it. Applications take you from source code to a deployable workload. Connect an application's Git repository and Ankra analyzes it, generates the packaging it's missing, and opens a pull request in your repository, unless it finds nothing there it can build and keep running. Once merged, CI builds and publishes a container image that you can deploy to a cluster. > [!NOTE] > > Applications connect to **GitHub** repositories and use a [GitHub credential](/integrations/github). By default the generated CI/CD pipeline pushes the container image to your organisation's private Ankra registry; if you already run your own registry, [declare it](#using-your-own-registry) and Ankra publishes to and reads from it instead. > [!TIP] > > **No repository yet?** A [Claude Design](https://claude.ai/design) export can be imported straight into an application: Ankra converts the artboards into a static site, creates the GitHub repository, commits it with a Dockerfile and registers it, and the same setup pull request, build and deploy lane follows. See [From Claude Design to a live application](/guides/claude-design-to-ankra). *** Watch a repository go from source to a live URL with one command. [Ship any repo to production on Ankra - one command](https://www.youtube-nocookie.com/embed/F5G_lrsiF64?rel=0) ## How it works ```mermaid theme={null} flowchart LR Repo[Your app repo] -->|connect| Ankra[Ankra] Ankra -->|"analyze + generate (PR)"| PR[Pull request] PR -->|merge| CICD[CI/CD pipeline] CICD -->|push image| Registry[Private Ankra registry] Registry -->|deploy| Cluster[Application on cluster] ``` **1. Connect a repository** Provide a name and a GitHub credential, then pick the repository and a branch from the repository's own branch list - the default branch is preselected, and **Enter a different branch** lets you type one that does not exist yet. **2. Analyze and generate** Ankra inspects the repository, detects the language and framework, and generates the artifacts it needs: a Dockerfile, Kubernetes manifests, and a CI/CD workflow. **3. Merge the pull request** The pull request runs its own checks first: it builds the image, scans it and runs the security gate. It stays a draft, which GitHub will not merge, until they pass, and then Ankra marks it ready for review; if you run the [AI review](/platform/ai-code-review), it skips the draft until then unless **Review drafts** is on. Review and merge it. Merging activates the CI/CD pipeline in your repository. When they fail, Ankra fixes the pull request itself, and the [PR shepherd](#the-pr-shepherd) keeps it moving until you merge it - or merges it for you, if you opt in. Beside CI that Ankra adopted, with no CI at all, or on a private repository whose GitHub plan has no drafts, it opens ready for review. **4. Build and publish** CI builds and pushes the container image to the organisation's private Ankra registry. Ankra surfaces the image URL and scans the published image. **5. Deploy** Deploy the application onto a cluster. Ankra verifies that the image was published and tracks the rollout. > [!NOTE] > > **When setup stops before the pull request.** In two cases Ankra opens no setup pull request and deploys nothing. The setup ends as failed, and the application's error message says why: > > * **Nothing in the repository keeps running.** When the analysis finds only a program that runs once and exits, or nothing that runs at all, the message starts "Ankra did not set this repository up as an always-on application" and ends "Add a program that keeps running and listens on a port, then run setup again." > * **Nothing can be built.** When there is no Dockerfile in the repository, on an open setup branch or among the files Ankra generated, the failed `setup_stopped` task says what Ankra found and what it could not write. Add a Dockerfile and run setup again. > > Both messages carry the same sentence: "No setup pull request was opened and nothing was deployed." A repository that commits its own manifests under `.ankra/manifests/`, or whose own CI already publishes an image, still gets its setup pull request, with no build for anything that has no Dockerfile. So does a monorepo in which at least one component can be built. ### When your repository already has CI If your repository already runs CI, Ankra reads it during analysis and works with it instead of adding a pipeline of its own. It reads GitHub Actions workflows, `.gitlab-ci.yml`, `bitbucket-pipelines.yml` and `.circleci/config.yml`, and notes for each one whether it runs tests, builds an image, pushes it (to which registry, tagged how), deploys, what triggers it and which runners it asks for. | What your CI does | What Ankra does | | - | - | | Builds and pushes the image | Adopts it. The application deploys the image your CI pushes, and the setup pull request adds only what deploying needs, such as the manifests. No `.ankra/pipeline.yaml`, workflow or Dockerfile is added. | | Pushes an image under a name only the run knows, such as a repository name held in a secret | Adopts it, and tells you what it still needs: point the application at that image with `ankra application registry set`, or choose Ankra Pipelines to publish instead. | | Runs tests but publishes no image | Keeps your CI for the tests and proposes Ankra Pipelines to build and publish the image. | | Nothing (no CI) | Proposes Ankra Pipelines. | The analysis says which of these it chose and why, and the setup pull request carries the same reasoning under **Your existing CI**. When the image your CI pushes lives in a private registry, add a registry credential for it and name it with `ankra application registry set --credential` so the cluster can pull the image. Ankra also checks whether your CI actually starts. If the newest run of a workflow has waited in the queue for more than 20 minutes, the analysis reports it, names the runner labels the jobs ask for (for example a `depot-` or self-hosted label nothing is serving), and suggests switching `runs-on` to `ubuntu-latest`, connecting a runner for that label, or letting Ankra Pipelines run the build. Ankra never edits your workflow. To build with Ankra Pipelines anyway, convert the application with `ankra application pipeline convert`. A repository that already commits `.ankra/pipeline.yaml` keeps building with it. ### When the setup pull request does not pass its checks A repository can already carry problems Ankra did not put there - most often dependencies with known security issues, which the security gate refuses to publish. The setup pull request then fails its own checks, and you do not have to fix it yourself: * **Ankra AI fixes it on the same pull request.** An agent reads the run, commits a fix to the setup branch (for vulnerable dependencies: newer releases, overrides, a regenerated lockfile) and watches the checks until they pass. It never opens a second pull request and never touches your default branch. * **The pull request says what is happening.** Ankra's status comment explains in plain words what stopped the run and that Ankra AI is fixing it, and the application's **Go live** page shows the same. Merge once the checks are green. * **You can turn it off.** Automatic build fixing is on for every application by default. Turn it off for one application, or pause Ankra's autonomous actions, and the pull request only gets the explanation and a **Fix with Ankra** link. When you create the application from the portal yourself and hold pipeline management access, Ankra also approves the pipeline it generated in your name, so the first runs are not held for an approval. An application added with the CLI, an API token or the AI Assistant leaves that approval to an administrator. ### When a build on your branch fails A failed build on the application's tracked branch dispatches the same agent. What it may do depends on the repository's **CI/CD** lane on the application's **AI** tab: | Lane | What happens | | - | - | | **Ask** (the default) | The agent investigates and proposes a fix, without committing | | **Agent** | The agent commits the fix and opens a pull request for you to review | ### When a pull request fails its checks On Ankra Pipelines, a failed build, image gate or `run` step (your tests, for example) on any pull request from a branch of your repository, not only the setup pull request, is fixed on that pull request: * **The fix lands on the pull request's own branch.** An agent commits it to the head branch and watches the run that commit starts. It opens no second pull request and changes only what the failure needs. For an image gate the agent is handed each vulnerable package with the releases that fix it, and moves it to at least the highest. * **A conflict with the base branch is resolved the same way.** The status comment opens "This pull request conflicts with" and names the base. The agent merges the base into the head with a merge commit, resolving every file both sides changed, and the run of that merge commit is its check. Nothing is ever merged into your default branch. * **When the AI allowance is used up**, on Ankra's own models with no key of your own, the status comment says the fix is queued and starts by itself when the allowance resets on the 1st of the month, or as soon as an organisation owner adds the organisation's own model key under **Organisation settings**, **AI provider**; if billing has paused AI, it names that instead. * **Without AI, vulnerable Python packages are still upgraded.** When the image gate blocked the run and the fix has to wait, Ankra moves each package a lock file pins to a release that fixes it, checked against the packages' own requirements, commits that to the pull request on GitHub, and leaves the rest for the allowance. * **The same switches hold it:** automatic build fixing turned off for the application, Ankra's autonomous actions paused, or an environment of the application set to **You run it**. A pull request from a fork is never committed to: it gets the explanation and a **Fix with Ankra** link on the run page. ### The PR shepherd Ankra watches the pull requests it opens in your repository - the setup pull request, and any [portability pull request](/use-cases/vercel-to-ankra) a Launch Plan opens - and drives each one to a state you can merge. You see it as an **Ankra PR shepherd** check on the pull request's latest commit. Its summary says where the pull request stands - waiting, ready, needs a person, or merged - and why, and it is updated as things change. What the shepherd does on its own: * **Your CI never starts.** When no CI check has started on the pull request after about 20 minutes - no runner picks the job up, or the Ankra agent on your CI cluster cannot run pipeline steps - Ankra starts a full Ankra pipeline run on the pull request's head, builds it on Ankra's builders and runs a proof of the application, so the pull request still gets an answer. * **The pull request falls behind its base branch.** Ankra merges the base branch into its own branch with a merge commit - never a force-push or a rebase. It waits until the head has sat for about 6 hours, or does it straight away when merging is opted in (below). * **The pull request conflicts with its base branch.** Ankra resolves the conflict only when every file in the pull request is one Ankra owns: the Dockerfile, `.dockerignore`, the `.ankra/` directory and the generated build workflow. Your own files are kept exactly as the base branch has them, and a conflict that touches them is left for you. * **A check fails.** When the pull request only changes Ankra's own files, Ankra AI fixes it on the same branch, up to 3 times, then reports it for a person. A failure in a pull request that changes your files is reported, never "fixed". * **The proof of the head never gives an answer.** Where your organisation has a staging cluster, a proof still queued 20 minutes after Ankra queued it, or one that ended not proven, is a dead end: once nothing else is pending, the check reads **Needs a person**, names the proof and its reason, and Ankra does not merge. The shepherd keeps looking every 15 minutes, so a proof that passes later turns it **Ready to merge**. These are the shepherd's own actions. On Ankra Pipelines, a pull request from a branch of your repository is also fixed as [When a pull request fails its checks](#when-a-pull-request-fails-its-checks) describes, whichever files it changes. #### Merging Ankra merges a pull request only when all of these hold: * merging is opted in for the application (below); * every check on the head has finished green, and the proof of the application passed; * GitHub reports the pull request mergeable. Branch protection is always honoured, never worked around. Otherwise the check reports the pull request as ready for a person to merge. Ankra never merges a pull request whose own CI never ran - Ankra's pipeline run and proof make it ready for you, not merged - and it never merges a portability pull request, because that changes your code. After a merge, the application deploys as usual. When your CI could not build the pull request, Ankra builds the merge commit on its own builders so the deploy has an image. #### Opting in to merging Merging is off by default. Two settings decide it: * **The organisation's setting**, `auto_merge_setup_pull_requests` on `PUT /org/applications/settings` (or `ankra_application_settings` in [Terraform](/integrations/terraform)). Only organisation admins can change it. * **The application's own setting**, which overrides the organisation's: on for this application, off for this application, or follow the organisation (the default). Read it with `GET /org/applications/{application_id}/auto-merge-setup-pull-requests` and change it with `PUT` and a body of `{"enabled": true}`, `{"enabled": false}`, or `{"enabled": null}` to follow the organisation again. Only organisation admins can change it. The answer carries the application's own value, the organisation's, and the `effective` one Ankra acts on. An application set to on is merged even when the organisation's setting is off, and one set to off never is. If a setting cannot be read, Ankra does not merge. *** ## Building on Ankra's builders The flow above puts the first image behind a merged pull request: until the generated workflow is on your default branch, your CI has nothing to run, and nothing to publish. Ankra can also build the image itself, on its own builders, with no workflow in the repository at all. Ankra clones the commit, resolves a recipe for it, builds it, and pushes the image to the same registry the application publishes to. The recipe is the first of these that fits: 1. **The repository's own `Dockerfile`** — used as-is. 2. **A generated Dockerfile** — the one Ankra wrote for this application. 3. **Buildpacks** — when there is no Dockerfile to use. No Actions minutes are spent, no runners need operating, and no registry credentials go anywhere near the repository. ```bash theme={null} # Queue a build and follow it to a result ankra application build start --commit --ref main --wait # Read what the builders did ankra application build list ankra application build get ``` Reach for this when the repository's own CI cannot do the job — a private repository on a plan whose Actions never leave the queue, a first image needed before anyone merges the setup PR, or an unattended caller with no browser to click through. > [!NOTE] > > `--commit` takes a **full** 40- or 64-character commit sha. The queue deduplicates on the string it is given, so an abbreviation would be a second key for the same commit and would build it twice. Asking twice for one commit joins a single build rather than racing two, and the answer's `already_requested` says when that happened. ### Following a build A request is not yet a build. `start` answers with a **request id** immediately; the build itself is created when a builder claims the request. So there are two things to read: ```bash theme={null} # The queued ask - pending until claimed, then it names its build ankra application build request # The build itself ankra application build get ``` `ankra application build start --wait` walks both steps for you and exits non-zero if the build failed, which makes it usable as a pipeline gate. Watching the build list instead is a trap: a previous failed build of the same commit is still the newest one for that commit, so it would report an old failure as your build's result. ### Waiting for a builder Ankra's builders are one pool that every organisation shares. Your organisation runs at most three builds on it at a time, so a burst of builds from one organisation - onboarding a repository with many components, say - cannot hold every builder while others wait. Requests beyond three stay `pending`, in the order they were made, and the next one starts as soon as one of your running builds finishes. A pending request is waiting its turn, not failing: it keeps its request id, and `--wait` keeps following it. ### What a failure means A failed build carries an `error_class` that says whose failure it was: | `error_class` | Whose | What to do | | - | - | - | | `build_failed` | Yours | The recipe did not build. Read `error_message`. | | `clone_auth` | Yours | Ankra could not clone the repository — check the application's Git credential. | | `recipe_missing` | Yours | No Dockerfile and no buildpack matched the repository. | | `push_failed` | Ankra's | The image built but could not be pushed. | | `timeout` | Ankra's | The build exceeded its budget. | | `capacity` | Ankra's | No builder was running to take the build. | The last three are platform-side and are already visible to Ankra without anyone reporting them. > [!NOTE] > > This replaces the **image build**, not the whole generated pipeline. That workflow also runs a Semgrep scan, and packages a Helm chart for applications that publish one. If you plan to retire the workflow entirely, decide where those two go first. *** ## The application workspace An application opens as a workspace: a context sidebar beside the main one, in the order the work happens, with one page per section at `/organisation/applications//
`. A link to an older `?tab=` address redirects to its section. | Section | What it shows | | - | - | | **Overview** | Where the application is right now: the setup journey, the environments it is live in, and the latest run. **Add** beside the environments puts the application on another cluster; see [Add an environment on another cluster](/guides/application-environments#add-an-environment-on-another-cluster). | | **Pipelines** | Run history as one lane per run, with skipped and cancelled runs grouped on their own. Stages fold into phases decided by each stage's kind, so a long pipeline reads as a handful of things you can name; a kind the grouping does not know yet sits under *Other steps*. A run opens as its own sheet - logs, the failed step and its exit code, the artifact and the commit - with the suggested fix separate from the rerun. | | **Deployments** | One installation per row: the cluster and its environment label, the owning add-on or stack, the version it serves, health and the reported address. A version that differs between installations is shown as a difference, not called drift. A field the read does not carry reads *unknown*, never zero, and the installation sheet links to the owning add-on for namespace, pods, events and logs rather than pretending to show them. | | **Previews** | What the CLI and API call demos. One card per preview with its branch or commit, component, image, expiry, access (browser URL, in-cluster only, URL not live, or not reachable yet) and the staging cluster, plus the launch dialog and the environment template. See [Preview demos](#preview-demos). | | **Security** | Eight views over the application's scan reports, ranked by exploitability. See [Security scanning](#security-scanning). | | **Environment** | The secret keys the application reads, each with three independent states: stored (*Set*, *No value*, *Value missing*), consumed (*Manifests* or *Not referenced*) and its deployment state. Only a stored key the manifests reference inherits the application-wide apply answer; a key nothing references reads *Stored only*, a key with no value *Nothing to apply*, a lost value *Cannot apply*. Values are never displayed - the API has no route that returns one. Ordinary, non-secret variables are not available for applications yet. | | **Source & config** | The files Ankra wrote: the file tree, the editor, pending changes per file and in total, and the commit message with **Push to PR**. The editor says whether it is showing the setup branch or the generated set, and why the branch is not showing when it is not. | | **Settings** | The repository, branch and component paths setup ran against (read-only), delivery per environment, the [image registry](#using-your-own-registry), publishing the application as an add-on, running setup again, and delete last. | The sidebar carries two badges: **Security** shows the CISA-listed finding count once the KEV catalog has synced, and never before; **Environment** shows the declared keys still without a value. The Applications list has two ways in: **All applications**, or **Needs attention** - a failed setup, an unmerged setup pull request, a failed deploy or a degraded one. Setup in progress, deploying, parked, resting, and a runtime Ankra could not read are not attention. ## What Ankra tracks For each application you see: * **State** and **analysis status** - where the application is in the connect/analyze/generate/build lifecycle, with an error message if something needs attention. * **Repository** - owner, name, branch, and URL. * **Components** - the repository's deployable apps. An ordinary repository has one; a monorepo has one per app. * **Artifacts** - the private container image URL and the latest published build, per component. * **Pull request** - a link to the generated PR. * **Jobs** - the underlying platform jobs for the application, so you can follow analysis and generation as it runs. ### Monorepos A repository that builds more than one deployable app is onboarded as a **monorepo**: Ankra records one component per app, and each component gets its own packaging and its own image. | Per component | Where it lives | | - | - | | Dockerfile | `/Dockerfile` | | Kubernetes manifests | `.ankra/manifests/-*.yaml` | | Build workflow | `.github/workflows/-build-and-publish.yml`, with a `paths:` filter scoped to the component's directory so touching one app does not rebuild the others | | Container image | `//` | Ankra decides the components from, in order: the `components` block of the repository's [`.ankra/ankra.yaml`](/reference/application-descriptor#components), when it declares one; the per-component workflows the repository already carries (so re-running analysis never renames a component and orphans the images it has published), the apps the analysis proposed, then the repository's structure - two or more Dockerfiles in subdirectories and no Dockerfile at the root, or a workspace marker (`nx.json`, `turbo.json`, `lerna.json`, `pnpm-workspace.yaml`, `go.work`) plus two or more subdirectories with their own dependency manifest. **Without a declaration, a Dockerfile at the repository root always means a single app**, whatever else the repository contains. The AI proposal and structural detection can add components to a monorepo but never leave it with fewer than it records: a run whose inference finds fewer keeps the recorded ones. Only a declaration, or two or more of Ankra's per-component build workflows for fewer components than the record, can shrink it, and a run that drops or renames a recorded component records the new set and reports the change as the failed task `record_components`. A repository whose layout inference misreads, such as an API built from `deploy/docker/Dockerfile` at the root beside a web app in `web/`, declares its components in [`.ankra/ankra.yaml`](/reference/application-descriptor#components), and the declaration is also how you remove one. Build state, published images, deploys, demos, and container scanning are all reported per component, so an application whose API component built and whose frontend did not says exactly that. ### Using your own registry An application publishes to your organisation's private Ankra registry unless you say otherwise. If you already operate a registry - the same Harbor whose OCI charts Ankra indexes, for instance - declare it on the application and Ankra publishes there, reads the published tags back from there, and pulls from there. Declare it on an existing application from **Settings** → **Image registry**, from the CLI, or with `image_registry` when you create the application. The settings panel also reports the host and project the declaration resolved to and the image repository each component is expected to publish to, so you can compare them against where your builds actually push. ```bash theme={null} # Show the effective registry and the expected repositories ankra application registry get # Declare a registry you operate ankra application registry set \ --url oci://artifact.example.com/commerce-images \ --credential example-harbor-pull # Go back to your organisation's own Ankra registry ankra application registry clear ``` The same declaration at create time: ```json theme={null} { "name": "commerce", "app_repo_credential_name": "github", "app_repo_owner": "smartoptics-dwdm", "app_repo_name": "commerce", "image_registry": { "url": "oci://artifact.example.com/commerce-images", "credential_name": "example-harbor-pull", "pull_secret_name": "harbor-registry", "username_secret_name": "HARBOR_USERNAME", "password_secret_name": "HARBOR_PASSWORD" } } ``` | Field | Meaning | | - | - | | `url` | The registry project images publish to, as `oci:///`. A deeper path becomes a prefix every component's repository nests under. | | `credential_name` | An existing [registry credential](/integrations/helm-registries) of the organisation - the same credential resource your chart registries authenticate with. Ankra reads published tags and builds the cluster pull secret from it. | | `api_url` | Optional. The registry's management API base; defaults to `https://`, which is where a Harbor serves its own API. | | `pull_secret_name` | Optional. The `kubernetes.io/dockerconfigjson` Secret the generated manifests reference through `imagePullSecrets`. Defaults to `ankra-registry-pull`. | | `username_secret_name` / `password_secret_name` | Optional. The repository Actions secrets the build workflow logs in with. Default to `ANKRA_REGISTRY_USERNAME` / `ANKRA_REGISTRY_PASSWORD`. | | `admin_credential_name` | Optional. A registry credential with project administrator rights on the project (or a robot granted the robot-account permissions). Setting it hands robot management on your registry to Ankra: it mints and revokes a push robot for the application with it, and rotates it when the credential is a Harbor user. See [Robot accounts](#robot-accounts). | | `manage_actions_secrets` | Optional, default `false`. See below. | | `component_repositories` | Optional. The repository inside the project for a component, keyed by component name - `{"backend": "commerce-backend"}` publishes the backend at `/commerce-backend` - for a registry whose layout follows neither Ankra's nesting nor the flat component name. Unnamed components keep the derived path. CLI: `--component-repository backend=commerce-backend` (repeatable); portal: one field per component under **Settings** → **Image registry**. | | `flat_repositories` | Optional, default `false`. Publish a monorepo's components straight under the project - `/` - instead of Ankra's `//`. Turn it on when the registry already holds the images that way, so publish readiness, the deploy gate and the generated workflows all address the repositories your pipeline already pushes to. Single-component applications are unaffected. | **Ankra does not mint robots for a registry you operate unless you hand it the keys.** With a declared registry and no `admin_credential_name`, setup names the two Actions secrets the workflow reads and leaves them to you, rather than writing a credential Ankra happens to hold over a push robot you administer. Publish readiness reports whether those secrets exist and names them. Name an `admin_credential_name` to have Ankra mint a push robot for the application there ([Robot accounts](#robot-accounts)), or set `manage_actions_secrets: true` to have it write the declared credential into them instead. > [!WARNING] > > `credential_name` is what makes a declaration usable rather than merely descriptive. Without one Ankra records where the images live but cannot read their tags or pull them, so publish readiness, the demo build check and the deploy gate all keep reporting the application as never built - while it publishes healthily to a registry Ankra simply cannot see. > [!NOTE] > > An application is bound to the GitHub credential it was created with; its App installation must reach the repository for builds, deploys and Actions secrets. Move an application onto another credential of the organisation with `ankra application credential set --credential ` (`GET`/`PUT /api/v1/org/applications/{application_id}/repository-credential`); the answer reports whether the new binding resolves to an installation. > [!NOTE] > > On a monorepo, a component may declare its own `image_registry` to publish into a different project of the same registry - useful when each app has its own robot accounts and retention rules. A component's declaration wins over the application's; the application's applies to every component that declares none. Publish readiness, the deploy gate, image tag listings, container scanning, and preview demos all resolve the declared registry, so an application whose images live outside Ankra reports what is actually published rather than staying blocked on an artifact that was never going to appear in Ankra's own project. Running Harbor yourself? [Harbor as your application registry](/guides/harbor-registry) covers the operator's side: which credential does what, which Harbor releases let a robot administer robots and when the credential must be a Harbor user, how to check the minted robot from Harbor's own API, and how the pull secret reaches every cluster and namespace the application deploys to. ### Robot accounts Every application gets a registry robot account of its own: a push+pull robot named `robot$+app-` on the project it publishes to, stored as the platform-managed registry credential `ankra-harbor-app-` and written into the repository's Actions secrets as the login the build workflow uses. Nothing else shares that login, so a leaked repository secret is rotated or revoked for that one application, and a deleted application takes its robot with it. * **On your organisation's Ankra registry** the robot is minted automatically when the application is set up, and again whenever the application is reconciled. Applications onboarded before robots existed get one the first time **Fix automatically** runs in the **Pipelines** section, or when you create it from **Settings** → **Image registry**. * **On a registry you operate**, Ankra mints one only with a credential that may: set `admin_credential_name` to a registry credential with project administrator rights on the declared project (in Harbor, a project admin user, or a robot granted the robot-account permissions). Without it Ankra never touches your robots; publish readiness names the field. The robot is shown - name, registry, when it was created and last rotated - under **Settings** → **Image registry**, where you can create, rotate or revoke it. Rotating refreshes the secret and rewrites the repository's Actions secrets; the previous secret stops working the moment the registry answers. Revoking deletes the robot from the registry and drops its credential, leaving the repository's secrets holding a login that no longer works. ```bash theme={null} # Show the application's robot ankra application registry robot get # Mint one (or confirm the existing one) and write its login into the repository ankra application registry robot ensure # Rotate its secret after a leak ankra application registry robot rotate # Revoke it ankra application registry robot revoke --yes # Let Ankra manage robots on a registry you operate ankra application registry set \ --url oci://artifact.example.com/commerce-images \ --credential example-harbor-pull \ --admin-credential example-harbor-admin ``` The same is available on the API at `/api/v1/org/applications/{application_id}/registry-robot` (`GET`, `POST` with `{"rotate": true|false}`, `DELETE`). The robot's credential appears in your organisation's registry credentials as managed by Ankra; it cannot be edited or deleted there, only through the application. ### Security scanning Applications include code and container security insights, so vulnerabilities surface alongside the build rather than in a separate tool. The **Security** section reads the way the [Security Center](/guides/cluster-security) reads a fleet finding - risk first, ranked by exploitability - in eight views: | View | What it shows | | - | - | | **Overview** | Risk score, vulnerabilities, known exploited, last scan and scanners reporting, then *First to review*: the CISA-listed finding when the catalog is synced, the highest-severity one otherwise. An unscanned image reads **Not assessed** on every tile, never `0 / 10`; a pipeline configured but not yet built reads **Waiting**; a report older than two hours is marked stale. | | **CVEs & findings** | Every finding, ranked by [exploitability](/guides/cluster-security#exploitation-intelligence-cisa-kev-and-epss): CISA KEV listing first, then EPSS, then severity, then CVSS, with highest severity as the alternative order. A **Known exploited** filter narrows to CISA-listed findings, and a banner leads the table when any finding is listed. | | **KEV** | One card per CISA-listed finding: date added, due date (overdue marked), ransomware use, EPSS, the fix, CISA's required action, and where it runs. | | **SBOM** | The package inventory of the selected image version - package and PURL, version, type, licence and tier, the CVEs on that version and the workloads it runs in - and the versions list with one row per pushed tag, its bill of materials, its findings and the CycloneDX and CSV downloads. Only versions with a bill of materials are selectable; nothing borrows another image's inventory. | | **Compliance** | Not available for applications yet; points at the organisation Security Center. | | **Coverage** | Which scanner reported - Trivy, Semgrep, Checkov, bill of materials - and how fresh: fresh, stale after two hours, pending (configured, no build since), not configured, or unknown when the read failed. | | **Code** and **Infrastructure** | The Semgrep and Checkov findings; a row opens with the file, lines, rule, category and the rule reference. Waiting for a first scan and zero findings are distinct states. | Every finding row opens a sheet with severity, CVSS, EPSS (as `EPSS 94% · top 1%`), the CISA KEV entry - date added, due date, ransomware use and required action - the fix, and a link to the platform's own [advisory page](/guides/cluster-security#advisory-pages) for the CVE. Exploitability reads **Unknown** until the KEV catalog has synced; absent is never read as not exploited. On a monorepo, the image tags and the scanned image reference follow the component you select; code and IaC findings cover the whole repository, because that is where those scanners run. Pair this with [AI Insights](/platform/ai-insights) for proactive analysis. *** ## Preview demos Before you merge, you can spin up a **throwaway demo** of a pull request or branch build to see it running. In the portal these live in the application's **Previews** section, which calls each one a preview; the CLI and API keep the `demo` name. Each demo is deployed into its own isolated namespace (`ankra-demo-pr-` or `ankra-demo-br-`) on the organisation's **staging cluster**, PodSecurity-hardened and quota-bounded, and is automatically torn down when its TTL expires - so it can never affect existing workloads. **1. Configure a staging cluster** An admin sets the organisation's staging cluster under **AI** → **Settings** → **Workspaces**. Optionally set a **demo base domain** (with an ingress class and TLS secret) there too - this is what gives demos a public URL. **2. Deploy a demo** Deploy a branch or PR demo from the application's **Previews** section, from the CLI, or by asking the AI assistant. The demo pulls the image tag the PR/branch build pushed. **3. Open the preview** When a public host is available, Ankra returns a **preview URL** you can open directly. Otherwise the demo stays reachable in-cluster (service DNS + a `kubectl port-forward` command). Two guides cover the two ways a demo starts: [branch demos](/guides/branch-demos) walks the launch dialog field by field, and [PR preview environments](/guides/pr-preview-environments) covers the automatic per-pull-request flow. ### The preview URL Ankra resolves the demo's public hostname automatically, and every surface (portal, CLI, MCP, and automatic PR previews) uses the same rule: 1. **An organisation demo base domain**, if configured, wins outright - the host is `.`, served with your configured ingress class and TLS secret. 2. **Otherwise, the staging cluster's own delegated DNS zone**, but only when that zone is active - giving `...`, a hostname the `external-dns` running on that cluster can resolve. When the cluster also carries the Ankra networking stack, the demo's ingress requests a Let's Encrypt certificate from its `letsencrypt-prod` issuer automatically, so the preview URL is served over **https**. 3. **Otherwise the demo stays in-cluster-only** - no ingress is created, and you reach it with the returned service DNS name and `kubectl port-forward`. > [!NOTE] > > A demo only gets a resolvable public URL when the organisation has a demo base domain configured **or** the staging cluster has an active Ankra DNS zone. Without either, the demo still deploys - it just stays in-cluster-only. The Ankra domain in rule 2 is `ankra.cc` by default. An organisation can register **its own custom domain** - dedicated to that organisation alone - in the **Custom Ankra domain** field under **AI** → **Settings** → **Workspaces**, or from the CLI with `ankra org domain set `. The domain must be a bare domain the organisation owns, its NS records must already point at Ankra's nameservers (the ones serving `ankra.cc`), and no other organisation may hold it. With a custom domain registered, the `external-dns` Ankra runs on each of the organisation's clusters is scoped to the **whole domain**, not just that cluster's delegated `..` zone. An Ingress on any hostname under the domain - a top-level name like `app.example.com` included - is published automatically by the cluster serving it. On the shared `ankra.cc` root the scope stays the cluster's own delegated zone: names directly under `ankra.cc` are allocated by the platform, and one organisation's cluster must never be able to write another's. Zones already provisioned keep the domain they were minted under, so the switch is refused while any cluster DNS zone or [DNS record](/platform/organisation-settings#dns-records) still lives under the old domain. The refusal names exactly what is blocking it - the portal lists the cluster domains and records beside the field, and `ankra org domain set` prints the same list - so the migration is a checklist rather than a guess: **1. Inventory what is under the current root** `ankra org dns zones` lists every cluster domain in the organisation with its state; `ankra org dns list` lists the DNS records. Both are reads. In the portal the same two lists live under **Organisation** → **Settings** → **DNS records**. **2. Destroy any playground environment** A playground publishes a wildcard DNS record in the organisation's zone, and that record is **reconciled** - the platform writes it again on its next pass over a ready environment, so deleting it in the next step will not clear it. Destroy the environment instead: `ankra cluster playground destroy `. The refusal names any playground that is still holding a record. **3. Remove each cluster domain** `ankra cluster domain --remove` (or `DELETE /api/v1/clusters/{cluster_id}/dns-zone`, or **Remove** on the cluster domain row in the portal) hands the zone to Ankra for teardown. The removal is **held**: nothing re-creates the zone until you ask for it back in the last step - not the `external-dns` Ankra runs on that cluster, and not the discovery that mints zones for clusters that have none. `ankra cluster domain ` reports `Opted out: yes` for as long as the hold stands, so a domain that is gone is distinguishable from one that is between reconciler passes. **4. Delete the DNS records** `ankra org dns delete `, or delete them from the DNS records table. These are the records you created; anything the platform publishes is listed separately in the refusal, with what to remove instead. **5. Register the new domain** `ankra org domain set example.com`, or save the **Custom Ankra domain** field. Ankra tears the organisation zone down and re-creates it under the new domain automatically. **6. Re-enable the cluster domains** `ankra cluster domain --enable` withdraws the hold and re-creates each cluster's zone under the new root, and the cluster's external-dns picks up the new zone on its next cloud-provider stack pass. Every cluster you removed in step 3 needs this, the staging cluster included. > [!NOTE] > > `ankra cluster domain ` on its own is a **read**: it reports the cluster's domain, or state `none` when it has none, and never creates a zone. Creating one is the explicit `--enable`. (Before CLI v0.13.0 the bare command created a zone, which meant checking a cluster's domain could add a blocker to this very migration.) ### What a root-domain switch does and does not touch Two questions decide whether the switch is safe to attempt on a domain that is already in use. Both have short answers. **Ankra touches only the records it creates in the domain you register.** Registering `example.com` adopts it as the organisation's zone apex, with each cluster's own `.example.com` subzone under it, and the `external-dns` Ankra installs on every cluster is scoped to the whole domain so it can publish top-level names like `app.example.com`. Each controller runs with its own record ownership (`--txt-owner-id`), so it creates, updates and deletes only the records that exist because of an Ingress on its cluster: records you already publish at the apex or under any other name in `example.com` are never read, written or deleted - not by the switch, and not by anything Ankra runs afterwards. **A switch never re-labels a zone.** Zone labels are derived from the organisation and cluster ids, so they are the same under any root: a cluster that was `axmndle4sl..ankra.cc` becomes `axmndle4sl..example.com`. The label is also what Ankra passes as `external-dns`'s `--txt-owner-id`, and what any GitOps path built from a cluster domain embeds - so the TXT ownership registry stays matched to its records across the switch, and the paths do not move. `--enable` re-creating a zone preserves the label for the same reason. > [!WARNING] > > `external-dns` on each cluster runs with `--policy=sync` against its own subzone. That is safe here precisely because the label and the owner id survive the switch together: an owner id that changed under a `--policy=sync` controller would be the configuration that deletes records the controller no longer recognises as its own. Ankra does not change it, and nothing in the switch asks you to. ### Serving a zone in your own DNS account from every cluster A custom Ankra domain has to be hosted in Ankra's own DNS account, because Ankra mints the per-cluster credentials for it. A zone you keep in **your own** DNS provider account is different: Ankra cannot mint anything there, so the `external-dns` it installs drops Ingress hostnames on that zone silently (the sync logs `All records are already up to date`, because from its point of view there is nothing in scope), and cert-manager's HTTP-01 challenge for such a hostname waits forever for a name that never resolves. Declare the zone with a credential of your own instead, once, for the whole organisation: ```bash theme={null} ankra org dns credentials create --name my-dns \ --webhook-provider-url https://dns.example/api/external-dns/ # stored in the platform's secret store; never echoed back ankra org custom-dns-zones add --zone example.dev --credential my-dns ankra org custom-dns-zones list ``` Every cluster in the organisation - the ones running now and every one created afterwards - then gets its own `external-dns` for `example.dev`, rendered and reconciled by Ankra in a dedicated `custom-dns` stack, within about two minutes of the declaration or of the cluster coming up. An Ingress on `chat.example.dev` publishes from whichever cluster serves it, and the certificate follows once the name resolves. Each cluster's controller is pinned to exactly that zone (`--domain-filter`) with its own record ownership, so it can never fight the controller Ankra runs for the cluster's Ankra domain, another cluster's controller, or records you publish in the zone yourself. * The credential is the external-dns **webhook provider URL** including its token, for any provider that speaks the external-dns webhook protocol. It is written to the platform's secret store on create and no read surface returns it; re-creating it under the same name re-points every cluster at once, which is how a rotated token rolls out. Its name is what `list` shows. * `ankra cluster custom-dns-zones add --zone --credential ` declares the same thing for **one** cluster. A cluster's own declaration of a zone takes precedence over the organisation's on that cluster - the way to serve a zone with a different credential on one cluster - and `ankra cluster custom-dns-zones list ` shows where each zone a cluster serves was declared (`SOURCE` = `cluster` or `organisation`). * A zone that overlaps the domain Ankra already serves for the organisation (its `.` zone, or the whole custom Ankra domain when one is registered) is refused: Ankra's own controllers publish there already and cannot be told to stay off those names. A zone **above** the Ankra domain is accepted, and each cluster's controller is told to leave the Ankra-served part alone. * A domain in Avura needs no credential of your own: once the Avura workspace is connected to the organisation and the domain is shared with Ankra, declare it with `--credential avura`. See [use Avura domains with Ankra](/guides/avura-domains). * `ankra org custom-dns-zones remove --zone example.dev` withdraws the declaration. Ankra tears down the controllers it rendered on every cluster that inherited the zone; clusters that declared it themselves keep theirs, and the zone's records are yours and are left untouched. The same declarations are available on the API: `GET`/`POST` `/api/v1/org/custom-dns-zones`, `DELETE /api/v1/org/custom-dns-zones/{zone}`, and the per-cluster `/api/v1/clusters/{cluster_id}/custom-dns-zones` twins. ### Deploying a demo A demo deploys **every component** of the application by default. A single-app application runs its one image; a monorepo runs one pod per component — the frontend and the API of a two-app repository both come up, wired together, instead of half an application answering where the other half belonged. You can still deselect components or demo a single one. > [tab] Portal > > Open the application's **Previews** section, choose **Launch preview**, pick a branch or enter a pull request number, and deploy. The dialog groups what to preview, its lifetime and environment, and the staging cluster, and lists the branch, image, lifetime, template, resources, cleanup and access in a summary before the launch button; a missing image or an offline staging agent is an explicit blocker. The section shows one card per active preview with its branch or commit, component, image, expiry, access and cluster. Each preview links to its own detail page - live provisioning progress read from the staging cluster, the namespace's bill of materials with manifests (grouped per component), Kubernetes events, and pod logs, plus the preview URL or port-forward command to reach it. On a monorepo the launch dialog lists every component with its own build status and tag, lets you include or exclude each, and marks the **web entry** — the component that owns the demo URL. > [tab] CLI > > ```bash theme={null} > # Deploy a branch demo > ankra application demo deploy --branch feature/login > > # Deploy a PR demo with an explicit TTL > ankra application demo deploy --pr-number 42 --ttl-hours 8 > > # List active demos, then stop one > ankra application demo list > ankra application demo stop > > # Inspect a demo: record + provisioning steps, and a bounded log tail > ankra application demo detail > ankra application demo logs --tail 200 > > # Read or edit the saved demo defaults > ankra application demo config get > ankra application demo config set \ > --database=true --env 'DATABASE_URL=${{ ankra.demo_database.url }}' \ > --migrate-command 'pnpm run db:migrate' --database-extension vector > > # Dispatch the AI pre-setup mission for a failed demo > ankra application demo fix > ``` > > The deploy response includes `preview_url` when a public host was resolved. `config set` fetches the saved configuration first and applies only the flags you pass, so it never drops entries or dependency designations you did not name. > [tab] AI / MCP > > Ask the AI assistant to demo a pull request, or call the `deploy_pr_demo` tool directly (it is allowed in **Ask** mode - the demo is isolated and self-expiring). The result carries the same `preview_url`, names the `entry_component`, and lists every deployed workload under `components[]` with its image, container port and ingress path - so a multi-component launch reports what it actually provisioned rather than only the component that owns the URL. Pass `entry_component` to choose which component serves `/`. Tear down early with `demo_stop`. Opening a pull request on a connected application also deploys a preview automatically and posts the URL back as a PR status comment that updates in place - see the [PR preview environments guide](/guides/pr-preview-environments) for the comment lifecycle, requirements, and troubleshooting. ### Monorepo demos: every component, one URL A monorepo demo deploys each component as its own Deployment and Service inside the demo namespace, and the demo only reports **ready** once every component accepts connections: * **The web entry owns the demo URL.** Ankra picks the frontend-shaped component (a name or directory like `frontend`, `web`, `ui`, `portal`) as the entry serving `/` on the demo host; you can move the entry in the launch dialog or with `entry_component` on the API. * **API components share the host under a path.** The single API-shaped component (`api`, `backend`, `server`) is published under `/api` on the same demo host, routed straight to its Service — so a browser calling `/api/...` reaches the API even when the frontend's own proxy target was baked for another environment. Override per component with `ingress_path`. * **Save the routing when the guess is wrong.** The entry and the `/api` path above are heuristics, and the lanes with no human in the loop — the automatic PR previews and the MCP demo tools — have no launch dialog to correct them in. Declare the routing once in the demo configuration and every lane follows it: ```json theme={null} { "routing": { "entry_component": "commerce-frontend", "components": [{ "name": "commerce-backend", "ingress_path": "/api/v1" }] } } ``` A declaration is authoritative: the `/api` guess stops running, so a component you leave out of `components` stays reachable in-cluster only rather than picking up a path it never asked for. Per-launch `ingress_path` overrides still win over the declaration. Clear it with `"routing": null` to return every lane to the heuristics. * **Components reach each other by name.** Every component's Service is named after it, so in-namespace URLs like `http://crm-api:8090` resolve. Env values can also use placeholders that resolve at deploy time: | Placeholder | Resolves to | | - | - | | `${{ ankra.demo_component..url }}` | `http://:` inside the namespace | | `${{ ankra.demo_component..host }}` | The component's Service name | | `${{ ankra.demo_component..port }}` | The component's container port | The saved [migration command](#environment-variables-and-a-throwaway-database) runs once per deploy, inside the image of the component that owns the schema (the primary component - usually the backend). Demo environment defaults and the throwaway database are shared by every component of the demo. > [!NOTE] > > Existing demos and single-app applications are unaffected: a demo recorded before multi-component support (or of an application with one component) keeps exactly the previous single-pod shape. ### Environment variables and a throwaway database Most real applications need configuration to boot — a database name, an API key, an SMTP host. Demos support both **per-application defaults** and **per-launch overrides**, so any codebase can run as a demo without changes: * **Defaults** are the application's **environment template**, under **Previews** (*Preview settings*), which separates the disposable dependencies each preview gets from the shared services it points at and labels every generated value with the dependency it comes from. Every demo of the application inherits them — manual launches, CLI/MCP deploys, and the automatic PR previews. * **Overrides** are set per launch in the *Environment & database* section of the launch dialog (or via the `env` argument of `deploy_pr_demo`). Overrides win by name. Values marked **secret** are stored as Ankra secret slots, held in the platform secret store — the plaintext never persists and is mounted into the demo as a Kubernetes Secret at deploy time. **Need a database?** Toggle **Attach a throwaway Postgres** and an ephemeral Postgres is provisioned inside the demo namespace with a random per-demo password, destroyed together with the demo. Because every codebase names its configuration differently, nothing is injected automatically — reference these placeholders in your env *values* and they resolve when the demo starts: | Placeholder | Resolves to | | - | - | | `${{ ankra.demo_database.url }}` | Full `postgres://` connection string | | `${{ ankra.demo_database.host }}` | In-namespace service host | | `${{ ankra.demo_database.port }}` | `5432` | | `${{ ankra.demo_database.name }}` | Database name | | `${{ ankra.demo_database.user }}` | Database user | | `${{ ankra.demo_database.password }}` | The per-demo random password | > [!NOTE] > > The throwaway database is ephemeral by design: it starts empty on every deploy and is wiped with the namespace. The database runs a pgvector-capable Postgres, so migrations using `CREATE EXTENSION vector` work. `.url` and `.password` references are always delivered through a Kubernetes Secret, never as plain env text. **Migrations.** If your image does not run its own migrations on boot, set a **migration command** in the demo configuration (`migrate_command`) — for example `pnpm run db:migrate` or `alembic upgrade head`. It runs inside your application image (`sh -c`) as an init container, after the database accepts connections and with the same environment the app sees, so a fresh demo database provisions its schema before the first request. Because every deploy starts the database empty, the command re-runs on every deploy — write migrations to be idempotent (every standard migration tool is). > [!NOTE] > > The command runs with your image's `WORKDIR` as the working directory **and** on `PYTHONPATH`. Python puts the *script's* directory on `sys.path`, not the working directory, so a migration entrypoint that is a script rather than a console script — `python scripts/bootstrap_database.py` — could not import the application package sitting beside it and died at import with `ModuleNotFoundError`. Setting `PYTHONPATH` yourself in the command still wins. **Extensions.** If your schema needs Postgres extensions beyond what migrations create themselves, list them in `database_extensions` (for example `["vector"]`) and the demo database creates them at initdb, before your migrations run. Ankra detects both automatically when it analyses a repository: a Postgres client dependency, an ORM configuration, or a composed database marks the application as database-needing, its referenced connection variables are wired to the `${{ ankra.demo_database.* }}` placeholders, and a recognised migration script becomes the migration command — so the first demo of a database-backed application works without any manual configuration. Detection only ever seeds an application that has no demo configuration yet; it never overwrites what you or the pre-setup agent saved. ### The demo container port The demo's readiness probe, Service, and preview route all target one container port, resolved in this order: the component's recorded port, the generated runtime Dockerfile's `EXPOSE`, the generated Deployment manifest's `containerPort`, the analysed `target_port` parameter, and only then the platform default. The launch dialog shows the resolved port and where it came from; a port you did not edit is left to the platform to resolve. When the resolved port is still wrong — stale analysis, a hand-edited Dockerfile — the platform corrects itself at runtime: a demo whose container runs cleanly but never accepts connections has its logs read for the port the server actually announces (`Accepting connections at…`, `Listening on…`, and the other common startup banners). If the evidence is unambiguous, the demo is repointed at the announced port on the fly, the correction is recorded on the application so the next launch resolves it statically, and the demo detail page shows the correction. Ambiguous evidence never auto-corrects; it flows into the failure message ("Nothing accepted connections on port 3000. The container's logs say it listens on port 8001.") and dispatches the pre-setup agent instead. ### Automatic pre-setup when a demo crashes An image that validates its environment on boot — refusing to start without a database URL, an auth secret, an API key — crash-loops when demoed with no configuration. Ankra now detects this and fixes it with an agent: 1. When a demo fails with a **startup crash** (CrashLoopBackOff or a container configuration error), a **failed migration command**, or a port-evidence timeout (the container announced a different port than the demo probed), Ankra dispatches a one-shot **pre-setup agent** for it automatically. The run appears on the **AI agents** page like any other mission. 2. The agent reads the crashed container's logs, works out which environment variables the application demands, and generates a **pre-setup**: missing database URLs become `${{ ankra.demo_database.* }}` references with the throwaway Postgres enabled, secrets get fresh random values, and mode flags get the value that avoids external side effects. 3. It saves the pre-setup as the application's demo defaults — so **every future demo inherits it** — and redeploys the failed demo to prove it boots. The agent can also set the migration command and database extensions when the logs show schema or `CREATE EXTENSION` failures. It never weakens the application's own validation, never reuses values found in logs, and merges with existing demo settings rather than replacing what you configured. Dispatch is bounded to one run per demo per day; image-pull failures and provisioning timeouts never dispatch (no environment can fix those). You can also trigger it on demand with `POST /org/applications/{application_id}/demos/{workspace_id}/fix`, or just ask the AI assistant to fix the failed demo — the chat has the same `get_demo_diagnostics`, `update_application_demo_config`, and `redeploy_demo` tools the mission uses. *** ## Managing applications | Action | What it does | | - | - | | **Retry** | Re-run analysis and generation after fixing an issue (for example, adding the right credential or repository permissions) | | **Reconcile** | Re-evaluate the application against its repository and refresh its state | | **Delete** | Disconnect the application from Ankra | Which Ankra AI lanes run on the application's repository - the pull request review, the organisation skills review, the automatic preview URL - is set per application under **Settings** → **Ankra AI**. See [Application AI settings](/platform/application-ai). *** Built the app with Claude? Watch it shipped to Kubernetes on Ankra. [From the Claude App to Kubernetes: ship a Claude-built app on Ankra](https://www.youtube-nocookie.com/embed/vvKHNo-6ERQ?rel=0) ## Prerequisites **1. Connect GitHub** Add a [GitHub credential](/integrations/github) with access to the application's repository. The Ankra GitHub App needs permission to open pull requests, commit workflow files, and manage Actions secrets. Ankra installs the Ankra registry credentials on the repository automatically; for [your own registry](#using-your-own-registry) you set the login secrets yourself. **2. Have a target cluster** Make sure you have a [cluster](/guides/import-cluster) with the [agent](/concepts/cluster-agent) connected to deploy onto. *** ## API Applications are available over the API for CLI and scripted use, under `/api/v1/org/applications` (bearer-token authenticated) - create, list, inspect, retry, reconcile, and delete. See the [API Reference](/api-reference/introduction) for endpoints and schemas. The same lifecycle is available through Ankra's AI and MCP clients - connect, deploy, retry, reconcile, and delete applications, and follow their CI workflow runs - see the [MCP Tool Reference](/platform/mcp-tools#applications). See the [CI/CD Pipeline guide](/guides/cicd-pipeline) for how the generated pipeline fits into GitOps. # Backup Vaults Source: https://docs.ankra.ai/concepts/backup-vaults How backup vaults, stack backup policies, restore points, runs and backup posture fit together, and why Ankra copies data between clusters by restoring from a backup. > [!NOTE] > > A backup vault is an organisation-level record of an S3-compatible bucket you own, plus the credentials to reach it. Everything Ankra backs up is written there; nothing passes through the platform. ## The five objects | Object | Scope | What it is | | - | - | - | | **Backup vault** | Organisation | A bucket + credentials, verified by Ankra before use. One organisation can have several - a production vault and a staging vault, or one per region. | | **Backup policy** | Stack | "Protect this stack": a vault, a schedule and a retention. Stored as the stack's own `backup` block, not as a separate object. | | **Restore point** | Stack | One captured backup: when it was taken, what triggered it, the assets in it, what it could **not** carry, its size, and its status. | | **Run** | Organisation | The unit of work that takes, restores or clones a restore point, with a step for each thing it does. Every capture answers with the run that will seal it. | | **Backup posture** | Stack, cluster, fleet | The answer to "is this data protected, and if not, why" - `protected`, `unprotected` or `unknown`. | ## Where the data flows ``` source cluster ──write──▶ vault bucket ◀──read── target cluster (agent) (your S3) (agent) ``` The source cluster's agent writes backups straight to the bucket. A target cluster - another Ankra cluster, or a UAT environment - reads them back over HTTPS. The platform orchestrates and records; it never holds a byte of your data, which is why a vault must be reachable from the clusters themselves and not just from Ankra. ## Why backup-first Copying a database live between two clusters needs a network path between them, a consistent snapshot, and a way to replay changes - and it produces nothing durable. Restoring from a backup needs only the bucket, produces a disaster-recovery copy as a side effect, and is how every database operator already models its own recovery. Ankra therefore recommends a backup policy on every stateful stack. ## Engines The engine that captures an asset decides how consistent its restore point is, and every engine records its honest answer in the restore point rather than an aspiration. | Engine | Handles | Consistency | Restore in place | | - | - | - | - | | Velero | Any persistent volume (Redis, Prometheus, anything on a PVC) | **Crash-consistent.** The workload's own replay (WAL, AOF) finishes the job on restore | Supported | | Logical dump | PostgreSQL and MySQL captured as a dump, including every `ankra migrate` import | Transactional per dump | Supported | | CloudNativePG | PostgreSQL clusters run by the CNPG operator, captured through the barman-cloud plugin | Transactional, with point-in-time recovery | **Not yet** - see below | | Percona | MySQL clusters run by the Percona operators, captured through the operator's own backup | Transactional (XtraBackup) | **Not yet** - see below | A volume owned by a database operator is backed up by that operator's engine, never twice. **Volumes are captured two different ways, and which one you get is measured, not assumed.** Where the platform has observed the cluster's CSI driver and that driver can take a volume snapshot, Velero moves the data through a snapshot. Where it has observed that the driver cannot - Hetzner's CSI is the case to know about - or where it has not observed the cluster's storage at all, Velero falls back to file-system backup: slower, universally available, and honest. A cluster the platform has never seen is never assumed to support snapshots, because a render that assumed it would silently produce nothing. The restore point records which it was. ## What a restore point carries A restore point is self-describing: a schema-versioned manifest naming every asset in it, the source cluster and its Kubernetes version, the sizes, and the list of what the capture could **not** take. **That last list is a first-class field, not a footnote.** Every engine reports what it left behind and why, and every listing and detail read carries it - in the CLI, in the API and in the dashboard. A volume outside the selection, a data engine no adapter covers, an external managed service that lives outside the cluster: each appears with a reason and a remedy. Silence about un-carried data is the one failure mode that turns a backup product into a liability, so a backup listing that showed sizes and hid omissions would defeat the point of having one. ## Restoring in place Restoring a restore point over the stack it came from destroys before it replaces: the workloads that own the data are scaled down, the volumes the restore point replaces are removed, the assets are restored, and the workloads are scaled back up. That sequence is printed before any of it runs, and typing the stack's own name is what starts it. > [!WARNING] > > **A restore point carrying a CloudNativePG or Percona database cannot be restored in place yet, and Ankra refuses rather than pretending.** Velero's update policy patches an object's metadata, so a bound claim keeps the volume it was already bound to and the data on disk is never replaced; CloudNativePG's bootstrap runs once, at cluster creation, and an existing cluster ignores it. Running the ordinary path anyway would report success over unchanged data, which is worse than refusing. Those databases are still captured, and still restorable into a new cluster once that lane ships. ## Backup posture Posture is the same verdict rendered on the stack, the cluster and the fleet, and it has three states rather than two: | State | Means | | - | - | | **Protected** | The stack has an enabled policy naming a vault that is ready. | | **Unprotected** | No policy, or a policy whose vault is missing or not ready, or one that excludes databases from a stack that runs them, or a stack that holds no data at all. The reason is always given. | | **Unknown** | The platform could not tell: the stack's `backup` block did not decode, the vault lookup could not be answered, or the cluster's inventory has not landed. | There is deliberately no boolean beside this. "We could not tell" is a different answer from "this is not protected", and collapsing the two is how a cluster nobody can read gets reported as a cluster with nothing to protect. ## Two ways a vault comes to exist An **Ankra-provisioned** vault (`ankra_provisioned`) is created from one of the organisation's provider credentials: the platform creates the bucket on that provider, mints the access keys where the provider's API allows it (UpCloud, DigitalOcean), reuses the credential's own keys where they already are S3 keys (Scaleway), or stores the pair the user supplied where the provider issues keys only in its console (Hetzner). A **bring-your-own** vault (`customer_s3`) registers a bucket that already exists, on any S3-compatible service, with its keys. Both end up identical from the backup tooling's point of view: an endpoint, a bucket, a key pair and a repository password in the secret store. Provisioning runs as a platform job: the vault is recorded in `provisioning` the moment the request is accepted, and moves to `ready` or `error` when the job finishes. Every step of the job is written to be repeatable, so running it again reuses what an earlier attempt created rather than creating it twice - but nothing re-runs it on its own today, so a failed run is recovered by deleting the vault and provisioning again. ## Credentials Vault credentials live in Ankra's secret store from the moment you submit them. They are never returned by the API, the dashboard or the CLI, never written into a cluster's stack definitions, and never included in exports. Inside a cluster, the backup tooling reads them from a Secret the platform manages. The vault also holds one **repository password**, minted when the vault is created, which encrypts the volume data in the bucket. Every cluster pointed at that vault is given the same one. A per-cluster password would make a restore point readable only from the cluster that wrote it, which would make restoring into a second cluster impossible - so a vault without a stored repository password is refused rather than allowed to fall back. Deleting a vault removes Ankra's record and credentials; the bucket and its objects are untouched unless you explicitly ask for the provider resources to be destroyed with it, which Ankra only offers for a vault it provisioned itself. ## The backup data plane Protecting the first stack on a cluster installs `ankra-backup`, a system stack Ankra owns and keeps current: Velero and its node agent, a CSI snapshot controller and `VolumeSnapshotClass` where the driver supports one, the CloudNativePG barman-cloud plugin where the cluster runs that operator, and cert-manager for that plugin's certificates where the cluster does not already have one. It is installed the same way on every kind of cluster - one Ankra provisioned, one you imported, a managed-Kubernetes cluster, a playground - because a backup product that only protects the clusters the platform built is not a backup product. You do not author it, and you should not edit it. Unprotecting the last stack never removes it either: the restore points already taken are restored *through* it, so tearing it out would make them unrestorable. ## Permissions `backups.read` sees vaults, policies, restore points, runs and posture; `backups.operate` verifies a vault, takes restore points and runs restores; `backups.manage` creates and deletes vaults, sets policies and deletes restore points. Organisation admins hold all three. Permissions are the second gate, not the first: the organisation's **backups** feature is checked before them, so an organisation without the feature answers the same way whatever the caller may do. Beyond that, every restore point is addressed through the cluster and stack it was taken from, and one that belongs to a different cluster's stack of the same name answers exactly as an absent one does - `shop` is not a rare stack name, and two clusters must never see each other's restore points through their own URLs. ## Related > [card] [Protect a stack and restore it](/guides/backups) > > Vaults, protection, restore points, restore in place, runs and troubleshooting. > [card] [The backup block](/reference/importcluster#spec-stacks-backup) > > Every field and default of a stack's backup policy. # Ankra Agent Source: https://docs.ankra.ai/concepts/cluster-agent Learn how the Ankra Agent connects your Kubernetes cluster to the platform. Ankra has to reach into your cluster to deploy Stacks, read resources and stream logs, but you should not have to open the cluster to the internet for that. The **Ankra Agent** solves this: it runs inside the cluster, opens an outbound connection to Ankra, and carries every request over it. [How Ankra Works](/concepts/how-ankra-works) shows where the agent sits in the overall model. The agent ships as a single Go binary on the **2.x version track** (`2.0.x`), installed with a Helm chart into the `ankra` namespace. Whether the platform upgrades a cluster's agent for you depends on the auto-upgrade settings described in [Upgrading the Agent](#upgrading-the-agent). > [!NOTE] > > The agent requires **cluster-admin** permissions to manage all Kubernetes resources and deploy add-ons. ## What the Agent Does * **Resource browsing** - lists and watches Deployments, Pods, Services and other resource types for the portal, CLI and AI. * **Pod logs** - streams container logs to Ankra. * **Deployments** - installs and upgrades your Stacks' add-ons and manifests with the native Helm engine (the default) or through ArgoCD. See [Deployment Engines](/concepts/deployment-engines). * **kubectl access** - proxies authenticated `kubectl` requests to the cluster API server with no inbound ports. See [Accessing Clusters with kubectl](/guides/kubeconfig). * **Fleet map reporting** - optionally reports the cluster's public egress IP so it appears on the [Dashboard](/platform/dashboard) world map. *** ## Installation When you import a cluster, Ankra generates a Helm install command with a unique token. It looks like this: ```bash theme={null} helm upgrade --install ankra-agent oci://registry.ankra.cloud/ankra/ankra-agent \ --version \ --set config.ankra_url=https://platform.ankra.app \ --set config.token= \ --namespace=ankra --create-namespace ``` Copy the exact command from the Import dialog rather than typing this one: it pins the agent version and carries your cluster's token. The agent will connect to the platform and your cluster will appear online within seconds. ### Verify Installation Check the agent is running: ```bash theme={null} kubectl get pods -n ankra ``` View agent logs: ```bash theme={null} kubectl logs -n ankra -l app.kubernetes.io/name=ankra-agent -f ``` *** ## Cluster States Every cluster carries one status badge, shown on the clusters list, in the cluster's sidebar and in the activity drawer. It combines whether the agent is connected with the health of what runs on the cluster: | Status | What it means | | - | - | | **Healthy** | The agent is connected and nothing needs attention. | | **Needs Attention** | At least one thing needs a person: the agent is offline, a resource is failing, the last operation failed, GitOps sync is failing or has a merge conflict, a resource is stuck pending, the kubelet version skew is unsupported, or there is an open AI insight. The badge is red when any of these is critical (agent offline, failing resources, a failed operation, failing GitOps sync, unsupported kubelet skew) and amber otherwise. | | **Awaiting Connection** | An imported cluster whose agent has never connected. Run the install command to finish the import. | | **Provisioning** | Ankra is building the cluster. The agent connects when setup completes. | | **Stopped** | The cluster has been powered off on purpose. Start it to resume live data. | | **Archived** | The cluster is parked and monitoring is paused. | | **Destroying** | A playground cluster is being torn down. It leaves the list when the teardown finishes. | The activity drawer lists the reasons behind a **Needs Attention** badge. A real failure always wins: a cluster that is provisioning, awaiting connection or stopped still shows **Needs Attention** if an operation failed or a resource is failing. While the agent is offline, the cluster's workloads keep running, but Ankra cannot deploy to the cluster or show live resources and logs. Its configuration and history stay available. To bring it back, see [Agent Not Connecting](#agent-not-connecting). *** ## Configuration Reference ### Required Settings | Parameter | Description | | - | - | | `config.token` | Authentication token (provided during cluster import) | | `config.ankra_url` | Platform URL (default: `https://platform.ankra.app`) | ### Using an Existing Secret For production environments, store the token in a Kubernetes secret: ```bash theme={null} kubectl create secret generic ankra-agent-secret \ --namespace ankra \ --from-literal=token=YOUR_UNIQUE_TOKEN ``` Then reference it in your Helm install: ```bash theme={null} helm upgrade --install ankra-agent oci://registry.ankra.cloud/ankra/ankra-agent \ --namespace ankra \ --set config.existing_secret_name=ankra-agent-secret \ --set config.secret_key=token ``` ### Performance Tuning For large clusters (1000+ resources), adjust these settings: | Parameter | Default | Description | | - | - | - | | `nats_worker_max_workers` | `15` | Worker threads for command processing | | `read_worker_count` | `5` | Concurrent worker slots for read jobs | | `write_worker_count` | `5` | Concurrent worker slots for write jobs (create/update/delete) | | `resources.limits.memory` | `512Mi` | Memory limit | | `resources.requests.memory` | `256Mi` | Memory request | | `replica_count` | `1` | Number of agent replicas | Example for large clusters: ```bash theme={null} helm upgrade --install ankra-agent oci://registry.ankra.cloud/ankra/ankra-agent \ --namespace ankra \ --set config.token="YOUR_TOKEN" \ --set nats_worker_max_workers=25 \ --set resources.limits.memory=1Gi \ --set resources.requests.memory=512Mi ``` ### Fleet world map (public IP reporting) To place an imported cluster on the [Dashboard](/platform/dashboard) world map when it has no recognisable cloud region, let the agent report its public egress IP on check-in: ```bash theme={null} helm upgrade --install ankra-agent oci://registry.ankra.cloud/ankra/ankra-agent \ --namespace ankra \ --set config.token="YOUR_TOKEN" \ --set public_ip.reporting_enabled=true ``` When enabled, the agent performs an outbound HTTP lookup (to `public_ip.lookup_url`, default `https://api.ipify.org`) and reports the result. Leave it **disabled** (the default) for air-gapped clusters or where egress IP lookups are undesirable. ### All Helm Values The complete value list - connection, workers, watch tuning, public IP reporting, image, security contexts, scheduling, and metrics - lives in the [Agent Helm Values reference](/reference/agent-helm-values). > [!NOTE] > > The agent runs as a **non-root** container by default (`runAsNonRoot: true`, UID/GID `1000`, `readOnlyRootFilesystem: true`, all Linux capabilities dropped, `seccompProfile: RuntimeDefault`). Because the root filesystem is read-only, the chart mounts writable `emptyDir` volumes for `/tmp`, `~/.cache`, and `~/.config` via `writable_volumes.enabled` (default `true`). Don't lower these security settings unless you are running a custom image. ### Advanced tuning via `extra_env` Less-common knobs are set as environment variables through `extra_env`: | Env var | Default | Description | | - | - | - | | `KUBERNETES_HTTP2_ENABLED` | `false` | Enable HTTP/2 to the API server (only if the path supports it cleanly) | | `KUBERNETES_REQUEST_MAX_ATTEMPTS` | `3` | List/get retry attempts on connection-terminated errors | | `KUBE_PROXY_UNARY_CONCURRENCY` | `64` | Max concurrent unary requests through the kubectl proxy | | `KUBE_PROXY_STREAM_CONCURRENCY` | `128` | Max concurrent streaming connections (watch/logs/exec) through the proxy | | `DYNAMIC_API_DISCOVERY_ENABLED` | `false` | Discover and watch additional API groups dynamically | | `RECONCILE_AUTO_HEAL_ENABLED` | `true` | Auto-heal native-engine releases stuck mid-operation | | `FORWARD_SERVICE_REQUEST_MAX_TIMEOUT_SECONDS` | `120` | Upper bound for platform-requested forwarded-request timeouts (e.g. Prometheus) | ```yaml theme={null} extra_env: - name: KUBERNETES_REQUEST_MAX_ATTEMPTS value: "5" ``` *** ## Architecture The agent uses a NATS-based architecture for real-time communication: ```mermaid theme={null} flowchart TB Portal[Ankra Portal] <-->|HTTPS| Platform[Ankra Platform] Platform <-->|NATS JetStream| Agent[Ankra Agent] Agent <-->|Kubernetes API| Cluster[Your Cluster] ``` **Key features:** * **Outbound connections only** - The agent initiates all connections, no inbound ports required * **Real-time streaming** - Resource data streams efficiently using pagination * **Automatic reconnection** - Handles network interruptions gracefully * **kubectl proxy** - Forwards authenticated `kubectl` requests (including `watch`, `logs -f`, and `exec`) from the platform to the cluster API server, so you can reach private clusters without inbound access. See [Accessing Clusters with kubectl](/guides/kubeconfig). * **Health monitoring** - Exposes `/livez` and `/readyz` endpoints on port 8080 *** ## Network Requirements The agent requires outbound connectivity to: | Endpoint | Port | Purpose | | - | - | - | | `platform.ankra.app` | 443 | API communication | | `command.platform.ankra.app` | 4222 | NATS real-time streaming | No inbound ports need to be opened on your cluster. *** ## Upgrading the Agent New agent releases are published on the `2.0.x` version track. When auto-upgrade is on, the platform rolls clusters forward to the latest published version, a few clusters at a time. You can still trigger an upgrade yourself at any time. * Organisations created since 7 September 2026 have **Auto-upgrade Agents** on by default. Older organisations turn it on in **Organisation Settings** → **General**. * A single cluster can opt out with **Disable Auto-upgrade** in its own settings. * Every change to either switch is recorded in the [audit log](/guides/audit-log) (as `set_agent_auto_upgrade` for a cluster, on `update_organisation` for the organisation) with who made it, the setting it replaced and, when one was given, the reason (`ankra cluster agent auto-upgrade disable --reason "..."`, or `reason` on the MCP tool `set_cluster_agent_auto_upgrade`). Changes to the agent's sizing and pipeline settings are recorded the same way. ### From the Platform In the cluster's **Settings** → **General**, click **Upgrade to v\**. The agent will self-upgrade using Helm. ### From the CLI ```bash theme={null} ankra cluster agent upgrade --cluster prod # the newer of the running and the latest stable release ankra cluster agent upgrade --cluster prod --version 2.1.1250 # exactly that published release ankra cluster agent upgrade --cluster prod --version 2.1.1247 --allow-downgrade # roll back on purpose ``` An upgrade you trigger never moves an agent backwards on its own: without `--version` an agent already on a newer release stays on it. A `--version` older than the running release is refused unless `--allow-downgrade` is also given, and a release that is neither published nor the one the agent runs is refused too; both refusals say why and change nothing. The MCP tool `upgrade_cluster_agent` takes the same `version` and `allow_downgrade`. See [`ankra cluster agent upgrade`](/reference/cli/cluster#ankra-cluster-agent-upgrade). ### Manually ```bash theme={null} helm upgrade ankra-agent oci://registry.ankra.cloud/ankra/ankra-agent \ --namespace ankra \ --reuse-values ``` Check the current agent version: ```bash theme={null} kubectl get deployment -n ankra ankra-agent -o jsonpath='{.spec.template.spec.containers[0].image}' ``` *** ## Troubleshooting ### Agent Not Connecting 1. **Check agent pods are running:** ```bash theme={null} kubectl get pods -n ankra ``` 2. **View agent logs:** ```bash theme={null} kubectl logs -n ankra -l app.kubernetes.io/name=ankra-agent --tail=100 ``` 3. **Verify network connectivity:** ```bash theme={null} kubectl run --rm -it --restart=Never debug --image=curlimages/curl -- \ curl -s https://platform.ankra.app/health ``` 4. **Check the token is set:** ```bash theme={null} kubectl get secret -n ankra ankra-agent -o jsonpath='{.data.token}' | base64 -d ``` ### Common Issues | Issue | Cause | Solution | | - | - | - | | Cluster shows **Needs Attention** with *Agent offline* | Agent not running or outbound network blocked | Check the agent pods and firewall rules | | Cluster stays **Awaiting Connection** | The agent was never installed, or cannot reach Ankra | Run the install command from the Import dialog, then check the agent pods | | Token invalid | Token expired or revoked | Go to Clusters → Your Cluster → Settings → Generate Command to get a new install command | | Connection refused | Outbound network blocked | Allow connections to `platform.ankra.app:443` | | Resources not loading | Agent memory limits too low | Increase `resources.limits.memory` | ### Health Checks The agent exposes health endpoints: ```bash theme={null} kubectl port-forward -n ankra svc/ankra-agent 8080:8080 curl http://localhost:8080/livez curl http://localhost:8080/readyz ``` *** ## Uninstalling To remove the agent from your cluster: ```bash theme={null} helm uninstall ankra-agent -n ankra kubectl delete namespace ankra ``` > [!WARNING] > > Uninstalling the agent will disconnect your cluster from Ankra. You'll need to re-import it to reconnect. *** ## Security ### RBAC Requirements The agent requires cluster-admin permissions to: * Browse all Kubernetes resources * Deploy Helm charts and manifests * Manage add-ons via the native Helm engine or ArgoCD * Stream pod logs and proxy authenticated `kubectl` requests The Helm chart creates a `ClusterRoleBinding` with the necessary permissions. ### Token Security * Tokens are unique per cluster * Tokens can be revoked by deleting the cluster from Ankra * Store tokens in Kubernetes secrets (not in Helm values) for production # Deployment Engines Source: https://docs.ankra.ai/concepts/deployment-engines How Ankra deploys and reconciles addons on your clusters. The deployment engine is the part of Ankra that turns a Stack's add-ons into Helm releases on your cluster and keeps them there. You rarely choose one: new clusters use the native engine, and clusters created before it keep ArgoCD until you migrate them. This page explains what each engine does, how an update is applied, and how to move a cluster from ArgoCD to native. [How Ankra Works](/concepts/how-ankra-works) shows where the engine sits in the overall model. ## Overview Ankra supports two deployment engines for add-ons: * **Native (`ankra_native`)**: The Ankra agent uses the Helm CLI directly to install, upgrade, and reconcile chart-based add-ons. Releases are written to standard Helm storage (`helm.sh/release.v1` Secrets). * **ArgoCD (`argo_cd`)**: The Ankra agent talks to an ArgoCD installation in the cluster, which renders the chart and applies it via server-side apply. The engine is set per add-on, with a cluster-level default. New clusters use the native engine. Clusters created before it keep ArgoCD until you [migrate](#migrating-a-cluster-to-the-native-engine). ## Why migrate? The native engine reduces the dependency surface on your cluster: * No `argocd-server`, `argocd-application-controller`, `argocd-repo-server`, `argocd-redis`, `argocd-applicationset-controller`, `argocd-notifications-controller`, or `argocd-dex-server` to run on the cluster. * No JWT lifecycle. The agent's existing ServiceAccount is the only auth. * Add-on status comes from the agent directly, not from ArgoCD. On the native engine every add-on is a normal Helm release, so `helm list`, `helm history`, `helm rollback` and `helm get manifest` work directly against your cluster. ## Feature parity | Feature | ArgoCD | Native | | - | - | - | | Self-heal on drift | Yes | Yes (per-addon `sync_policy.self_heal`) | | Prune orphaned resources | Yes | Yes (`auto_prune` + post-apply orphan sweep) | | `ignoreDifferences` (jsonPointers / jqPathExpressions / managedFieldsManagers) | Yes | Yes | | Sync windows | Yes | Yes (cron-style schedule + duration + timezone) | | Custom retry policy | Yes | Yes | | `strict_health_check` | Yes (Healthy only, not Progressing) | Yes (Healthy only, not Progressing) | | `revision_history_limit` | Yes | Yes (`--history-max`) | | SOPS-encrypted values | Yes (helm-secrets) | Yes (direct `sops --decrypt`) | | GitHub repos | Yes | Yes (HTTPS Contents API) | | Bitbucket Cloud repos | Yes | Yes (HTTPS `2.0` Contents API, OAuth refresh) | | GHCR via git token fallback | Yes | Yes | | Multi-source applications | Yes | Not yet (single-source only) | | ApplicationSet | Yes | Not used | A cluster's GitOps repository can be on GitHub or Bitbucket Cloud. Bitbucket Data Center is not supported as a GitOps source under either engine. ## Applying an update Installing or updating an add-on on the native engine runs `helm upgrade --install`, and the step does not succeed until the release is healthy. Two waits sit behind that, and both matter when an update cannot converge on its own. Helm waits first. The agent passes `--rollback-on-failure --cleanup-on-fail`, which makes Helm wait for the release's resources to become ready and roll the release back if they do not, whether or not `--wait` is passed. The budget is the add-on's `helm_wait_timeout_seconds` setting, 600 seconds by default. The automatic rollback then starts a fresh wait with the same budget, so a failing update can occupy twice the configured timeout before the step reports. The agent waits second, watching the rendered resources after Helm returns. If that wait expires, the agent rolls the release back to its previous revision - or uninstalls it, when the update was the add-on's first install. Two add-on settings switch both behaviours off: `rollback_on_failure: false` and `keep_failed_resources_for_debug: true`. With either set, Helm applies the release and returns without waiting and nothing is rolled back. The agent still watches the rollout and still reports a timeout if the resources never become healthy, but the release keeps the new revision. > [!WARNING] > > An update whose resources cannot become ready without a further action from you > will not stick under the default settings. The common case is a DaemonSet with > `updateStrategy: OnDelete`: its Pods keep the old spec until something deletes > them, so the release never reaches ready, Helm times out and rolls back, and the > add-on is left on its previous values. > > Apply such a change either by deleting the affected Pods while the wait is still > running, or by setting `rollback_on_failure: false` on the add-on for that > update and deleting the Pods afterwards. In the second case the step still > reports a rollout timeout, but the new revision stays applied. If the agent stops while a rollback is running, the release can be left in `pending-rollback`, which blocks every later Helm operation on it. Clearing that by hand is not necessary: the next update of the add-on recovers the release before retrying, and so does the reconcile sweep below. ## Reconcile loop The native engine reconciles each add-on on its own schedule. Ankra asks the agent to reconcile an add-on whenever it is due. An add-on that just changed or failed is checked again within a minute or two; one that keeps coming back unchanged backs off in steps (5, 10, 20 minutes) to a check every 30 minutes, and a repeatedly failing one backs off to once an hour. Any change you make to the add-on makes it due immediately. The schedule lives in the platform, not in the agent. While a cluster cannot reach Ankra its workloads keep running untouched, but nothing is reconciled, deployed or self-healed until the connection returns; the agent reconnects on its own and the next due reconcile picks up where things stand. If the same drift reappears on three consecutive reconciles, self-heal for that add-on is paused for six hours rather than fighting whatever keeps changing the resource. Each sweep: 1. Recovers any release stuck in `pending-*` for more than 10 minutes: first attempts a fresh `helm upgrade --install --atomic`; if that fails, rolls back to the last `deployed` revision. 2. Detects drift against `helm get manifest` (live resources fetched in parallel; Helm-injected metadata stripped from both sides before diff). 3. If drift is detected and `sync_policy.self_heal=true`, marks the addon for update so the existing reconciler re-runs `helm upgrade --install`. 4. If `sync_policy.auto_prune=true`, deletes orphaned resources whose UID is no longer in the rendered manifest. ## Sync windows Set `settings.sync_window.enabled=true` on the addon and provide a 5-field cron expression and duration. The agent will only run `create`/`update`/`self-heal` operations inside the configured window. Outside the window, jobs return immediately as `cancelled` with an "outside\_sync\_window" task message. ## Moving an add-on between namespaces The native engine supports relocating an add-on to a different namespace. When you change an add-on's target namespace, the agent installs the release into the new namespace and cleans up the resources left behind in the old one, so you don't end up with orphaned objects. As with any namespaced move, persistent data tied to the old namespace (for example, `PersistentVolumeClaim`s) is not automatically carried over - plan for data migration where relevant. ## Migrating a cluster to the native engine Moving a cluster from ArgoCD to the native engine is one flow in the portal: 1. Open the cluster's **Settings** → **General** and scroll to the **Danger Zone**. 2. Click **Decommission ArgoCD**. Ankra first runs a preflight check and shows the add-ons that will migrate, anything that blocks the migration (resolve those add-on issues first), and warnings you have to acknowledge. 3. Confirm with **Permanently decommission ArgoCD**. Ankra migrates every ArgoCD-managed add-on to the native engine, then removes the Ankra-managed ArgoCD user. Workloads keep running during the migration, and re-running a failed migration is safe. Each add-on is adopted with `helm upgrade --install --take-ownership`, so its live resources move into Helm storage without being recreated: resource UIDs do not change and no Pod restarts. The only change on the cluster is Helm's `app.kubernetes.io/managed-by=Helm` label and the `meta.helm.sh/release-name` / `meta.helm.sh/release-namespace` annotations. The decommission leaves the ArgoCD installation itself and any ArgoCD `Application` CRs Ankra does not manage in place. Many teams keep using ArgoCD outside Ankra. If you no longer need it, uninstall it yourself with the tooling you installed it with (`helm uninstall argocd`, `kubectl delete -f install.yaml`, or `argocd uninstall`), and remove the leftover `accounts.ankra` and `policy.csv` entries from the `argocd-cm` and `argocd-rbac-cm` ConfigMaps if you keep ArgoCD running. The migration of addons is **one-way**: changing the cluster default deployment engine back to ArgoCD after decommission is not supported. # GitOps Source: https://docs.ankra.ai/concepts/gitops Manage your clusters with GitOps using one directory of YAML files per cluster in Git A change made only in a portal has no review step and no history your team controls. Connecting a Git repository to a cluster gives you both: Ankra writes the cluster's Stacks to the repository, and commits you push are applied to the cluster. Sync runs both ways, and a repository is optional - a cluster without one is fully supported. [How Ankra Works](/concepts/how-ankra-works) shows where Git sits in the overall model. *** ## Repository Structure There are two ways a cluster's files end up in a repository, and they can look different. **The layout Ankra writes.** When you connect a repository, or change a cluster in the portal, CLI, API or through Ankra's AI, Ankra writes the cluster to its own directory: ``` my-repo/ clusters/ my-cluster-/ my-cluster.yaml # the cluster's ImportCluster definition .ankra-owned.json # what Ankra last wrote (never edit or delete) stacks/ monitoring/ add-ons/ kube-prometheus-stack/ values.yaml manifests/ monitoring-namespace.yaml ``` Edit these files and push; Ankra applies the change. [What Ankra Writes Back to Git](#what-ankra-writes-back-to-git) explains how it avoids overwriting your edits. **Files you add by hand.** Ankra reads one directory per cluster, the directory shown above. The file in it named after the cluster, `my-cluster.yaml`, is the cluster file, and it is the only file Ankra reads as a cluster definition. Every other file in the directory is content: it takes effect when an entry in the cluster file names it with `from_file`. You can add your own files anywhere in the directory except under `argocd/`, in subdirectories too: ``` my-repo/ clusters/ my-cluster-/ my-cluster.yaml # the cluster file manifests/ logging-namespace.yaml logging-namespace.AGENTS.md logging-rbac.yaml values/ fluent-bit.yaml ``` The optional `AGENTS.md` sibling files hold per-manifest and per-add-on operational learnings for your team and Ankra's AI - see [Teach Your Agents with AGENTS.md](/platform/agents-md). *** ## Cluster Definition Example The cluster file lists every manifest and add-on by `name` and points at its file with `from_file`. For the directory above, `my-cluster.yaml` reads: ```yaml theme={null} apiVersion: v1 kind: ImportCluster metadata: name: my-cluster spec: git_repository: provider: github credential_name: my-credential branch: main repository: my-org/my-repo stacks: - name: logging manifests: - name: logging-namespace from_file: manifests/logging-namespace.yaml agents_md_from_file: manifests/logging-namespace.AGENTS.md - name: logging-rbac from_file: manifests/logging-rbac.yaml parents: - name: logging-namespace kind: manifest addons: - name: fluent-bit chart_name: fluent-bit chart_version: 0.49.1 registry_name: fluent registry_url: https://fluent.github.io/helm-charts namespace: logging configuration: from_file: values/fluent-bit.yaml parents: - name: logging-namespace kind: manifest ``` Each `from_file`, `agents_md_from_file` and `description_from_file` is a path relative to the cluster directory, and Ankra looks it up exactly as written among the files in that directory. There is no folder or wildcard form, a path that leaves the directory never matches, and Ankra never follows a symlink. When a manifest, an add-on's values, an application or a stack description names a file that is not there, the sync stops with an error ending `which is not in the repository snapshot` and applies nothing; a missing `AGENTS.md` file is skipped. An entry without a `name` stops the sync too: every manifest, add-on and application entry needs one. One kind of file needs no entry. A file at `stacks//manifests/.yaml` (or `.yml`) that no entry already names, for a stack the cluster file declares, is added to that stack as a manifest called ``, unless a stack already lists that name. If two declared stacks hold a file with the same unlisted name, the sync stops until you list it under the stack you mean. `ankra cluster apply -f` reads the same keys on your machine. It resolves each path against the directory the applied file is in and refuses a path that is absolute or contains `..`, so running it on the cluster file in its cluster directory resolves the same `from_file` paths a sync does; it does not add unlisted manifest files. [Files in the cluster directory](/reference/importcluster#files-in-the-cluster-directory) lists every key that names a file. *** ## Single-File Cluster Definition A cluster file you apply with `ankra cluster apply -f` can instead carry everything inline, with manifest and values content embedded as base64. This form is for the CLI only: a sync from Git refuses a cluster file whose manifests carry `manifest_base64`, and reads a manifest's content only from its file: ```yaml theme={null} apiVersion: v1 kind: ImportCluster metadata: name: my-cluster description: Imported cluster with an inline stack spec: git_repository: provider: github credential_name: my-git-credential branch: main repository: my-org/my-gitops-repo stacks: - name: secrets manifests: - name: secrets-namespace manifest_base64: namespace: secrets parents: [] addons: - name: external-secrets chart_name: external-secrets chart_version: 0.10.5 registry_name: external-secrets registry_url: https://charts.external-secrets.io namespace: secrets configuration_type: standalone configuration: values_base64: parents: - name: secrets-namespace kind: manifest ``` Use `parents` to control deployment order: a resource deploys only after its parents succeed. When Ankra writes a cluster like this to Git, it moves each manifest's and add-on's content into a file in the cluster directory and names it with `from_file`, as in the [cluster definition example](#cluster-definition-example). Every field of this file - stacks, manifests, add-ons, applications, parents, and encrypted paths - is documented in the [ImportCluster YAML Schema](/reference/importcluster). ### Data Sources in the Cluster Document The cluster's metrics and log data sources live in the same file, as `spec.prometheus_metrics` and `spec.log_source`. Both are read back on every sync, so writing either block in Git connects the source and updates it - you do not have to configure it in the platform first: ```yaml theme={null} spec: prometheus_metrics: endpoint: http://prometheus-operated.monitoring.svc.cluster.local:9090 log_source: provider: loki endpoint: http://loki-gateway.monitoring.svc:80 credential_name: loki-read ``` Credentials stay out of Git: a `username`, `password`, `token` or `auth_scheme` inside a source block is refused, and the credential is referenced by `credential_name` instead. Deleting a block does not disconnect the source - Git can create and update one, only the platform can disconnect it. See [`spec.prometheus_metrics`](/reference/importcluster#spec-prometheus-metrics) and [`spec.log_source`](/reference/importcluster#spec-log-source). *** ## How Ankra Syncs Your Cluster 1. You update or add YAML files in your Git repository 2. Ankra detects the change and automatically syncs your cluster 3. Ankra reads the cluster file and the files it names with `from_file`, and applies the manifests and add-ons in the order you specify ```mermaid theme={null} flowchart LR A[Git Push] --> B[Ankra Detects Change] B --> C[Read Cluster Directory] C --> D[Apply Manifests] D --> E[Deploy Add-ons] E --> F[Cluster Updated] ``` ## What Ankra Writes Back to Git Sync is two-way. Changes you commit are applied to the cluster, and changes made through the portal, the CLI, the API or Ankra AI are written back to the cluster's directory in the repository, so Git stays the record. A platform write lands as a commit authored by `ankra-platform[bot]` (a change made in the portal, CLI or API) or by `Ankra Reconciler` with the subject `Sync cluster state from Ankra` (the reconciler exporting stored state). ### The ownership manifest: `.ankra-owned.json` Every cluster directory carries a file named `.ankra-owned.json`. Ankra rewrites it on each platform write. It is a record of what that write rendered - not an inventory of the directory, and not a claim on it: * `paths` lists the files the last write rendered. It bounds **deletion**: when a stack or add-on is removed, only files listed here are pruned. A file Ankra never rendered is never deleted, only reported. * `contents` maps each rendered file to a digest of the bytes Ankra itself last wrote there. It bounds **overwriting**: before writing a file, Ankra compares what is in Git with that digest. If they differ, the file was edited since, and Ankra keeps the Git version instead of replacing it. Because the manifest is regenerated from each render, it grows when the cluster gains stacks or add-ons, and a write scoped to one stack may list a path it chose not to write. Neither means Ankra will overwrite that path; the digest decides. > [!WARNING] > > **Never delete or hand-edit `.ankra-owned.json`.** It does not fail safe. With no manifest at head, Ankra has no record of what it wrote, so the overwrite boundary switches off and the next platform write replaces whatever is in Git. If it has been removed, restore it from Git history. ### Hand-maintaining a file To keep a value Ankra would otherwise render - an image tag pinned above a chart's `appVersion` for a CVE fix, an `updateStrategy`, `topologySpreadConstraints` - edit the file in Git and merge it. Ankra syncs the change into the cluster, and from then on the file's bytes in Git no longer match the digest Ankra recorded, so no platform write replaces them. This holds for every write path: portal, CLI, API, Ankra AI and the reconciler's own export. Three limits apply today: * **SOPS-encrypted files are not covered.** Their committed bytes are ciphertext that cannot be reproduced from the render, so Ankra keys them on the embedded content fingerprint instead, and re-encrypts the file whenever its stored values change. A hand edit to an encrypted file sealed under the cluster's `.sops.yaml` is imported like any other Git change and then re-sealed by Ankra's next write - the values survive, the ciphertext does not, so expect one re-seal commit after rotating a sealed Secret by hand (see [Rotating by API](/integrations/prometheus#rotating-by-api) and [SOPS](/guides/sops)). * **`.sops.yaml` in the cluster directory has its own rule.** Ankra rewrites the creation rules it generates there on every export, but it no longer replaces the file: rules you add by hand are kept, and kept above Ankra's own. See [Merging the SOPS configuration file](#merging-the-sops-configuration-file) below. * **Deciding a preserved file takes the API for now.** While a file is preserved, a change made to it in Ankra does not reach Git, and the cluster reads **Diverged** for as long as the two versions differ. The portal and the GitOps status name those files, but neither the portal, the CLI nor the MCP tools can show both versions or decide them yet. See [Diverged files](#diverged-files) below. ### Diverged files A file you edited in Git stays yours: no later platform write replaces it. When a later change in Ankra would change that file - an edit through the portal, the CLI, the API, Ankra AI or an MCP client, or a change Ankra makes to the cluster itself - Ankra keeps your bytes, records the file as **preserved** with both versions, and the cluster's GitOps status reads `diverged` instead of `synced`, with the files in `preserved_paths`. The portal shows **Diverged: Git Edits Kept** and lists them. * **Nothing is paused.** Unlike a [conflict](#when-git-and-the-platform-both-change), a preserved file does not hold the sync. Commits to it and to every other file keep being applied, including a new stack or manifest you register in a preserved cluster file. Only Ankra's own change to that file waits. * **Only a difference in substance counts.** YAML and JSON are compared as decoded documents, so comments, key order and quoting alone never make a cluster diverged. Your file is still kept byte for byte. * **It clears when the two versions agree again** - you make the same change in Git, or Ankra's side changes back - or when a person decides it. It does not clear by waiting. To decide, list the preserved files with both versions, then record a decision for each one. Both calls take an [API token](/api-reference/introduction) (a token scoped to MCP is refused), and the decision needs `clusters.write` on the cluster: ```sh theme={null} curl -H "Authorization: Bearer $ANKRA_TOKEN" \ "https://platform.ankra.app/api/v1/org/clusters//gitops/preserved-paths" ``` Each entry carries the `path`, Git's version as `head_content` (read at `head_commit_sha`), Ankra's version as `render_content`, the digest of each as `head_content_hash` and `render_content_hash`, when the difference was first seen as `detected_at`, and any `resolution_choice` already made. A file that may hold a secret is listed with `content_redacted` and no content; its digests are still exact. Each entry also carries `structural_diff`, even when its content is withheld: the key path of every key or list member where the two versions differ, each marked `added`, `removed`, `changed` or `reordered` from Git's version to Ankra's. A value is shown only where Ankra's checks find nothing sensitive in it, and only to a caller holding `kubernetes.secrets_reveal` on the cluster; where a side holds a value that is not shown, the change reads `value_masked: true`, and a key name that may itself be a secret reads ``. ```sh theme={null} curl -X POST -H "Authorization: Bearer $ANKRA_TOKEN" -H "Content-Type: application/json" \ "https://platform.ankra.app/api/v1/org/clusters//gitops/preserved-paths/resolve" \ -d '{"path": "", "resolution": "git", "head_content_hash": "", "render_content_hash": ""}' ``` | `resolution` | What the next sync does | | - | - | | `git` | Imports Git's version into Ankra: wherever your file differs from Ankra's version, yours replaces it. It runs only once every preserved file on the cluster has a decision. The file stays yours, so a later change to it in Ankra is preserved again. | | `cluster` | Writes Ankra's version over your file in Git, without your comments or layout, and the file is Ankra's to write again. Refused for a SOPS-encrypted file, which would put plaintext over ciphertext. | | `none` | Withdraws a decision that has not run yet. | A decision is pinned to the two digests it names. If either version has changed by the time you send it, the call answers `409` and you list again; if one changes before the sync runs it, the decision is cleared. A `git` import Ankra refuses - one that would lower a chart version, for example - records why in `import_refused_reason` and clears the decision, so you decide again. A `git` import never removes a stack: a stack Ankra declares and your file leaves out stays until you remove it through the platform. ### Merging the SOPS configuration file The `.sops.yaml` in a cluster directory has two authors, so it is not covered by the digest boundary above: it is **merged** on every platform write instead of replaced, and the file it writes opens with a header saying so. * **Ankra owns only the rules it generates**: a rule carrying a `path_regex` Ankra generates, an `age` key, and at most an `encrypted_regex` - and nothing else. Ankra rewrites those on every sync and lists them **last** in the file. While the cluster declares `encrypted_paths`, each generated rule carries an `encrypted_regex` built from the union of every declaration across the cluster's stacks, rebuilt on every sync as the declarations change, so a `sops -e` that applies the rule seals only declared secret keys and leaves every other key readable. It is one union per cluster, so it is broader per file than what Ankra itself seals per resource: a key declared secret for any resource in the cluster is sealed in every matching file. * **Every other rule is yours.** It is preserved verbatim and kept **above** Ankra's own. SOPS applies the **first matching** creation rule, so a rule of yours takes precedence over Ankra's - including a deliberately keyless first rule, which is the supported way to keep a path out of encryption and make an external `sops -e` on it fail loudly rather than seal a file that must stay plaintext. **Write its `path_regex` relative to the cluster directory**, not the repository root: `sops` matches a rule against the file's path relative to the directory holding the `.sops.yaml` it is using, so for the cluster's file the path being tested is `stacks//add-ons//values.yaml`. A rule written from the repository root never matches, and a refusal caused by *nothing* matching is an accident, not protection - see [Checking that a keyless rule applies](#checking-that-a-keyless-rule-applies). * **To override one of Ankra's rules, write a rule that is not shaped like Ankra's**: keyless, using `key_groups`, carrying a field other than `path_regex`, `age` and `encrypted_regex`, or on a different `path_regex`. A rule that is a `path_regex` Ankra generates plus an `age` key, with or without an `encrypted_regex`, cannot be told apart from Ankra's own output and is replaced whatever its `age` or `encrypted_regex` values are - that is what lets a key rotation or a changed declaration reach the file, and it happens silently. To keep your own recipients or your own `encrypted_regex` on a path Ankra covers, name the recipients with `key_groups`. The warning below spells it out. * **Other top-level keys are yours too.** `stores`, `kms`, `shamir_threshold` and anything else in the file are carried through untouched. * **Ankra does not read these rules.** Its own encryption is driven per resource by that resource's `encrypted_paths` declaration, compiled to a SOPS `encrypted_regex` when the resource is sealed (see [SOPS](/guides/sops)). The file exists for external tooling that runs `sops` against the repository. A `.sops.yaml` Ankra cannot parse is left exactly as it is, rather than rewritten. #### Checking that a keyless rule applies `sops` looks for `.sops.yaml` by walking **up from the current directory**, not from the file it is given, and matches each `path_regex` against the file's path **relative to the directory holding that `.sops.yaml`**. Test a rule from the repository root with an explicit `--config`, writing to stdout (never `-i`): ```sh theme={null} sops --config clusters//.sops.yaml -e clusters//stacks//add-ons//values.yaml ``` What comes back says which rule decided. The wording is from sops 3.13.3 and is not a stable interface; an automated check should key on the positive control below rather than on the text. | Result | Meaning | | - | - | | `Could not generate data key: [empty key group provided]` | Your keyless rule matched first. The path is protected from a configuration-driven `sops -e`. | | `error loading config: no matching creation rules found` | No rule matched at all, usually a `path_regex` written from the wrong base directory. The refusal is an accident, not protection. | | `config file not found, or has no creation rules, and no keys provided through command line options` | `sops` found no `.sops.yaml` above the current directory. Pass `--config`, or run from inside the cluster directory. | | Ciphertext on stdout | A rule with recipients matched first, or `--age` was given on the command line. `--age` bypasses the creation rules entirely, so a keyless rule does not stop it. | Pair the check with a positive control in the same run: a neighbouring file that only a rule *with* recipients covers must encrypt, or the refusal proves nothing about your rule. Ankra's generated rules are written relative to the cluster directory (`.*values\.yaml$` and `.*manifests/.*\.yaml$`), so on a cluster synced since that change they match and can serve as the control: a values file only they cover must encrypt. A `.sops.yaml` still carrying the older repo-rooted spellings (`/.*values\.yaml$`) predates the change - those rules match nothing, Ankra's next write to the cluster replaces them, and until then a control needs a keyed rule of your own, written relative to the cluster directory like the keyless one. > [!WARNING] > > **The shape to avoid: one of Ankra's own `path_regex` values, an `age` key, and nothing else except an `encrypted_regex`.** A rule in that shape cannot be told apart from Ankra's output, so the next platform write replaces it - with no signal anywhere. The sync succeeds, and no portal, CLI or API surface reports it. The only trace is the reconciler's own `Sync cluster state from Ankra` commit, where the rule is simply gone from the diff. > > All three conditions have to hold together - that `path_regex`, an `age` key, and no field beyond those two and `encrypted_regex` - so the only rules at risk are those whose `path_regex` is character-for-character one of the two Ankra generates: > > ```yaml theme={null} > - path_regex: .*values\.yaml$ > - path_regex: .*manifests/.*\.yaml$ > ``` > > The older repo-rooted spellings of the same two patterns, prefixed with the cluster's own directory (`/.*values\.yaml$`, `/.*manifests/.*\.yaml$`), are read as Ankra's too: clusters synced before the config-relative change still carry them, and the next platform write replaces them with the rules above. The examples below use that older spelling - the ownership test is the same for both. > > The trap is tidying. A deliberately keyless rule on one of those two patterns is yours and is kept - but give it an `age` key, to "complete" it or to seal that path with your own recipient, and it becomes Ankra's shape and disappears on the next write. Adding an `encrypted_regex` of your own does not take it back out: that field is part of Ankra's shape, so the rule is still replaced, and your selector with it: > > ```yaml theme={null} > # Kept - keyless, so not Ankra's shape. > - path_regex: /.*values\.yaml$ > > # Replaced silently - Ankra's shape: its path_regex, an age key, nothing else. > - path_regex: /.*values\.yaml$ > age: age1... > > # Replaced silently too - encrypted_regex is part of Ankra's shape, whatever its value. > - path_regex: /.*values\.yaml$ > encrypted_regex: ^(password)$ > age: age1... > > # Kept - same path, your own recipients, named with key_groups. > - path_regex: /.*values\.yaml$ > key_groups: > - age: > - age1... > ``` > > Any one of these keeps a rule out of Ankra's shape: leave it keyless, name recipients with `key_groups`, carry a field outside `path_regex`, `age` and `encrypted_regex` (`unencrypted_suffix`, for example), or write it on a `path_regex` Ankra does not generate. An `encrypted_regex` on its own does not: it is part of Ankra's shape. The header Ankra writes at the top of the file states the same test. The paths in this example show the ownership test only; a rule you rely on `sops` to apply must be written relative to the cluster directory, as described above. > [!WARNING] > > **Removing the last `encrypted_paths` entry on a cluster deletes `.sops.yaml`, and your own rules with it.** Ankra renders the file only while the cluster has encrypted content. Clear the last `encrypted_paths` entry from the last add-on or manifest that had one, and the next platform write stops rendering `.sops.yaml`, so the export prunes it from the cluster directory. Ankra never reads `.sops.yaml` back in, so there is no copy in the platform to restore from - recover it from Git history. > > This matters most in the case that leads you there: removing an over-broad entry is the fix for a chart field that was sealed and should not have been (see [SOPS](/guides/sops#troubleshooting)). If that entry is the cluster's last one, keep a copy of `.sops.yaml` first, and re-commit your rules after Ankra's next write. ## When Git and the Platform Both Change Because sync is two-way, the same resource can change on both sides between two syncs: someone edits a stack in the portal while a commit to the same stack lands in Git. Ankra does not guess which side you meant. On every sync Ankra compares three things: the head of the branch, the state stored in the platform, and the baseline both sides last agreed on. That comparison has four outcomes: | What changed since the baseline | What Ankra does | | - | - | | Only Git | Applies the commit to the cluster | | Only the platform | Commits the change to Git | | Both, on different resources | Merges them, applying the Git side and committing the platform side | | Both, on the same resource | Records a conflict and waits for you | A conflict pins the cluster's sync. Nothing further is applied from Git and nothing further is pushed to it until every conflicting resource has a decision. For each one you choose **Git** (the committed version wins and the platform change is dropped) or **Cluster** (the platform version wins and is committed over the Git version). Resolve conflicts from the GitOps section of the cluster settings, one resource at a time or all at once, or with the `list_gitops_conflicts`, `get_gitops_conflict_diff` and `resolve_gitops_conflict` [MCP tools](/platform/mcp-tools). Sync resumes on its own once the last conflict is decided. There is no setting that makes one side win automatically. If Git should be the authority for a cluster, make changes to that cluster only through Git: with no platform-side edits there is nothing to conflict with. If a platform write finds a commit in Git that Ankra has not imported yet, the change is saved in the platform but its push to Git is held back, and the next sync merges the two sides or raises a conflict as above. ### Connecting a repository to a cluster that already has state When you connect a repository to a cluster Ankra is already managing, Ankra first pushes the cluster's current state to the repository as a commit named `Connect GitOps repository`, then starts syncing. Connect an empty repository, or one Ankra has written to before. A repository that already holds files at the paths Ankra renders, and has no `.ankra-owned.json`, has no overwrite boundary yet, so that first push can replace those files. You do not need a repository at all. With the [native engine](/concepts/deployment-engines), Ankra reconciles stacks and add-ons from the state stored in the platform, and a cluster with no repository connected is fully supported. Connecting one adds Git as a second place to read and make changes; it does not change how the cluster is reconciled. *** ## Monitoring Sync Status Track the status of your GitOps syncs in the cluster settings. ### Sync Status Banner Navigate to your cluster → **Settings** → **GitOps** to see the current sync status: | Status | Description | | - | - | | **Synced** | All configurations are up-to-date with the repository | | **Diverged: Git Edits Kept** | The last sync succeeded, but Ankra kept one or more files you edited in Git instead of writing its own, different version over them. Nothing is paused. See [Diverged files](#diverged-files) | | **Syncing** | A sync is currently in progress | | **Error** | The last sync failed. Check error details below | | **Not Configured** | GitOps is not yet connected to a repository | ### Applied Commit and Git Head `ankra cluster gitops status` and `GET /api/v1/clusters/{cluster_id}/gitops/status` report the last sync time and commit (`last_synced_at`, `last_commit_sha`) for the last full sync only. Beside them are two pointers: * **Applied Commit** (`applied_commit_sha`, `applied_commit_at`): the commit Ankra holds the cluster in step with, and since when. * **Git Head Checked** (`head_checked_sha`, `head_checked_at`): the head of the branch as Ankra last read it, and when. The applied commit moves on a sync or a push, and also without one: when Git's head moves but leaves the cluster's directory unchanged - a commit for another cluster in the same repository, for example - Ankra moves the applied commit to that head without running or recording a sync, so the last sync time can read days old on a current cluster. The same commit on both pointers means the cluster was in step with Git at that read; a different head is a commit Ankra has not applied yet. ### Sync Progress During a sync, you'll see the progress through these phases: 1. **Fetching** - Pulling latest changes from Git 2. **Validating** - Checking YAML syntax and configuration 3. **Applying** - Deploying changes to the cluster 4. **Completed** - Sync finished successfully ### Manual Sync Click **Sync Now** to trigger an immediate sync from your Git repository. Use this when: * You want to apply changes without waiting for automatic detection * Webhook delivery failed * You need to force a refresh *** ## Sync History View a complete history of all GitOps syncs for your cluster. ### Accessing Sync History 1. Go to your cluster → **Settings** → **GitOps** 2. Scroll down to the **Sync History** table ### History Table Features The sync history table shows: | Column | Description | | - | - | | **Status** | Success, Failed, or Running badge | | **Source** | How the sync was triggered (webhook or manual) | | **Commit** | The Git commit SHA with a link to view in GitHub | | **Started** | When the sync began (relative timestamp) | | **Triggered By** | Who or what initiated the sync | **Table Features:** * **Pagination**: Navigate through history (10 entries per page) * **Sorting**: Click column headers to sort by any field * **Commit Links**: Click the SHA to view the commit in GitHub ### Sync Metadata Each sync entry includes detailed metadata: * **Last Commit SHA**: The exact commit that was synced * **Sync Source**: `webhook` (automatic) or `manual` * **Timestamp**: When the sync occurred * **Retry Count**: Number of retry attempts (if any) * **IAC Files Link**: Direct link to view the configuration files in your repository *** ## Handling Sync Errors When a sync fails, Ankra provides detailed error information to help you fix issues quickly. ### Error Display Failed syncs show: * **Validation Errors**: Issues with YAML syntax or configuration * **Field-Level Guidance**: Specific fields that need attention * **Missing Fields**: Required fields that weren't provided * **Typo Detection**: Suggestions for misspelled field names ### Common Error Types > [accordion] Validation Errors > > YAML syntax issues or invalid configuration values. > > **Solution**: Check the error details for the specific field and fix the YAML in your repository. > [accordion] Missing Required Fields > > Required configuration fields weren't provided. > > **Solution**: The error shows which fields are missing with examples of expected values. > [accordion] Resource Conflicts > > A resource already exists or conflicts with another definition. > > **Solution**: Check for duplicate resource names or conflicting configurations. > [accordion] A Section That Is Not a List > > `spec.stacks`, or a stack's `manifests`, `addons` or `applications`, is present but is not a list: a lost `-` or an indentation slip turns a list into a mapping, and a key with nothing after it is null. Nothing is applied, because reading it as an empty list would remove everything it declared before; the error names the section, for example `stack "logging": addons must be a list, got a mapping`. > > **Solution**: Fix the YAML. To declare none, write an empty list, for example `addons: []`. ### Large Removals Are Held A sync that would remove a large share of the cluster at once is held: nothing in the commit is applied, and the sync fails with an error that starts `GitOps sync held, nothing was applied: this commit would remove` and names what would go. A sync is held when it would: * Remove two or more stacks that have members, when they are half or more of the cluster's stacks with members, or more than three of them * Remove two or more add-ons, manifests or applications from the stacks it keeps, when they are half or more of those stacks' members, or more than five of them * Remove every member of a kept stack that has two or more A commit that removes only one stack, or only one member, always applies. Anything you are already removing through Ankra does not count. If the removal is intended, remove those stacks or members through Ankra: in the portal, or with `ankra cluster stacks delete` and `ankra cluster manifests delete`. The next sync then applies the rest of the same commit, without a new push; click **Sync Now** to run it straight away. If the removal is a mistake, such as a YAML slip or a merge conflict resolved the wrong way, restore them in the cluster file and push. ### Retrying Failed Syncs 1. Fix the issues in your Git repository 2. Commit and push the changes 3. Click **Sync Now** or wait for automatic detection 4. Monitor the new sync in the history table A [held large removal](#large-removals-are-held) you meant to make needs no change in Git: remove those stacks or members through Ankra, and the next sync applies the same commit. *** ## Troubleshooting If Ankra isn't syncing as expected: | Issue | Solution | | - | - | | Sync not triggering | Check that Git credentials are valid and have repo access | | A sync fails with `which is not in the repository snapshot` | An entry's `from_file` or `description_from_file` names a path no file in the cluster directory has. Write it relative to the cluster directory, exactly as the file is named, and commit the file itself rather than a symlink. See [Cluster Definition Example](#cluster-definition-example) | | A sync fails with `GitOps sync held, nothing was applied` | The commit would remove a large share of the cluster in one sync. If you meant it, remove those stacks or members through Ankra and the next sync applies the rest of the commit; if not, restore them in the cluster file. See [Large Removals Are Held](#large-removals-are-held) | | Invalid configuration | Ensure YAML files are syntactically correct | | Wrong branch | Confirm the branch name in your cluster definition matches | | Unsure which repository or commit a cluster deploys from | Run `ankra cluster gitops status --cluster `. It prints the configured repository, branch and credential, and the **Applied Commit** and **Git Head Checked** with their times ([Applied Commit and Git Head](#applied-commit-and-git-head)). The repository the last successful sync actually read is `observed_source` in `GET /api/v1/clusters/{cluster_id}/gitops/status` and the `get_gitops_status` [MCP tool](/platform/mcp-tools); `source_mismatch` is `true` when it differs from the configured repository | | A `prometheus_metrics` or `log_source` block you deleted keeps coming back | Removing the block is not a disconnect - the platform re-renders it. Disconnect the source in the cluster's **Settings** → **Integrations** | | A field you added to a data source block fails the sync | Only the fields in the [ImportCluster schema](/reference/importcluster#observability-sources-in-git) are accepted; the error names the offending key. Credential fields (`username`, `password`, `token`, `auth_scheme`) are always refused - reference a credential by `credential_name` | | A value you set in Git is reverted by a platform write | Check that `.ankra-owned.json` exists in the cluster directory - without it the overwrite boundary is off ([What Ankra Writes Back to Git](#what-ankra-writes-back-to-git)). Restore it from history, and report the reverting commit: its author, `ankra-platform[bot]` or `Ankra Reconciler`, tells support which write path produced it | | A change made in the portal never reaches Git | The file is hand-maintained - its bytes in Git are not what Ankra last wrote, so Ankra preserves it and the cluster reads **Diverged**. Make the change in Git, or [decide the file](#diverged-files) | | A cluster reads **Diverged** and nobody changed anything in Ankra | Something changed Ankra's side of a file you edited in Git. `ankra cluster gitops status` lists the preserved files, each with when it was detected and whether it has a decision. For both versions, [list them through the API](#diverged-files): `render_content` is Ankra's version, `structural_diff` names the keys that differ even when the content is withheld, and a `Sync cluster state from Ankra` or `ankra-platform[bot]` commit near `detected_at`, when that change wrote other files too, shows what else it changed. Decide the file through the API, and report it to support if the same file comes back after a `git` import | > [!TIP] > > Use the Ankra WebUI to see sync status and any errors. The Operations page shows deployment history and helps identify issues. *** ## Related * [Stacks](/concepts/stacks) - Learn about organizing resources into stacks * [Add-ons](/concepts/addons) - Install Helm charts as add-ons * [Manifests](/concepts/stacks#manifests) - Deploy your own Kubernetes YAML *** Still have questions? [Join our Slack community](https://join.slack.com/t/ankra-community/shared_invite/zt-3a5rem8f8-cUho4epX2MoLT83bFf~VSA) and we'll help out. # How Ankra Works Source: https://docs.ankra.ai/concepts/how-ankra-works The one model behind every other concept page - organisation, cluster, Stack, add-ons and manifests, variables, Git and the deployment engine. Every other page in this section describes one piece of Ankra. This page shows how the pieces connect, so you can read the rest in any order and know where each one sits. ```mermaid theme={null} flowchart LR O[Organisation] --> C[Cluster
runs the Ankra agent] C --> S[Stacks] S --> A[Add-ons
Helm charts] S --> M[Manifests
Kubernetes YAML] V[Variables] -.-> S G[(Git repository
optional)] <-.-> C E[Deployment engine] --> C ``` ## Organisation An organisation is your team's account. It holds the members and their roles, the credentials Ankra uses for Git, registries and cloud providers, organisation-wide variables, and every cluster. Organisations that run several teams or products divide their clusters and applications into [projects](/concepts/projects), each a room a member can be limited to; every organisation starts with a Default project that holds everything. See [Organisations](/concepts/organisations). ## Cluster and agent A cluster is any Kubernetes cluster Ankra manages, whether you imported it or Ankra built it. Each one runs the [Ankra agent](/concepts/cluster-agent), installed with Helm into the `ankra` namespace. The agent opens the connection to Ankra itself, so the cluster needs no inbound ports and works behind a firewall or NAT. When the agent is not connected, the cluster shows as needing attention and nothing is deployed to it until it reconnects - see [Cluster states](/concepts/cluster-agent#cluster-states). ## Stack A [Stack](/concepts/stacks) is what a cluster should run: a named set of components with the order they deploy in. A cluster usually has several Stacks, one per concern - monitoring, ingress, your application. ## Add-ons and manifests A Stack contains two kinds of component. An [add-on](/concepts/addons) is a Helm chart with its values. A [manifest](/concepts/stacks#manifests) is your own Kubernetes YAML, such as a namespace, a Secret or a custom resource. Dependency edges between them decide what deploys first. ## Variables [Variables](/concepts/variables) hold the values that differ between clusters, such as a domain name or a replica count. You write `${{ ankra.name }}` in values or YAML, and Ankra fills it in when it deploys. You can set a variable on the organisation, a cluster or a Stack, and the more specific one wins. ## Git (optional) You do not need a repository. Ankra stores each cluster's Stacks itself and deploys from there. When you connect a repository, sync runs [both ways](/concepts/gitops): Ankra writes each cluster's Stacks to its own directory in the repository, and commits you push are applied to the cluster. ## Deployment engine The [deployment engine](/concepts/deployment-engines) is what turns a Stack into running resources. New clusters use the native engine: the agent runs Helm on the cluster itself and keeps each add-on in line with its Stack. Clusters created before the native engine keep ArgoCD until you migrate them. ## Where to go next Read the concept pages in order, starting with [Organisations](/concepts/organisations), or follow the [Quickstart](/get-started/quickstart) to see the whole chain work on a real cluster. # Managed Services Source: https://docs.ankra.ai/concepts/managed-services Run PostgreSQL, Valkey, NATS, OpenSearch, S3-compatible object storage, VictoriaLogs and VictoriaMetrics on your own cluster from a package Ankra publishes, and connect them to your applications. > [!WARNING] > > **Closed beta.** Managed services are in closed beta. They are part of the new Ankra portal and are enabled per organisation on request. [Contact support](/platform/support) to have managed services turned on for your organisation. A managed service is a backing service your applications use, such as a database, a cache or a queue, set up from a package Ankra publishes. You add one from the application that will use it, or from the catalogue. You choose the application environments that will use it, the cluster it runs on and its size. Ankra prepares a plan from those choices, and nothing is installed until you confirm it. Ankra then installs the service on your cluster and shows its progress, its health and how to connect to it under **Managed services** and on the **Services** card of each application it serves. Once the service is ready, Ankra delivers its connection into the namespace of each application it serves on the same cluster, and the application's containers read it as environment variables. When one application no longer needs a service, you disconnect that application through a reviewed plan, and the service keeps running for the others. When you no longer need a service at all, you delete it through a reviewed plan. While managed services are in the pilot, Ankra charges nothing for them. Their running time and storage are recorded as [usage](/platform/usage). ## The services Ankra publishes seven services, each built on an open-source engine: | Service | Provides | Engine | Licence | | - | - | - | - | | `postgresql` | Database | PostgreSQL 18.6, run by the CloudNativePG operator | PostgreSQL Licence (operator: Apache-2.0) | | `valkey` | Cache | Valkey 9.1 | BSD-3-Clause | | `nats` | Queue | NATS 2.15 with JetStream | Apache-2.0 | | `opensearch` | Search | OpenSearch 3.9 | Apache-2.0 | | `seaweedfs` | Object storage (S3 API) | SeaweedFS 4.48 | Apache-2.0 | | `victoria-logs` | Logs | VictoriaLogs 1.52 | Apache-2.0 | | `victoria-metrics` | Metrics | VictoriaMetrics 1.153 | Apache-2.0 | [Managed Service Settings](/reference/managed-services) lists every setting of each one. ## Who runs it Every service in the catalogue runs on **Your infrastructure**: Ankra installs it on one of your clusters, and your team operates it. You own the cluster, the service's availability and the cost of the capacity it uses. Ankra supplies the package, installs the plan you confirmed and keeps a record of the installation. **Ankra-hosted** services, which Ankra would run for you, are not available yet. ## How a service runs * **One stack, one namespace.** Each service is deployed as a stack named after the service, into a namespace of the same name. Several services can share a cluster, including several of the same kind. * **The service owns its stack.** Deleting the stack on its own is refused, and so is deleting the cluster the service runs on until the service is deleted. Deleting an application the service serves is refused until you [disconnect the application](/guides/disconnect-an-application-from-a-managed-service) from it or delete the service. A cluster whose agent has been offline for 15 minutes, and a playground, can still be deleted: see [A cluster or application a service depends on](/guides/delete-a-managed-service#a-cluster-or-application-a-service-depends-on). To remove the service, [delete it](/guides/delete-a-managed-service): that removes the stack and then deletes the namespace with its data, after you review the plan. * **Credentials are generated in the cluster.** When a service is first installed, it generates its own password or keys and keeps them in a Kubernetes Secret in its namespace. Every later apply reuses them. Ankra never sees or stores the values, and deleting the service deletes them with its namespace. * **Cluster-internal only.** Nothing is exposed outside the cluster. Applications reach the service at fixed in-cluster addresses. Inside the cluster, PostgreSQL, Valkey, NATS, OpenSearch and SeaweedFS admit their client port (5432 for PostgreSQL) only from the service's own namespace, the namespace of the cluster's Ankra agent and the namespaces of the applications connected to the service, where the cluster's network plugin enforces NetworkPolicy. A service installed on an earlier package version keeps that port open to every namespace. VictoriaLogs and VictoriaMetrics admit their client port from every namespace. * **Connections are delivered inside the cluster.** Each application environment the service was set up for, on the service's cluster, receives a `-connection` Secret in its own namespace, copied in the cluster from the service's credentials Secret, and its workloads read it as environment variables such as `DATABASE_URL`. Once the service is ready, Ankra writes the Secret without a redeploy while none of the application's running pods references it, and otherwise leaves it to the application's next deploy. Ankra never sees the values. See [Connect to a Managed Service](/guides/connect-to-a-managed-service). * **Native deployment engine.** The credentials are generated with a Helm lookup against the live cluster, which only Ankra's [native deployment engine](/concepts/deployment-engines) evaluates. On an ArgoCD cluster every sync would generate new credentials under running applications, so Ankra only places services on native-engine clusters. ## Health and readiness Ankra reports two things about a running service, and they answer different questions: whether it is running as planned (health), and whether a client can connect to it (readiness). ### Health Ankra observes each service about once a minute from its own synced view of the service's cluster: the service's workload against the size you chose, the Kubernetes Service its addresses name, the credentials Secret with every key the connection details list, and the stack that owns it. It never reads the Secret's values. Each observation stands for five minutes. [Health checks](/reference/managed-services#health-checks) lists what is checked for each service. | Health | In the portal | Meaning | | - | - | - | | `serving` | **Serving** | Every check passed. | | `progressing` | **Not serving yet** | A check has not passed while the deployment is still being applied, or within the first minutes after it finished: 10 minutes for PostgreSQL and OpenSearch, 5 for the other services. | | `degraded` | **Service degraded** | A check failed after that window, a member of the stack failed to apply or is being removed, or the stack that owns the service is gone or being removed. The portal shows the reason. | | `unknown` | **Health unknown** | Nothing failed, but Ankra could not see enough of the cluster to judge: the cluster's agent is offline, or Ankra's view of a resource type on the cluster is more than 15 minutes old. An observation older than five minutes also reads unknown. | Health describes the running service, not the operation that deployed it: a failed deployment whose workloads all run reads serving, and one that left a workload missing reads degraded at once. A cluster that is offline reads unknown, never degraded. In the portal, each service in **My services** carries a **Health** badge, and the service's page has a **Service health** section with the state, when it was observed and what decided it. Until Ankra has observed the service, the section reads **Not observed**. Health is not shown for a deleted service or while a service is being deleted. ### Readiness Health does not prove that your applications can connect. A serving PostgreSQL service has a ready primary and its credentials Secret, but that does not show a client can reach it and log in. Readiness does. Ankra checks readiness for every service it publishes, about every five minutes. The cluster's agent starts a short-lived pod in the service's namespace that connects to the address the service publishes, as a client would, with the credentials taken from the service's credentials Secret by reference, and does one small round trip: | Service | What the check does | | - | - | | PostgreSQL | Connects to the read-write address as `app` and runs `SELECT 1`. | | Valkey | Sends `PING`, then sets a key with a 60-second expiry, reads it back and deletes it. | | NATS | Answers a request on a check subject from one connection and sends it from another, so a message makes the round trip through the server. | | OpenSearch | Over TLS, trusting the service's own CA: cluster health must be green or yellow, then it writes, reads and deletes one document in the hidden index `.ankra-canary`. | | S3-compatible object storage | Writes, reads and deletes one object in the bucket `ankra-canary`, creating the bucket when it is missing. | | VictoriaLogs | Writes one log line to the stream `{ankra_canary="readiness"}` and reads it back. | | VictoriaMetrics | Imports one sample of `ankra_canary_probe`, reads it back and deletes the series. | Everything a check writes is its own, named after the check run, and deleted in the same run. The one exception is the VictoriaLogs line: VictoriaLogs cannot delete single lines, so it stays in the `ankra_canary` stream until your retention removes it. A query that selects your own streams never sees it. The credentials never leave the cluster, and Ankra keeps no log of the check. A result stands for 15 minutes. | Readiness | Meaning | | - | - | | `verifying` | No check has passed yet. None has run, or the latest failed or could not run while the service was still deploying, or within the grace window after its deployment finished: 10 minutes for PostgreSQL and OpenSearch, 5 for the other services. | | `ready` | A check passed within the last 15 minutes. | | `degraded` | A check ran and failed after the service had its chance to start. Once a check has passed, a failure reads degraded straight away. | | `stale` | Readiness cannot be established right now. The check could not run - no agent, the cluster unavailable, or the pod refused or not finishing - or the last result is more than 15 minutes old. A check that could not run never reads as a failure, and the last result stands until it expires. | | `unverified` | No check covers the service, or the cluster's agent is too old to run it. For any service other than PostgreSQL, the agent must be recent enough to run the check: until it is, the service reads unverified and `readiness_check.reason` says to upgrade the agent. | Readiness is reported on the service in the API, as `readiness` and `readiness_check` on [Get a managed service instance](/api-reference/services/get-a-managed-service-instance). The portal shows it on the service's page and in **My services**: **Ready**, **Verifying**, **Not ready** (`degraded`), **Not checked recently** (`stale`) or **Not verified** (`unverified`). While a service reads unverified, check it yourself before you rely on it: see [Check that the service answers](/guides/connect-to-a-managed-service#check-that-the-service-answers). ## Data policies A **data policy** declares the region a cluster keeps data in and its data boundary. It is optional: you can set up services without one. Declare a policy when services must keep data within a boundary. Your organisation declares it in the cluster's settings. Ankra records who declared it and does not verify the location. A cluster without a declared policy reads region and data boundary `unspecified`. That boundary is shared by every cluster without a policy, and cannot be declared by hand. Once a cluster declares a policy, it decides where a service may run: * One service connects only environments within one data boundary. Environments in two boundaries need two services. A cluster that declares a policy and one that does not are in different boundaries, so one service cannot serve both. * A cluster can declare that services bound to it must run on that cluster only. A plan records the policy it was reviewed against. A review prepared before a cluster declares or changes its policy needs a new review. ## Limits and good to know * **A finished deployment is not yet a ready service.** A PostgreSQL service can report its deployment finished 30 to 100 seconds before the database accepts connections; any service's readiness reads `verifying` until a check passes. * **Readiness checks need a pod they can start.** The check pods pull their images from Docker Hub (`postgres:16-alpine` for PostgreSQL, `valkey/valkey`, `natsio/nats-box` and `curlimages/curl` for the others); a cluster that cannot pull the image in time cannot run the check, and readiness then reads `stale`. Every check pod runs as non-root, with privilege escalation off, every capability dropped and the `RuntimeDefault` seccomp profile, which a namespace that enforces the `restricted` Pod Security Standard admits. The check pods other than PostgreSQL's also have a read-only root filesystem. On a cluster whose DNS domain is not `cluster.local` the checks fail, as the published addresses do. * **Connections are delivered within one cluster, without restarting anything on their own.** Once the service is ready, Ankra writes an application environment's connection by itself only while none of the application's running pods references it. When running pods do, Ankra leaves it to the application's next deploy, which writes it and rolls those workloads together, so pods restarting one by one never read different values. A workload deployed before the service was connected reads the connection only after its next deploy adds the reference to it. An application on another cluster than the service is not connected for you: give it the values yourself, as in [Connect from another cluster](/guides/connect-to-a-managed-service#connect-from-another-cluster). * **Only PostgreSQL is backed up.** In an organisation with backups enabled, a PostgreSQL service is protected into the organisation's [backup vault](/guides/backups): its write-ahead log is archived continuously and a base backup is taken daily, kept for 7 days. It is installed without backups on a playground cluster, whose data is for testing, and when the organisation has no verified backup vault or more than one; the installation then says so, and `ankra cluster stacks protect --vault ` protects it later (the `--vault` flag is always required). A PostgreSQL service installed on an earlier package version has no backups. Restoring a service from **Managed services** is not available yet, and a restore point that carries a CloudNativePG database cannot be [restored in place](/guides/backups) yet either. Valkey, NATS, OpenSearch, SeaweedFS, VictoriaLogs and VictoriaMetrics are not backed up: do not keep data you cannot recreate in them without a backup of your own. * **Settings are fixed after setup.** Changing, upgrading or recovering a service from **Managed services** is not available yet. You can [delete a service](/guides/delete-a-managed-service) and set it up again, which starts it with new credentials and none of its data. * **NATS runs a single server.** The `nats` package has no clustered option yet. * **OpenSearch runs a single node.** An index created with replicas stays yellow. * **SeaweedFS keeps its data on one volume.** It runs as a single pod and is unavailable while that pod restarts, which happens once, for about 40 seconds, the first time Ankra reconciles it after installation. > [!WARNING] > > **Deleting a service deletes its data.** Its namespace is deleted with every volume claim and Secret in it, and no export is made first. A SeaweedFS service's volume goes with its stack, the first thing a deletion removes, so its objects can be gone even when the deletion then stops short. Export anything you need first. When Ankra cannot show that the data is gone, for example because a volume's reclaim policy kept it, the deleted service reads **Deleted, data may remain** and its page lists the volumes that may still hold it. See [Delete a Managed Service](/guides/delete-a-managed-service#when-data-may-remain). ## Related > [card] [Set up a managed service](/guides/set-up-a-managed-service) > > Prerequisites, the four setup steps, and following the installation. > [card] [Connect to a managed service](/guides/connect-to-a-managed-service) > > Addresses, credential Secrets, and wiring them into an application. > [card] [Disconnect an application](/guides/disconnect-an-application-from-a-managed-service) > > Take one application off a service that keeps running for the others. > [card] [Delete a managed service](/guides/delete-a-managed-service) > > Review what is removed and kept, confirm, and follow the deletion. > [card] [Managed service settings](/reference/managed-services) > > Every setting of each service, and the health and readiness checks. > [card] [Applications](/concepts/applications) > > The applications whose environments use a service. # Organisations & User Management Source: https://docs.ankra.ai/concepts/organisations Manage your organisations, invite users, and control access in Ankra. An organisation is your team's account in Ankra. It holds the clusters, the people who can reach them and what each person may do, and the credentials and variables every cluster shares. If you work for more than one team or customer, each gets its own organisation, and you switch between them. Inside one organisation, [projects](/concepts/projects) divide the clusters and applications into rooms that a member can be limited to, while people, roles and billing stay organisation-wide. [How Ankra Works](/concepts/how-ankra-works) shows where the organisation sits in the overall model. ## Accessing Your Organisation Open your current organisation directly from anywhere in Ankra. 1. Click your profile icon in the menu at the bottom left of the Ankra platform. 2. Select **Organisation**. 3. You land straight on your current organisation's overview - members, stats, roles, audit log, billing, and settings are in the sidebar. ## Switching Organisations Switch between organisations to see the resources, stacks, and credentials for each one. * In the profile menu, hover **Switch organisation** to see every organisation you belong to. Your current organisation is marked with a check - click any other one to switch to it. The list scrolls, and the search box at the top narrows it by name, slug or id as you type: just start typing anywhere in the submenu, then press **Enter** to switch to the first match or use the arrow keys to pick one. * **All organisations** at the bottom of that submenu (it shows how many you belong to) opens the full list as a page, including pending invitations. The page has the same search, shows your current organisation first, pages through the rest twenty at a time, and has a switch button per organisation. * You can also switch from the command palette with the **Switch Organisation** command. * When you switch, your active session updates to reference that organisation. You'll see only the stacks, clusters, and credentials for the selected organisation. ## Inviting Users Invite teammates to your organisation by email and assign their role. * To add people, click **Add User** in the organisation view. * Enter the user's email address to send an invitation. * Choose the role the invitation grants. **Project only** is the role for someone who should reach only the [projects](/concepts/projects) you choose and nothing organisation-wide; give them a role in each of those projects afterwards. ## Managing Users & Roles Ankra has five built-in roles - Owner, Admin, Operator, Member and Viewer - plus custom roles you define, and each can be assigned to the whole organisation, one cluster, a cluster group, or one project. See [Roles & Access](/guides/roles-and-access) for what each role grants and how to assign one. * To remove a user, use the menu next to their name in the user list. ## Projects A project is a room inside the organisation: every cluster and application belongs to exactly one, a member can be limited to one project, and cost is reported per project. Credentials, registries, backup vaults, object storage buckets, variables, notification routes and DNS zones are either in a project or organisation-wide. Every organisation has a **Default** project that holds everything you had before projects arrived, so nothing changes until you create a second project. The invoice, the members and the roles never split. [Projects](/concepts/projects) has the model, how your current project is chosen, project-only members, and how resources move between projects. ## Organisation Variables Variables set on the organisation apply to every cluster and Stack in it, and a cluster or Stack variable of the same name overrides them. Where to set them and how precedence works is on the [Variables](/concepts/variables) page. ## Next [Ankra Agent](/concepts/cluster-agent) explains how a cluster connects to your organisation. # Projects Source: https://docs.ankra.ai/concepts/projects A project is a room inside your organisation. Clusters, applications and their cost live in one project each, a member can be limited to one project, and people, roles and billing stay organisation-wide. A **project** is a room inside an [organisation](/concepts/organisations). The organisation keeps everything that is about people and money - members, roles, invitations, billing, single sign-on, the AI provider. A project owns a slice of the resource estate: its clusters, applications, environments, stack profiles, pipeline repositories, AI tickets, workspaces and automations, incidents, and the cost those produce. Projects are on for every organisation on the new portal. Every organisation has a **Default** project, and every cluster, application, environment, stack profile, pipeline, AI ticket, workspace and automation you already had is in it. Nothing changes until someone creates a second project: the lists, links, tokens and scripts you use today keep working as they did. In an organisation whose portal is not yet the new one (the new portal carries `/next` in its address), projects are off: its Default project stays out of sight and everything works as it did before projects. Projects exist for the organisation that holds several teams, products or stages that should not see or change each other's clusters, without splitting into several organisations and several invoices. > [!NOTE] > > A project is not a cluster group. A [cluster group](/guides/roles-and-access#cluster-groups) is a named or label-selected set of clusters used as an assignment scope and nothing more. A project is a tenancy boundary: a resource belongs to exactly one, listings and cost are per project, members can be limited to one, and tokens can be pinned to one. ## What lives where | Level | Holds | | - | - | | **Organisation** | Members and invitations, role definitions, billing (customer, plan, invoices, activation), single sign-on, the AI provider and AI configuration (MCP servers, custom tools, skills, playbooks), the image registry, support tickets, cluster groups | | **Project** (always exactly one) | Clusters and everything reached through a cluster, applications, environments, stack profiles, pipeline repositories, AI tickets, workspaces and automations, incidents, cost budgets and autopilot defaults | | **In a project, or organisation-wide** | Credentials, Helm and OCI registries, backup vaults and object storage buckets, organisation variables, notification routes, DNS zones | The third row holds the **shared kinds**. Each credential, registry, vault, bucket, variable, notification route or DNS zone is either **in one project**, where only that project sees and uses it, or **organisation-wide** (no project), where every project sees and uses it. The ones you had before projects were released are organisation-wide. When you create a new one you choose **This project** or **Whole organisation**, and a move can change it later. For a [notification route](/guides/notification-routing#routes-per-project), the choice decides what it hears, not only who sees it: * **A route in a project** delivers only that project's notifications. A notification belongs to the project of the thing it is about: the cluster, application, pipeline run, repository, ticket, automation, workspace, incident or environment. * **An organisation-wide route** delivers every project's notifications, plus the organisation's own, such as billing. * **A route created without a choice** (before the form asked, or through the API without `project_id`) is in the project its creator was in at the time. Such a route now delivers only that project's notifications. If it should hear everything, move it to the whole organisation. Reading an organisation-wide resource needs only the ordinary read permission. Changing, revealing or deleting one needs the matching permission held **on the organisation**: a role assigned in a project can create, change and delete that project's own credentials and variables, and can read the organisation-wide ones, but cannot change them. The refusal says so: `403 Organisation-wide resources require an organisation-level grant`. ## The Default project Every organisation carries a reserved project whose short name is `default`. It is created with the organisation and cannot be deleted. Everything that existed before projects were released is in it, so nobody lost sight of anything on the day they arrived. You create further projects and move resources into them deliberately. A project has a name (up to 128 characters), a short name or slug (`a-z`, `0-9` and hyphens, up to 63 characters, unique within the organisation, never shaped like a UUID) and an optional description. The short name cannot change once the project exists, because links, the CLI and kubeconfig files use it. Projects are managed under **Settings → People and access → Projects** in the console, or with [`ankra project`](/integrations/ankra-cli#projects). ## Your current project Every request acts in one project. It is resolved once, when the request is authenticated, in this order: 1. **The token's pin.** A token minted in a project stays in it. See [Tokens and projects](#tokens-and-projects). 2. **The `X-Ankra-Project-Id` header** on a bearer-token request, naming a project of the current organisation by id. An id that is not a project of this organisation is refused with `403 Project not found in this organisation` - a `403` rather than a `404`, so the answer is the same whether the id belongs to another organisation or to nothing - and a pinned token that names a different project is refused outright. 3. **Your current project** in this organisation. Each organisation you belong to remembers its own current project, and switching it is server-side, like switching organisations: after a switch, the console, the API and the CLI read and write in the new project. Two things do not follow: a CLI where you ran `ankra project switch` keeps the project it saved on that machine until you switch there again or run `ankra org switch`, and an MCP token never follows a switch: one minted in a project stays in it, and an older unpinned one acts where [Tokens and projects](#tokens-and-projects) says. Switch with the project switcher beside the organisation switcher in the console, with `ankra project switch `, or with `POST /api/v1/org/projects/switch`. `GET /api/v1/org/projects/current` tells you where you are, and `GET /api/v1/org/projects` lists the projects you can reach, each with `is_current` and `is_default`. > [!TIP] > > Older CLIs, Terraform and scripts that send no project header run in the current project of the token's owner, the same project that person sees in the console. There is no separate fallback for them: if they stop seeing a cluster, that person's current project is not the cluster's project, or an administrator has moved the cluster. ## Roles at project scope A project is the fourth [assignment scope](/guides/roles-and-access#scopes), beside the organisation, a cluster and a cluster group. A role assigned in a project grants its resource permissions on every cluster the project holds now and later, and on the project's applications, environments, profiles, repositories and shared resources. A project assignment can never confer the permissions that are about the organisation as a whole. Whatever role is assigned, these are dropped from a project grant: `organisation.*`, `members.*`, `billing.*`, `tokens.*`, `audit.*`, `projects.*`, `kube_access.manage`, `kube_access.policy`, `sops.manage`, `secrets.manage`, `git.manage`, `security.manage`, `ai.manage`, `workspaces.manage` and `profiles.share`. An `admin` assigned in one project is the resource half of admin inside that project, and cannot invite members, see invoices or create projects. Creating, renaming, deleting and moving between projects needs `projects.manage`, which owners and admins hold at organisation scope. Listing projects needs `projects.read`, which every built-in role holds. ### Project-only members A member's organisation role is a floor that reaches every project: a `viewer` on the organisation sees every project, read-only, whatever else is assigned. To give someone one project and nothing else, invite them with the membership role **Project only**. It holds nothing organisation-wide: * The member reaches only the projects they are assigned a role in - `admin`, `operator`, `member` or `viewer` in that project - and sees nothing of the others. * They can never hold an organisation-level permission: members, billing, other members' tokens, the audit log and organisation settings stay out of reach whatever role they are given in a project. * When they sign in they land in their current project if they still reach it, otherwise in the first project they reach, oldest first. Invitations and the **People** page offer **Project only** beside the organisation roles, and `ankra org invite --role project --project --project-role operator` invites one from the CLI (`--project-role` is `admin`, `operator`, `member` or `viewer`, `member` by default). Change a member to or from it on their row in People. A project-only member whose last project assignment is removed can still sign in, but every request, the console's own `/auth/whoami` included, answers `403 You are not assigned to any project in this organisation` until they are assigned again. > [!WARNING] > > Ankra Cloud console access needs organisation-wide membership for now. A project-only member who opens the Ankra Cloud console is refused rather than shown the whole account. Everything in the Ankra platform itself works for them. ## Tokens and projects A token is **pinned** to a project when it is minted with one: an MCP token or a [service token](/guides/service-tokens) created in the console is pinned to the project chosen in the create dialog, which defaults to the project you are working in; `ankra tokens create --project ` pins from the CLI; and any token minted by a pinned token is pinned to that project. A pinned token: * reads and writes in that project only, however the request is phrased - `project_id=all` on a cost route and `X-Ankra-Project-Id` naming another project are both refused; * cannot switch organisations either, as before; * stops authenticating, with `403 The project this token is pinned to no longer exists`, if its project is deleted. Mint a new one in the project that replaced it. Tokens minted before projects were released, and REST tokens minted without a project, are not pinned. A personal REST token follows its owner's current project and accepts `X-Ankra-Project-Id`; an unpinned service token acts in the project the header names and otherwise in the Default project. An MCP token minted while the organisation has projects is pinned to the project it was created in. One minted before that is not pinned and never follows a switch: for an organisation-wide member it acts in the Default project whatever its owner switches to, and a project-only member's acts in the oldest project they reach. A project-only member's REST token minted without a project is not pinned either, and reaches only the projects they do. See [API tokens](/reference/tokens). ## Moving resources between projects A move takes one resource into another project of the organisation, or makes a shared kind organisation-wide. It is one transaction, audited as `move_to_project`. Move from the resource's settings page (**Move to another project**), with `ankra project move --kind --id --to `, or with `POST /api/v1/org/projects/{project_id}/move`. The kinds a move accepts are exactly these: `cluster`, `application`, `environment`, `stack_profile`, `pipeline_repository`, `ai_automation`, `ai_workspace`, `credential`, `registry`, `backup_vault`, `object_storage_bucket`, `organisation_variable`, `notification_route` and `dns_zone`. Only the shared kinds - the last seven - may be made organisation-wide. The move carries the rows whose project is derived from the resource: * a **cluster** takes its DNS zones, incidents, AI tickets and the notification routes pinned to it; * an **application** takes its connected pipeline repositories; * a **stack profile** takes its open drafts. Three things refuse a move, each with a code the console, CLI and API all show: | Code | Why | | - | - | | `same_project` | The resource is already in that project, or already organisation-wide | | `operations_in_flight` | Operations are still running on the cluster. Wait for them to finish, then move it | | `follows_cluster` | The DNS zone is bound to a cluster and follows that cluster's project. Move the cluster instead | Cost already metered stays attributed to the project the cluster was in at the time; only hours from the move onwards count against the destination. ## Deleting a project A project can be deleted only once nothing is in it: every cluster, application and other strictly scoped resource moved out, and no shared resource still pinned to it. The refusal lists what remains, per kind. Deleting a project retires the role assignments made at its scope, moves every member whose current project it was back to the Default project, removes its budgets and autopilot defaults, and stops the tokens pinned to it. The Default project cannot be deleted. ## Cost by project Cost is metered per project, so a report for a month in which a cluster moved shows its cost split between the two projects. The Cost pages filter by project, and the fleet view shows **By project**: each project's run rate and cluster count, with an **unattributed** bucket for spend no project can claim, such as waste found on an organisation-wide credential rather than a cluster. The projects reconcile to the fleet total only together with that bucket. Every cost route and tool takes `project_id`: a project id for one project, `all` for the whole organisation, and nothing for your current project. Cost budgets take a `project` scope, and the cost autopilot takes per-project defaults between the organisation's policy and a cluster's own override. From the CLI, the global `--project` flag reads another project, `--all-projects` on the fleet cost commands reads the whole organisation, and `ankra org usage --group-by project` gives the showback. [Cloud Cost](/platform/cloud-cost#by-project) has the detail. Billing is never split. The invoice stays one per organisation. The **By project** view under Billing is a showback: each project's share of worker vCPU-hours over a window, for charging teams back internally. See [Billing](/platform/billing#showback-by-project). ## The API, Ankra's AI and the MCP server * Bearer requests name a project for one call with `X-Ankra-Project-Id: `, as described under [Your current project](#your-current-project). * Portal links Ankra hands you - in notifications, AI answers and MCP results - carry `?project=` beside `?org=`, so a link to a cluster in another project opens in that project and offers to switch you to it. See [Portal URLs](/platform/portal-urls). * The [MCP server](/platform/mcp-server) and the AI Assistant act in the current project of the token or session. `list_projects` and `get_current_project` say where a session is, `move_cluster_to_project` and `move_application_to_project` are the moves, `get_organisation` reports the current project, and the cost tools take the same `project_id` as the routes. See [MCP Tools](/platform/mcp-tools#projects). ## Ankra Cloud For organisations that also use [Ankra Cloud](/guides/ankra-cloud-clusters), the platform is the source of truth for projects, and Ankra Cloud does not scope anything by project yet. Projects are never created, renamed or deleted from the Cloud, and a project-only member is refused there for now. Servers the platform creates for a cluster carry the cluster's project in the `platform.ankra.io/project-id` label beside the cluster label. ## Related * [Organisations](/concepts/organisations) - the level above a project. * [Roles & Access](/guides/roles-and-access) - scopes, project-only members and what each role holds. * [Service tokens](/guides/service-tokens) and [API tokens](/reference/tokens) - pinned credentials. * [Ankra CLI](/integrations/ankra-cli#projects) - `ankra project`, `--project` and `ANKRA_PROJECT`. * [Cloud Cost](/platform/cloud-cost#by-project) and [Billing](/platform/billing#showback-by-project) - cost by project and the showback. # Stack Profiles, Reusable Kubernetes Stack Templates Source: https://docs.ankra.ai/concepts/stack-profiles A stack profile is a reusable, versioned template of a working Kubernetes stack. Set parameters per environment and deploy it to any cluster. A stack profile is a reusable template captured from a real [stack](/concepts/stacks) of Helm add-ons and manifests. Build a stack once, save it as a profile, and instantiate it on any cluster with parameters filled in per environment. Profiles are versioned, so you can publish updates, diff versions, and see which deployments have drifted behind the latest. > [!NOTE] > > A profile is a **template**, not a running deployment. Instantiating a profile produces a stack draft on a target cluster, which you then review and commit like any other stack change. *** Watch a working OpenSearch stack reviewed, parameterised and published as a profile. [Your stack works. Is it correct? Publishing an OpenSearch stack profile on Ankra](https://www.youtube-nocookie.com/embed/zjVtg_09qwU?rel=0) ## Why use profiles * **Standardise** a golden stack (monitoring, ingress, security baseline) and reuse it across clusters. * **Parameterise** the bits that differ per environment - domains, replica counts, sizes - instead of copy-pasting YAML. * **Version** changes with a changelog and channels, and **diff** any two versions. * **Track drift**: a profile shows how many instantiations are behind its latest version. *** ## Anatomy of a profile | Concept | Description | | - | - | | **Version** | An immutable snapshot of the stack spec. Versions increment as you publish updates and carry a `channel` (e.g. `stable`) and an optional changelog. | | **Parameters** | Typed inputs callers fill in at instantiation. Each parameter has a `type` (`string`, `number`, `boolean`, `enum`, or `secret`), and may be `required`, have a `default`, an `enum` list, or a validation `pattern`. | | **Choices** | A parameter can offer `options`, and each option can `set` other parameters. One **Model size** choice of `8b` or `32b` then moves the model id, the context length and the volume size together, so the operator picks one thing instead of keeping four consistent. See [Choices that set other inputs](#choices-that-set-other-inputs). | | **Protection** | The stack's backup block, carried as one parameter group. A profile captured from a protected stack keeps `enabled`, the schedule, the retention tiers and the selection; the **vault travels by name** and is resolved against the organisation the profile is launched into. See [Protection](#protection-backups-travel-with-the-profile). | | **Visibility** | `organisation` (default) keeps the profile private to your org; `public` shares it more widely. Independently of visibility, a profile can be [shared with specific organisations](#share-with-specific-organisations). | | **Category & tags** | Metadata for organising and searching profiles. | | **Publisher** | The organisation that owns the profile. Publisher names, logos, and verified badges help you identify trusted profiles. Verified Ankra profiles appear first in profile results. | | **Drafts** | An editable working copy used to build or edit a profile before publishing a version. | | **Suggestions** | Proposed versions submitted by other organisations that can see the profile. The owner reviews the diff and approves (publishing it as the next version) or rejects. See [Suggestions](#suggestions-propose-and-review-changes). | Secret-typed parameters and other sensitive values are redacted when a profile is captured, so credentials are never baked into a shared template. *** ## Create a profile You can create a profile two ways: > [card] From an existing stack > > Capture a profile directly from a stack already running on a cluster. > Optionally include addon configurations. This is the fastest path - build > and validate the stack first, then snapshot it. > [card] Import > > Import a profile from exported IaC content, for sharing across > organisations or storing in Git. In the portal, **Save as Profile** starts with the stack's existing name and description. Categories combine common options with values your organisation already uses, and tags are suggested as you type. Choose who can use the profile before saving: * **This organisation** keeps it private to members of your organisation. * **Public** makes it available to every organisation. * **Specific organisations** keeps it private and grants access to the organisation slugs you enter. Public and specific-organisation sharing require an organisation admin. Quick save publishes immediately. Save and customise opens the profile builder so you can review inputs before publishing. ```bash theme={null} # Capture from a cluster's stack POST /org/stack-profiles { "name": "platform-baseline", "description": "Shared platform services", "category": "platform", "tags": ["baseline", "production"], "visibility": "organisation", "share_organisation_slugs": ["partner-org"], "source_cluster_id": "...", "stack_name": "platform", "include_addon_configurations": true } ``` Organisation admins can upload a publisher logo under **Organisation Settings**. Profile cards fall back to the organisation's initials when no logo is configured. ### Build with drafts For more control, work in a draft: create a draft (optionally seeded from a cluster's stack), edit its spec and parameters, validate it, then publish it as a new version. **1. Create a draft** `POST /org/stack-profiles/drafts` - optionally seeded from an existing profile or a source cluster stack. **2. Edit and validate** Update the draft's spec and parameters, then `POST /org/stack-profiles/drafts/{draft_id}/validate` to surface any issues before publishing. **3. Publish a version** `POST /org/stack-profiles/drafts/{draft_id}/publish` with a channel and changelog. This creates the profile (or a new version of it). > [!NOTE] > > To update an existing profile, always author through a draft (**Edit Builder** in the portal, or `ankra stack-profiles drafts` from the CLI) rather than exporting the IaC, editing it, and re-importing. A draft seeded from the profile keeps the version's input annotations - titles, descriptions, enum choices, and [choices that set other inputs](#choices-that-set-other-inputs) - and publishes a new version of the same profile. Importing an edited export re-detects the inputs from the spec, drops those annotations, and creates a separate new profile. *** ## Instantiate a profile Instantiating turns a profile into a stack on a target cluster: **1. Choose a profile and version** Pick the profile and, optionally, a specific version. When no version is given, the profile's **current** version is used - the pinned version, which can be older than latest after a rollback. **2. Bind parameters** Supply values for the profile's parameters. Required parameters must be set; others fall back to their defaults. In the portal, **Use Profile** opens a page of its own rather than a dialog: the destination and the inputs on the left, and a summary beside them that stays in view while you scroll, ticking off the target cluster, the stack name and the required inputs until **Create draft** is ready. The form opens on the inputs the profile left blank and folds the ones that already carry a default behind a disclosure, with a counter for the required inputs still empty. Each field names the addon or manifest it writes into, so a key declared on two components stays unambiguous. Secret inputs can be generated, revealed, and copied, and any input changed away from its default can be reset. An input whose name ends in `host`, `hostname`, `fqdn`, or `domain` offers **Use cluster domain**, filling in that hostname under the target cluster's delegated zone. **3. Review the draft** Instantiation creates a stack draft on the cluster (with the resolved addons and manifests). Review it. **4. Commit** Commit the draft to deploy, exactly like any other [stack](/concepts/stacks) change. ### Choices that set other inputs Some inputs only make sense together. A chat profile that can run a small model on one GPU or a large one on a bigger card has four inputs that must agree - the model id, the context length, the GPU memory ceiling and the size of the model store - and an operator asked for each one separately has to know all four answers. A profile author can instead give one input **options**, and have each option **set** the others: ```yaml theme={null} - name: model_size title: Model size type: enum default: 8b options: - value: 8b title: Qwen3 8B on one L4 (24 GB) sets: model_id: Qwen/Qwen3-8B max_model_len: "16384" gpu_memory_utilization: "0.90" model_store_size: 60Gi - value: 32b title: Qwen3 32B on one L40S (48 GB) sets: model_id: Qwen/Qwen3-32B max_model_len: "28672" gpu_memory_utilization: "0.95" model_store_size: 120Gi ``` When the profile is used, the values resolve in one fixed order, on every surface: ``` a value you set > what the selected option sets > the parameter's own default ``` * **In the portal**, the choice renders as a card of options above the rest of the form, each naming how many inputs it answers. The inputs an option answers show the value it assigned, labelled with the choice that made it; type over one and it is marked *customised*, and **Reset** hands it back to the option. A required input an option answers counts as answered. * **From the CLI**, `ankra stack-profiles get` prints a `Choices for model_size` block listing what every option sets, and `ankra stack-profiles apply --cluster --set model_size=32b` deploys the whole set with no other bindings. A `--set` of your own on one of those inputs still wins over the option. * **The platform resolves the choice server-side**, so the AI assistant, the MCP tools and Terraform get the same stack from the same selection, and the deployment's recorded parameters are the values that actually deployed. A profile may declare any number of such inputs, each driving a different slice of the form (model size, storage tier, ingress mode); when two set the same input, the one declared later wins. An option can never set a **secret** input - a profile never stores secret values - and cannot set another input that itself offers options, so every resolution is a single readable pass. Options are authored on the builder's **Inputs** tab; a choice-only input that no manifest references can be added there directly, and mistakes that would be refused at publish (a value the target input would reject, a default that names no option, an unknown target) are shown inline while you edit. ### From the CLI The [Ankra CLI](/integrations/ankra-cli) can instantiate a profile directly. Inspect a profile's parameters first, then apply it to a cluster as a draft (or pass `--deploy` to deploy in one step): ```bash theme={null} # See the profile's versions and parameters ankra stack-profiles get # Create a draft on a cluster, binding parameters ankra stack-profiles apply --cluster production \ --set replicas=3 \ --set-env api_token=MONITORING_API_TOKEN # Or apply a specific version and deploy immediately ankra stack-profiles apply --cluster production --version 3 --deploy ``` > [!NOTE] > > For **secret** parameters use `--set-file ` or `--set-env ` rather > than `--set` so the value never appears in your shell history or process list. *** ## Protection: backups travel with the profile A stack you protect with a backup policy keeps that policy when you save it as a profile, and every instance launched from the profile is protected too - so a fleet does not come up unprotected and need protecting again one cluster at a time. What the profile carries is the **declaration**, never the rendered schedules. The Velero, CloudNativePG and Percona objects a backup policy materialises are bound to one cluster's vault, storage classes and object prefix; they are recomputed per cluster from the block and are deliberately left out of a capture. The builder renders it as a single **Protection** section with three inputs: | Input | What it decides | | - | - | | `ankra_protection_enabled` | Whether this launch takes scheduled restore points. | | `ankra_protection_vault` | The **name** of the backup vault in the organisation being launched into. | | `ankra_protection_schedule` | `hourly`, `daily`, `weekly`, or a five-field cron expression. | Retention tiers and the database / volume selection are not launch inputs: they describe what a restore point of this stack *contains*, which is the profile author's decision, so they ride the block and the launcher does not retype them. ### The vault is resolved at launch Vault ids differ per organisation, so protection travels by vault **name** and is resolved against the target organisation when the profile is instantiated: * the profile names a vault the target organisation has, ready → that vault; * the profile names none and the organisation has exactly one ready vault → that one; * anything else - no ready vault, no vault by that name, several to choose from - the stack is created **without** a backup policy, the result carries a warning saying so, and a deploy is stopped with the draft kept rather than bringing a stateful stack up unprotected. Create and verify a vault, or bind `ankra_protection_vault` to the one this cluster should write to, then deploy the draft. Bind `ankra_protection_vault` per cluster to send each instance's restore points to a different vault. > [!NOTE] > > A **public** profile carries no vault name - naming one would disclose an organisation-internal resource and describe protection the installer cannot have. `enabled: true` with no vault is the publishable shape: the organisation launching it supplies its own vault. Making a profile public is refused while any of its versions still names one. *** ## Demo a profile before you deploy it Instantiating a profile puts it on a real cluster. When you only want to see what it deploys - to review a new version, to check a public profile before adopting it, or to show someone the stack running - launch it as a **demo** instead. A demo installs the profile into its own throwaway namespace on your organisation's staging cluster, quota-bounded and on a timer, and deletes itself when the timer runs out. Open the profile's **Demos** tab to launch one. See [Stack Profile Demos](/guides/stack-profile-demos). *** ## Share with specific organisations Making a profile `public` exposes it to everyone. When you want to share a golden stack with one partner, customer, or sibling organisation - and nobody else - share it directly instead: * Shared organisations can **list, view, diff, export, and instantiate** every version of the profile. They cannot edit, delete, or re-share it. * The profile keeps its `organisation` visibility; sharing is an explicit per-organisation grant. * The target organisation is identified by its **organisation slug** (found under organisation settings). Ask the other organisation for theirs. * Only **organisation admins** can grant or revoke shares, and a grant re-runs the plaintext-secret scan over every published version - a profile that still carries plaintext secrets cannot be shared. Grants and revokes are recorded in the audit log. * Revoking a share hides the profile from the other organisation again. Stacks they already deployed from it keep running - they just stop seeing profile updates. In the portal, open a profile you own and use **Share**. Via the API: ```bash theme={null} # Grant an organisation access POST /org/stack-profiles/{profile_id}/shares { "organisation_slug": "acme-corp" } # List grants / revoke one GET /org/stack-profiles/{profile_id}/shares DELETE /org/stack-profiles/{profile_id}/shares/{share_id} ``` Profiles shared **with** your organisation show up in your profile list marked "Shared with you". *** ## Suggestions: propose and review changes Sharing is read-only, but improvements can still flow back. Any organisation that can see a profile - shared or public - can open it in the builder with **Suggest changes**, edit a draft of it, and submit the result as a **suggestion**. The owning organisation reviews the diff on the profile's **Suggestions** tab and either approves it - publishing the proposed contents as the profile's next version - or rejects it with a note. The proposer can withdraw an open suggestion. The profile stays owner-only throughout: a suggestion changes nothing until an owner approves it. Suggestions are frozen at submit and pass the same plaintext-secret and unbound-placeholder gates as publishing, at submit and again at approve, so no secret value ever travels through one. See [Stack Profile Suggestions](/guides/stack-profile-suggestions) for both sides of the flow, end to end. *** ## Versioning, diffing, and updates * **Save a new version** from an updated source stack, or by publishing an edited draft. * **Diff** any two versions to see what changed: `GET /org/stack-profiles/{profile_id}/diff?from_version=1&to_version=2`. * **Diff a draft** before publishing it, against the version it was opened from: `GET /org/stack-profiles/drafts/{draft_id}/diff`. The draft is redacted first, so the comparison shows what publishing would actually store rather than your working copy. A draft that will create a brand-new profile has no base, so everything in it reads as added. * **Update tracking**: a profile reports `outdated_instantiation_count` and `has_update_available` so you can find the deployments that are behind. * **Update a deployment in place**: updating a tracked deployment replaces the contents of the stack it already runs with the chosen version - the same stack, so Kubernetes performs a rolling update of the workloads and nothing is deployed twice - and records the deployment at the new version, so the counters above follow. The non-secret inputs it was last applied with are carried forward; secret inputs are asked for again. Do **not** instantiate the profile a second time to update a deployment: that produces a *new* stack beside the running one, renamed around the name collision. * **Export IaC** for a version to store the template in Git or move it between organisations. ### Deprecate a version A published version cannot be deleted - deployments record which version they run, and that history has to stay readable - but it can be **withdrawn**. When a version turns out to be incompatible with what clusters run today, to carry a critical bug, or to ship a CVE, the profile's owner deprecates it with a reason and, optionally, a note and the advisories it relates to: ```bash theme={null} ankra stack-profiles deprecate payments 3 --reason cve \ --note "Fixed in v4." --reference CVE-2026-12345 ankra stack-profiles undeprecate payments 3 ``` The same actions sit on each version in the profile's **History** tab. From then on: * Nothing deploys the version. The launch page refuses it, and so do `ankra stack-profiles apply --version 3`, `rollout`, a demo launch and an in-place update onto it - every lane answers `409` with the reason and the note (`Version 3 of this profile is deprecated (CVE): Fixed in v4. Deploy another version.`), so the person who tried learns what to do instead. * It cannot be made the current version, and the **current version cannot be deprecated**: every launch that names no version resolves to it, so withdrawing it would leave the profile with nothing to deploy by default. Make another version current first. * Deployments still running it are flagged: the Deployments tab marks each row **deprecated**, `ankra stack-profiles deployments` counts them, and the profile detail reports `update_status.deprecated_instantiation_count`, so a fleet sitting on a withdrawn version is visible from the profile rather than discovered cluster by cluster. The notice travels with the version on the API as `deprecation` (`reason`, `note`, `references`, `deprecated_at`, `deprecated_by_user_id`; `null` while the version is deployable), written by `POST /org/stack-profiles/{profile_id}/versions/{version}/deprecation` and cleared by `DELETE` on the same path. Removing a deprecation restores the version exactly as it was; deployments made before the notice are never touched either way. *** ## See where a profile is deployed The profile detail page has a **Deployments** tab listing every stack your organisation has created from the profile: the cluster, the stack (a link to the running stack, or to the still-open draft when it was never committed), its current state, the profile version it runs, the non-secret parameter values it was instantiated with, and when. Rows running a version older than the profile's current version carry an **Update** action that opens the update flow pre-filled with the values used last time. A stack that is deleted from its cluster drops off the list, as does a draft that is discarded, so the tab always reflects what is actually running. The same rows are served by `GET /org/stack-profiles/{profile_id}/instantiations`. ### The stack a profile was captured from is not a deployment of it > [!WARNING] > > Saving a profile **from** a running stack - or publishing a new version from it - snapshots > that stack's contents. It does not register the stack as a deployment of the profile. The > Deployments tab is therefore empty right after a capture, the source stack never reports > itself as behind when you publish a new version, and it cannot be updated through the > profile. Only stacks created by instantiating the profile are tracked until you adopt the > source stack. This matters most for the platform layer - a CNI, ingress or storage stack - where deleting and recreating the stack to get a tracked deployment is not an option. Adopt the stack to record it as a deployment of the profile without touching it: ```bash theme={null} ankra stack-profiles adopt platform-baseline --stack platform --cluster my-cluster --version 2 ``` Nothing is written to the cluster: only the deployment record appears, after which the Deployments tab lists the stack and `ankra stack-profiles rollout` can update it in place. Adopt reports how the live stack differs from the version you name. Read that report before rolling out: a rollout replaces the stack with what the version lists, so a member that is only on the cluster is removed by it. To move such a stack onto a profile version without tracking it, export the version and apply it to the cluster under the stack's own name: ```bash theme={null} # Export the version you want the cluster to run ankra stack-profiles export-iac platform-baseline --version 2 --output platform.yaml ``` Then edit `platform.yaml` before applying it: * set `metadata.name` to the **target cluster's** name - the export carries the profile's name there, and applying it unchanged imports a new cluster under that name; * set the stack's `name` to the stack already running on that cluster. ```bash theme={null} ankra cluster apply -f platform.yaml ``` That updates the named stack in place, exactly as editing the stack would. Other stacks on the cluster are left alone, but any add-on or manifest of *that* stack the document does not list is removed - so compare the version against what the stack runs first if it has drifted from the profile. The Deployments tab still will not list the stack afterwards: applying IaC does not create the tracking record. Adopt the stack, as above, when the profile should track it. *** ## API All endpoints are under `/org/stack-profiles` and require authentication; write operations require a CSRF header for browser-originated requests. | Method | Path | Purpose | | - | - | - | | `GET` | `/org/stack-profiles` | List profiles (paginated) | | `POST` | `/org/stack-profiles` | Create a profile from a cluster's stack, with optional visibility and organisation shares | | `GET` | `/org/stack-profiles/metadata-suggestions` | List categories and tags already used by your organisation | | `POST` | `/org/stack-profiles/import` | Import a profile from IaC content | | `GET` | `/org/stack-profiles/{profile_id}` | Profile detail, versions, and update status | | `PATCH` | `/org/stack-profiles/{profile_id}` | Update metadata (name, description, category, tags, visibility; making a profile public requires the org admin role) | | `DELETE` | `/org/stack-profiles/{profile_id}` | Delete a profile | | `POST` | `/org/stack-profiles/{profile_id}/current-version` | Pin the current version (roll back / roll forward) | | `POST` | `/org/stack-profiles/{profile_id}/versions` | Save a new version from a source stack | | `GET` | `/org/stack-profiles/{profile_id}/versions/{version}` | Get a version's spec and parameters | | `GET` | `/org/stack-profiles/{profile_id}/versions/{version}/iac-export` | Export a version as IaC | | `GET` | `/org/stack-profiles/{profile_id}/diff` | Diff two versions | | `GET` | `/org/stack-profiles/{profile_id}/instantiations` | List the live deployments of the profile (cluster, stack, state, version, parameters) | | `GET` | `/org/stack-profiles/{profile_id}/shares` | List the organisations a profile is shared with | | `POST` | `/org/stack-profiles/{profile_id}/shares` | Share the profile with an organisation by slug (org admin role required) | | `DELETE` | `/org/stack-profiles/{profile_id}/shares/{share_id}` | Revoke a share (org admin role required) | | `GET`/`POST` | `/org/stack-profiles/drafts` | List or create drafts | | `GET`/`POST`/`DELETE` | `/org/stack-profiles/drafts/{draft_id}` | Get, update, or delete a draft | | `POST` | `/org/stack-profiles/drafts/{draft_id}/validate` | Validate a draft | | `POST` | `/org/stack-profiles/drafts/{draft_id}/publish` | Publish a draft as a profile version | | `POST` | `/org/stack-profiles/drafts/{draft_id}/rebase` | Move a stale draft onto the profile's latest version (`reseed` replaces its contents; `acknowledge` keeps them) | | `POST` | `/org/stack-profiles/drafts/{draft_id}/submit-suggestion` | Submit a draft as a suggestion to another organisation's profile | | `GET` | `/org/stack-profiles/{profile_id}/suggestions` | List suggestions (owners see all; others see their own organisation's) | | `GET` | `/org/stack-profiles/{profile_id}/suggestions/{suggestion_id}` | One suggestion with its frozen spec and diffs | | `POST` | `/org/stack-profiles/{profile_id}/suggestions/{suggestion_id}/approve` | Approve: publish the suggestion as the profile's next version (owner only) | | `POST` | `/org/stack-profiles/{profile_id}/suggestions/{suggestion_id}/reject` | Reject with an optional note (owner only) | | `POST` | `/org/stack-profiles/{profile_id}/suggestions/{suggestion_id}/withdraw` | Withdraw an open suggestion (proposer only) | See [Stacks](/concepts/stacks) for the deploy/commit flow and the [API Reference](/api-reference/introduction) for full schemas. # Stacks Source: https://docs.ankra.ai/concepts/stacks A Stack is what a cluster should run - Helm add-ons and your own Kubernetes manifests, deployed together in dependency order. A cluster rarely needs just one chart. Monitoring needs a namespace, Prometheus, Grafana and an ingress rule, and they have to go on in the right order. A **Stack** is how you describe that set once in Ankra and deploy it as one unit, to one cluster or to many. [How Ankra Works](/concepts/how-ankra-works) shows where Stacks sit in the overall model. ## What a Stack contains * **[Add-ons](/concepts/addons)** - Helm charts, each with a chart version and its values. * **[Manifests](#manifests)** - your own Kubernetes YAML: namespaces, Secrets, CRDs, RBAC, custom resources. * **[Variables](/concepts/variables)** - Stack-level values referenced as `${{ ankra.name }}`. A Stack variable overrides a cluster or organisation variable of the same name. * **Dependency edges** - which component has to succeed before another one starts. A cluster usually has several Stacks, one concern each, rather than one Stack that holds everything. ## Deployment order Components that do not depend on anything deploy in parallel. A component with parents waits until they have succeeded, so a namespace manifest goes before the add-ons and resources that live in it, and an operator goes before the custom resources it reconciles. ```mermaid theme={null} flowchart LR Namespace[Namespace manifest] --> ConfigMap[ConfigMap manifest] Namespace --> App[App add-on] ConfigMap --> App ``` Here the namespace deploys first, then the ConfigMap, then the add-on that reads both. ## Manifests A manifest is a YAML document of Kubernetes resources, written by you, that Ankra applies as part of a Stack. Use one for anything a Helm chart does not create for you: the namespace a chart installs into, a Secret it reads, the RBAC your team needs, or the custom resources an operator add-on reconciles. Manifests always live inside a Stack, so they deploy in order with the add-ons that need them. A single manifest can hold several resources separated by `---`: ```yaml theme={null} apiVersion: v1 kind: Namespace metadata: name: monitoring --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: monitoring name: monitoring-reader rules: - apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "list"] ``` **Secrets in manifests.** If a manifest carries a password or key and the cluster is connected to Git, list the key names under **Encrypted Keys (SOPS)** in the manifest editor, for example `password` or `stringData.admin-user`. Ankra encrypts those values with SOPS before it writes the manifest to Git and decrypts them when it deploys. SOPS needs a one-time setup for the organisation - see [SOPS](/guides/sops). **Operational notes.** Every manifest and add-on has an **AGENTS.md** tab where you and Ankra's AI record what matters when changing it, such as upgrade order or fields that must stay pinned. Editing it never triggers a redeploy. See [Teach Your Agents with AGENTS.md](/platform/agents-md). ## Building a Stack In the portal, open the cluster's **Stacks** and click **+ Create**. In the Stack Builder, **Add Add-on** adds a Helm chart and **Add Manifest** adds your own YAML. Drag from one component to another to add a dependency edge, and click a component to edit its values or YAML. The **Variables** tab holds the Stack's own variables. **Create Stack** saves the Stack and deploys it. A Stack that has not been deployed yet shows a **Draft** badge in the list, or **AI draft** when Ankra's AI proposed it. You can also describe what you need to Ankra's AI (`⌘J`) and let it propose the Stack, for example: > I need a monitoring stack with Prometheus, Grafana and alerting for this cluster. In chat every change needs your confirmation. The same Stack tools are available to MCP clients with the `mcp:write` scope - see the [MCP Tool Reference](/platform/mcp-tools#stacks). ## What a Stack Is Running The Stack page shows what a Stack declares: its add-ons and manifests and the deployments that applied them. The **Runtime** tab shows what those components actually put on the cluster right now, in four panels: **Endpoints** (the hosts and paths its Ingresses publish), **Pods**, **Services** and **Events**. Runtime scopes by ownership, not by namespace. Several Stacks can share a namespace and one Stack can span several, so Ankra follows the objects it recorded for the Stack and their owner references down to ReplicaSets, Jobs and Pods. When the cluster cannot answer a listing, the tab says so rather than showing an empty list. It reads the cluster only while it is open. ## Stacks in Git When the cluster has a repository connected, each Stack is written to its own directory, by default `clusters/-/stacks//`, with one `values.yaml` per add-on and one YAML file per manifest. Commits to those files are applied to the cluster. See [GitOps](/concepts/gitops). ## Reusing a Stack * **Clone to Cluster** in a Stack's menu copies it to another cluster, including one in another organisation. Encrypted values are not copied. See [Clone Stacks](/guides/clone-stack). * **Save as Profile** turns a deployed Stack into a versioned, parameterised template you can deploy on any cluster. See [Stack Profiles](/concepts/stack-profiles). ## Next [Add-ons](/concepts/addons) explains how a Helm chart becomes an add-on and how its values are managed. # Variables Source: https://docs.ankra.ai/concepts/variables Define reusable variables at organisation, cluster, and stack levels for flexible configuration management. The same Stack often needs a different domain, replica count or bucket name on each cluster. Variables let you write `${{ ankra.name }}` once in your values and manifests and set the actual value on the organisation, a cluster or a Stack. Ankra fills it in when it deploys. [How Ankra Works](/concepts/how-ankra-works) shows where variables sit in the overall model. ## Overview A variable can be set at three levels, and **the more specific one wins: Stack > cluster > organisation**. ```mermaid theme={null} flowchart TD Org[Organisation variables] --> Cluster[Cluster variables] Cluster --> Stack[Stack variables] Stack --> Final[Value used at deploy] ``` 1. **Stack variables** - set on one Stack. They win over everything else. 2. **Cluster variables** - set on one cluster. They win over organisation variables. 3. **Organisation variables** - set once for every cluster and Stack in the organisation. So a Stack variable named `domain_name` replaces a cluster or organisation variable of the same name for that Stack only. *** ## Variable Syntax Reference variables in your stacks using the following syntax: ```yaml theme={null} ${{ ankra.variable_name }} ``` **Examples:** ```yaml theme={null} # In a manifest apiVersion: v1 kind: ConfigMap metadata: name: app-config namespace: default data: domain: ${{ ankra.domain_name }} api_url: https://${{ ankra.domain_name }}/api ``` ```yaml theme={null} # In Helm values ingress: enabled: true hosts: - host: ${{ ankra.domain_name }} ``` *** ## Organisation Variables Organisation variables apply to all clusters and stacks within your organisation. Use them for values that are consistent across your entire infrastructure. ### Creating Organisation Variables **1. Navigate to Variables** Go to **Organisation Settings** → **Variables**. **2. Add a Variable** Click **Add Variable** to open the creation form. **3. Enter Variable Details** Fill in the required fields: * **Variable Name**: Must start with a letter, containing only letters, numbers, and underscores (e.g., `domain_name`, `api_key`) * **Value**: The variable's value * **Description** (optional): A brief description for documentation * **Secret**: Check this box to mask the value in the UI **4. Create** Click **Create Variable** to save. ### Common Organisation Variables | Variable Name | Example Value | Use Case | | - | - | - | | `domain_name` | `example.com` | Base domain for all services | | `registry_url` | `ghcr.io/myorg` | Container registry URL | | `environment` | `production` | Environment identifier | | `alert_email` | `alerts@example.com` | Notification email address | | `log_level` | `info` | Default logging level | *** ## Cluster Variables Cluster variables apply to all stacks deployed to a specific cluster. Use them for cluster-specific configurations that differ between environments. ### Creating Cluster Variables **1. Navigate to Cluster Settings** Go to your cluster → **Settings** → **Variables**. **2. Add a Variable** Click **Add Variable** to open the creation form. **3. Enter Variable Details** Fill in the required fields: * **Variable Name**: Must start with a letter, containing only letters, numbers, and underscores * **Value**: The variable's value * **Description** (optional): A brief description * **Secret**: Check this box to mask the value in the UI **4. Create** Click **Create Variable** to save. ### Common Cluster Variables | Variable Name | Example Value | Use Case | | - | - | - | | `cluster_domain` | `prod.example.com` | Override the [built-in cluster domain](#built-in-variables) for this cluster | | `storage_class` | `gp3` | Default storage class | | `node_selector` | `app=production` | Node selector labels | | `replica_count` | `3` | Default replica count | | `resource_quota` | `high` | Resource allocation tier | *** ## Stack Variables Stack variables are specific to a single stack and have the highest precedence. Use them for stack-specific configurations that should override cluster and organisation variables. ### Creating Stack Variables **1. Open the Stack Builder** Navigate to your cluster → **Stacks** and open an existing stack or create a new one. **2. Go to Variables Tab** Click the **Variables** tab in the Stack Builder. **3. Add a Variable** Click **Add Variable** to create a new variable entry. **4. Enter Variable Details** Fill in the fields: * **Variable Name**: Must start with a letter, containing only letters, numbers, and underscores * **Value**: The variable's value **5. Save** Click **Save Variables** to persist the changes. > [!TIP] > > Stack variables are saved as part of the stack configuration. When you deploy the stack to GitOps, the variables are resolved before manifests are written. ### Common Stack Variables | Variable Name | Example Value | Use Case | | - | - | - | | `app_version` | `v2.1.0` | Application version tag | | `replicas` | `5` | Stack-specific replica count | | `feature_flag` | `enabled` | Feature toggles | | `log_level` | `debug` | Stack-specific logging | | `custom_config` | `{"key": "value"}` | JSON configuration | ### When to Use Stack Variables Use stack variables when: * A specific stack needs values that differ from cluster defaults * You want to override cluster or organisation variables for one stack * You're testing different configurations on the same cluster * You need stack-specific feature flags or version tags *** ## Secret Variables Mark variables as secrets when they contain sensitive values like API keys, passwords, or tokens. > [!WARNING] > > Secret variables are masked in the UI but are still stored as-is on the server. For encrypted storage, use [SOPS encryption](/guides/sops) instead. ### Secret Variable Features * **Masked Display**: Values show as `••••••••` in the UI * **Reveal Option**: Click the eye icon to temporarily reveal the value * **Copy Protection**: Values can still be copied for use in configurations ### When to Use Secrets vs SOPS | Feature | Secret Variables | SOPS Encryption | | - | - | - | | **UI Masking** | Yes | N/A | | **Git Storage** | Plaintext | Encrypted | | **Runtime Decryption** | No | Yes | | **Best For** | UI-only protection | Values sealed in your Git repository | > [!TIP] > > Use secret variables for convenience in the UI. Use SOPS when you need values encrypted in your Git repository. *** ## Using Variables in Stacks ### In Manifests Reference variables anywhere in your Kubernetes manifests: ```yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: ${{ ankra.replica_count }} template: spec: containers: - name: app image: ${{ ankra.registry_url }}/myapp:latest env: - name: DOMAIN value: ${{ ankra.domain_name }} ``` ### In Add-on Values Use variables in Helm chart values: ```yaml theme={null} # Grafana add-on values grafana: adminPassword: ${{ ankra.grafana_password }} ingress: enabled: true hosts: - grafana.${{ ankra.cluster_domain }} ``` ### Built-in Variables Two names are reserved and resolve from platform state without any variable being created: | Variable | Resolves to | | - | - | | `${{ ankra.cluster_domain }}` | The cluster's public domain: by default its generated Ankra domain (`..`), or the organisation's Preview domain when a custom DNS zone you declared covers it. See [Cluster domains](/platform/organisation-settings#cluster-domains). `ankra cluster domain ` shows the value. | | `${{ ankra.stack_name }}` | The name the stack carries on its cluster, so a profile deployed twice renders distinct namespaces and hostnames. | A stack, cluster, or organisation variable of the same name overrides the built-in. A cluster with no public domain at all leaves the reference literal, which an Ingress host rejects loudly rather than resolving to nothing. ### Variable Resolution When you deploy a stack, Ankra resolves variables in this order: 1. Check for a matching stack variable (highest priority) 2. If not found, check for a matching cluster variable 3. If not found, check for a matching organisation variable 4. If still not found, the literal `${{ ankra.variable_name }}` is left in place (which will likely cause deployment errors) > [!NOTE] > > This precedence allows you to define sensible defaults at the organisation level, environment-specific overrides at the cluster level, and stack-specific customizations when needed. *** ## Managing Variables ### Copying Variable Syntax Click the **Copy** icon next to any variable to copy its full syntax: ``` ${{ ankra.variable_name }} ``` This copies the ready-to-use syntax for pasting into your manifests. ### Deleting Variables > [!WARNING] > > Deleting a variable that is in use by stacks will cause deployment failures. Review stack usage before deleting. 1. Click the **Delete** (trash) icon next to the variable 2. The variable is immediately removed ### Variable Naming Rules Variable names must follow these rules: * Start with a letter (a-z, A-Z) * Contain only letters, numbers, and underscores * Be unique within their scope (organisation or cluster) **Valid names:** `domain_name`, `API_KEY`, `replica_count_v2` **Invalid names:** `123_name`, `my-variable`, `name with spaces` *** ## Managing variables via API and CLI Organisation and cluster variables have full CRUD over the API and the [Ankra CLI](/integrations/ankra-cli), so you can manage them from scripts and pipelines, not just the UI. (Stack variables are managed as part of the stack via the CLI's stack commands.) ### CLI ```bash theme={null} # Organisation scope - available to every cluster ankra org variables set DB_HOST db.example.com --description "Primary DB" ankra org variables list # Cluster scope - shadows org variables on that cluster ankra cluster variables set DB_HOST db.prod.example.com --cluster prod ``` `set` is an upsert, and values can be read from stdin with `-` so secrets can be piped in from a vault or password manager. See the [CLI reference](/integrations/ankra-cli) for the full command set. ### REST API Both scopes expose the same list/create/update/delete shape under `/api/v1/org/variables` (organisation) and `/api/v1/org/clusters/imported/{cluster_id}/variables` (cluster), bearer-token authenticated. See the [API Reference](/api-reference/introduction) for endpoints and schemas. *** ## AI Prompts Press `⌘+J` to open the AI Assistant and use these prompts: > [accordion] Create Variables for a Stack > > ``` > I need to deploy the same stack to dev and prod clusters with > different domain names. Help me set up variables. > ``` > [accordion] Migrate Hardcoded Values > > ``` > Review my stack and suggest which values should be > converted to variables. > ``` *** ## Related > [card] [Stacks](/concepts/stacks) > > Build stacks that use variables. > [card] [SOPS Encryption](/guides/sops) > > Encrypt sensitive values in Git. > [card] [Organisation Settings](/platform/organisation-settings) > > Configure organisation-level settings. > [card] [Cluster Settings](/platform/cluster-settings) > > Configure cluster-level settings and variables. # Get started with Ankra CI Source: https://docs.ankra.ai/get-started/ankra-ci Move a repository's CI to Ankra Pipelines in about 20 minutes: choose the cluster your builds run on, connect the repository, commit .ankra/pipeline.yaml and get a green Ankra pipeline check on your next pull request. Ankra CI - Ankra Pipelines in the product - runs your repository's CI inside a Kubernetes cluster you already run through Ankra. You describe the pipeline in one file, `.ankra/pipeline.yaml`. Every push and pull request then runs it as hardened Kubernetes Jobs on your own nodes. The result lands on the commit as an `Ankra pipeline` check, next to the logs, test results and scan findings in Ankra. This tutorial takes one repository from nothing to a green check on a pull request. Every step ends with something you can see, so you know it worked before you move on. ## Why teams move to Ankra CI | | GitHub Actions, GitLab CI | Ankra CI | | - | - | - | | **Where jobs run** | Hosted runners billed per minute, or a self-hosted runner fleet you patch and scale | Pods on a cluster you already operate, under the quotas and node groups you set | | **Secrets** | Copied into each repository's CI settings, readable by any workflow that names them | References to your Ankra variables and registries, delivered to one step as a file and never written to a log | | **What a pull request can change** | Anything in the workflow file, including which secrets it reads | The steps and scripts. Secrets, network access, compute and caches stay as an administrator approved them | | **Image security** | Push first, scan afterwards, if a scan step exists | `build`, `scan`, `gate`, `publish`: nothing reaches your registry until the gate has judged the exact digest | | **Where the evidence lives** | The CI provider | Ankra, with the same status on GitHub, GitLab or Bitbucket Cloud | ## Choose your starting point > [card] [I already have GitHub Actions or GitLab CI](/guides/migrate-from-github-actions) > > Ankra converts your existing workflow into `.ankra/pipeline.yaml` and opens a pull request. Then come back for steps 1, 2, 6 and 7. > [card] [I am deploying a new app with Ankra](/guides/deliver-your-app) > > The setup pull request Ankra opens for a new application already commits a pipeline that tests, builds, scans and publishes. Steps 1 and 2 are all you need here. Step 7 is done for you when you create the application from the portal and hold pipeline management access. > [card] [I want to start from a blank file](#1-choose-the-cluster-your-builds-run-on) > > Follow this page from top to bottom. It is the best way to learn how a pipeline fits together. ## Before you start * **An Ankra organisation where you are an administrator.** Choosing the CI cluster and approving a pipeline are administrator actions. Anyone with pipeline access can do the rest. * **A cluster connected to Ankra** with a healthy agent and some spare capacity. Any cluster works, and it does not have to be dedicated to CI: steps run in their own `ankra-ci` namespace, in a worker pool kept apart from the agent's deploy work. Many teams start on their staging cluster and move CI to its own cluster later. * **The repository connected to Ankra** through the [Ankra GitHub App](/integrations/github), [GitLab](/integrations/gitlab) or [Bitbucket Cloud](/integrations/bitbucket-cloud). On GitHub, the App needs the **Checks: write** permission to post the `Ankra pipeline` check. * **The Ankra CLI**, logged in to the right organisation: ```bash theme={null} brew install ankraio/tap/ankra ankra login ankra org switch ``` Other install methods are on the [CLI page](/integrations/ankra-cli). ## 1. Choose the cluster your builds run on Pipelines run on one cluster per organisation, the **pipeline cluster**. Until you choose one, a push has nowhere to run, and the commit gets a failing check that asks you to choose a cluster. > [tab] CLI > > ```bash theme={null} > ankra org ci-settings set --cluster > ankra org ci-settings get > ``` > [tab] Portal > > Open **Settings → Pipelines**. Under **Pipeline cluster**, pick the cluster in **Cluster** and click **Save**. **You should see** your cluster named as the pipeline cluster in `ankra org ci-settings get`, or in the portal. ## 2. Turn on pipeline workers on that cluster The Ankra agent on every cluster starts with **zero** pipeline workers, which means pipelines are switched off there. Give it some. Each worker runs one pipeline step at a time, separately from the agent's deploy and read work, so a busy pipeline never delays a deploy. > [tab] CLI > > ```bash theme={null} > ankra cluster agent ci set --workers 2 --cluster > ankra cluster agent ci get --cluster > ``` > [tab] Portal > > In **Settings → Pipelines**, under **Workers and step placement**, set **Pipeline-step workers** to `2` and click **Save**. Ankra applies the setting through the agent's own release. A recent agent picks up the new count straight away, and an older one restarts to pick it up. Do not `helm upgrade` the agent yourself to change it, because Ankra re-renders that release and would overwrite your value. **You should see** the new worker count when you read the settings back. Start with 2 and raise it once you see steps waiting: the run page says when a step is waiting for a free worker. ## 3. Connect the repository > [tab] CLI > > Find the name of the organisation's Git credential, then connect the repository with it: > > ```bash theme={null} > ankra credentials list > ankra pipeline repositories connect --provider github --owner my-org --name my-repo --credential > ``` > > Use `--provider gitlab` or `--provider bitbucket` for those hosts, and add `--default-branch ` when it is not `main`. > > The output starts with `Connected my-org/my-repo as repository `. Keep that id - the commands below take it as `--repository `, and `ankra pipeline repositories list` shows it again later. > [tab] New portal (application) > > When the repository already backs an [Ankra application](/concepts/applications), open the application in the new Ankra portal (`/next` on your portal's address, for example `https://platform.ankra.app/next/applications`), then **Technical details → Pipelines**. Under **Connect this repository to pipelines**, pick the **Source connection**, click **Review pipeline connection**, then **Confirm pipeline connection**. > > For an application, use `--application ` in the commands below instead of `--repository`. Inside a checkout of the application's repository you can leave it out: the CLI works it out from your `origin` remote and says which application it picked. The connect output's second line reads `Definition: absent` until a pipeline file is on the default branch. That is expected - step 5 stores one. **You should see** the repository in `ankra pipeline repositories list`, and under **Settings → Pipelines** in the portal. ## 4. Add `.ankra/pipeline.yaml` On a new branch, create `.ankra/pipeline.yaml`. This first pipeline checks out the commit and runs your tests on every push to `main`, on every pull request into it, and whenever you start it by hand. Pick your toolchain: > [tab] Node.js (npm) > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "node:24-alpine" > needs: ["checkout"] > env: > npm_config_cache: "/workspace/.ankra-node-cache/npm" > run: | > npm ci > npm test > ``` > [tab] Python (pip) > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "python:3.13-slim" > needs: ["checkout"] > env: > PIP_CACHE_DIR: "/workspace/.ankra-python-cache/pip" > run: | > python -m venv /workspace/.ankra-venv > . /workspace/.ankra-venv/bin/activate > pip install -r requirements.txt > pytest > ``` > [tab] Go > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "golang:1.26" > needs: ["checkout"] > env: > GOPATH: "/workspace/.ankra-go" > GOCACHE: "/workspace/.ankra-go/cache" > GOMODCACHE: "/workspace/.ankra-go/mod" > GOTMPDIR: "/workspace/.ankra-go/tmp" > GOFLAGS: "-buildvcs=false" > run: | > mkdir -p "$GOTMPDIR" > go test ./... > ``` Each of these was run on a real pipeline cluster before it was published. A few lines in them are there because of how Ankra runs a step, and every pipeline you write will need them: * **`checkout` is a stage you declare.** Every run starts from an empty workspace, mounted at `/workspace`, and nothing is cloned unless a `checkout` stage does it. Stages that come after it list it in `needs`. * **`manual: {}` lets you start a run by hand.** Without it, `ankra pipeline run` and the portal's **Run pipeline** button answer that no trigger matches. * **`network: "egress-https"` lets a step download packages.** A `run` stage has no network access by default. `egress-https` allows HTTPS to the public internet and nothing private. * **Package caches point into `/workspace`.** A step runs as a non-root user on a read-only root filesystem. `HOME` is `/tmp`, which is writable but small and held in memory, so the file sends each package manager's cache to `/workspace`, the run's shared, disk-backed volume. More toolchains, image builds and database-backed tests are in [Starter pipelines](/guides/ankra-ci-starter-pipelines). ## 5. Validate and store the definition Check the file before you commit it. `validate` plans it for a sample push and a sample pull request and prints which stages would run: ```bash theme={null} ankra pipeline validate .ankra/pipeline.yaml --repository ``` A `fatal` result means the file cannot run - fix what it names. A `warn` means it runs, but probably not the way you meant, so read it. Then store the file as the repository's definition, so the very first webhook has something to run: ```bash theme={null} ankra pipeline definition put .ankra/pipeline.yaml --repository ``` On GitHub, every later run reads `.ankra/pipeline.yaml` from the commit it runs, so changing the file in a pull request changes that pull request's run. On GitLab and Bitbucket Cloud the stored definition is what runs, so run `definition put` again whenever you change the file. > [!TIP] > > You can also ask Ankra's AI to check a pipeline in the portal chat - "validate this .ankra/pipeline.yaml" with the file pasted in - which needs no repository at all. ## 6. Open a pull request and watch the run Commit the file on your branch, push it and open a pull request into `main`: ```bash theme={null} git checkout -b add-ankra-ci git add .ankra/pipeline.yaml git commit -m "Add the Ankra pipeline" git push -u origin add-ankra-ci ``` Shortly after the push, the pull request gets an **Ankra pipeline** check with a table of steps. On GitHub it has a **Cancel** button while the run is live and GitHub's own **Re-run** afterwards. Ankra also keeps one status comment on the pull request up to date with the same table. Follow the same run from your terminal: ```bash theme={null} ankra pipeline list --repository ankra pipeline get --repository --branch add-ankra-ci --latest --watch ankra pipeline logs --repository --step test --follow ``` In the portal, the run has a timeline, each step's output, test results and artifacts, with **Re-run failed steps** and **Cancel run** on the run page. **You should see** both steps succeed. On a repository's first pipeline the check then waits with the title **Awaiting authority approval** - that is the next step, not a failure. ## 7. Approve the pipeline once The check waits because a pipeline that runs on pull requests carries a security setting an administrator has to accept: `fork_policy: read_only`, which runs pull requests from forks with no secrets, no credentials and no network beyond what their checkout, build and scan need. It is the safest setting there is, and Ankra still asks a person to approve it rather than granting anything on its own. The check lists it in a table: | Protected setting | Approved today | This pipeline asks for | | - | - | - | | `fork_policy` | - | `read_only` | Approve it: > [tab] Portal > > Open **Settings → Pipelines**. **Pending approvals** lists every repository waiting, with the protected settings it asks for. Review them and click **Approve**. You can also approve from the run page, or press **Approve authority** on the GitHub check if your GitHub account is linked to your Ankra profile. > [tab] CLI > > The pull request comment prints the exact command with the definition id filled in. Read what you are granting, then approve it: > > ```bash theme={null} > ankra pipeline definitions get > ankra pipeline definitions approve > ``` Approving needs an organisation administrator with pipeline management access, signed in as themselves - an API token cannot approve. **You should see** the check turn green. Every later pull request gets a plain green or red check, as long as nobody changes a protected setting. Merge the pull request: the push to `main` runs the pipeline again, this time as a branch run. ### When you add more to the pipeline The same approval covers more than the fork policy. These settings are **protected**: secrets, credentials, caches, CPU and memory, a stage's timeout, sidecar services such as a database, network access beyond `egress-https`, and where a step runs. A pull request can change what a pipeline *does* - its stages, scripts and images - but not what it is *allowed to touch*. Otherwise anyone who can open a pull request could read your secrets or claim your biggest nodes. So a protected setting takes effect only once an administrator approves the version of the file on the default branch. Until then, runs execute as if the setting were not there, and the check and `ankra pipeline validate` both say which settings were left out. The order is always the same: merge the change to the default branch, approve it, and the waiting runs re-run on their own. A change that touches no protected setting never needs approval. ## 8. Make it the check that guards `main` Once the pipeline has been green on a few pull requests: 1. In your repository's branch protection rules (on GitHub, **Settings → Branches** or **Rules**), add **Ankra pipeline** as a required status check. 2. Remove or disable the old workflow (`.github/workflows/*.yml`, `.gitlab-ci.yml`), so each commit is built once. [Move a team to Ankra CI](/guides/move-to-ankra-ci) has the full rollout plan for more than one repository. ## What you have now * [x] A pipeline cluster and pipeline workers for the organisation * [x] A repository connected to Ankra Pipelines * [x] `.ankra/pipeline.yaml` that runs on every push and pull request * [x] An `Ankra pipeline` check on every commit, and run history in Ankra * [x] Protected settings that only an administrator can grant ## Next steps > [card] [Starter pipelines](/guides/ankra-ci-starter-pipelines) > > Copy-paste pipelines for Node.js, Python, Go and Rust, caches, a Postgres service, and build, scan and publish an image. > [card] [Move a team to Ankra CI](/guides/move-to-ankra-ci) > > A rollout plan from one pilot repository to all of them, with a GitHub Actions to Ankra mapping. > [card] [Troubleshooting](/guides/ankra-ci-troubleshooting) > > A push that started nothing, a step that waits, a step that cannot write or download. > [card] [Ankra Pipelines in depth](/guides/ankra-pipelines) > > How a run is planned and dispatched, the security model, organisation CI settings and capacity. # Guided Setup Source: https://docs.ankra.ai/get-started/guided-setup Go from a new organisation to a live website in four steps: a free cluster, your welcome credit, where the code lives, and a website Ankra generates from one sentence. Guided setup walks a new organisation from nothing to a website running on its own cluster, with a public address. Open it from the first-run screen (**Guided setup: a live website in ten minutes**) or go straight to `/organisation/onboarding`. There are four steps. Each one shows its progress and remembers where you got to, so you can leave and come back. **1. Create your cluster** One click creates your organisation's free cluster - a [playground](/guides/cluster-sandbox). Playgrounds are moving to Ankra Cloud dev clusters, a single-node Kubernetes cluster managed by Ankra; this is rolling out. The step shows the cluster provisioning, the domain your apps will be published under, and, for a free cluster, when it ends. If your organisation already has a playground, the step picks it up instead of creating a second one. **2. Unlock your credit** Click **Add payment method** to save a card. Saving it unlocks **€59 of credit, valid for 60 days**, which pays for your resources before anything is charged to the card. > [!TIP] > > **You will never be charged without a top-up.** Your card is only charged when you add credit yourself. Automatic top-up stays off unless you turn it on. We recommend turning automatic top-up on in **Billing** once you are running something you care about: when your credit runs low it adds more, so your services keep running without a pause. See [Credit and payment method](/platform/billing#credit-and-payment-method) for how credit is spent. Cards are handled by Stripe; Ankra never sees your card number. You can skip this step and come back to it, but without a card a free Ankra Cloud dev cluster lasts 24 hours. **3. Choose where the website lives** Pick one: * **The Ankra default** - no repository. Ankra deploys the generated site straight to your cluster as a Stack. * **A new GitHub repository** - Ankra creates the repository in the GitHub account or organisation you choose (public or private), commits the site to it, and registers it as an [application](/concepts/applications). Every push to the repository is built and deployed to your cluster. This option needs a [GitHub connection](/integrations/github). **4. Launch your website** Describe the website you want in one sentence - for example *"a landing page for a neighbourhood bakery with opening hours and a contact form"*. Ankra's AI generates a complete, responsive site and deploys it to your cluster. The step follows the site through designing, deploying and live, then links to the public address and, if you chose a repository, to the code. ## What you end up with * A Kubernetes cluster managed by Ankra, with the Ankra agent installed and connected. * A website served from that cluster at a public address under the cluster's domain. * With a repository: an application whose builds and deploys run on every push, the same way as any other [application](/concepts/applications). From here, everything in the [Quickstart](/get-started/quickstart) works on the same cluster: deploy a Stack, browse live resources, and ask Ankra's AI about what is running. ## Good to know * Website generation uses your organisation's AI allowance. One website is generated at a time per organisation. * Steps you cannot complete - for example adding a card without billing permissions - can be skipped. Ask a billing owner in your organisation to add the payment method. * The free cluster is for development and evaluation. See [Playground](/guides/cluster-sandbox) for its limits and how long it lasts. # Quickstart Source: https://docs.ankra.ai/get-started/quickstart In about 15 minutes, connect a Kubernetes cluster to Ankra (or start a free playground), deploy your first Stack, and ask Ankra's AI about what is running. Each step below ends with something you can see, so you know it worked before you move on. > [!TIP] > > New organisation? [Guided setup](/get-started/guided-setup) takes you from a free cluster to a live website, generated from one sentence, in four steps. **What you need:** an Ankra account ([sign up at platform.ankra.app](https://platform.ankra.app)), and either a Kubernetes cluster you can run `helm` against, or nothing at all - the free playground gives you a cluster. *** ## 1. Get a cluster > [tab] No cluster: use the playground > > The [playground](/guides/cluster-sandbox) is a real, writable Kubernetes cluster that Ankra runs for you. It is ready in about two minutes and needs no cloud account. > > **1. Start it** > > On your first visit, the dashboard offers **See a demo app first** - click **Start the playground**. You land on the new cluster's overview and can watch it provision. > > **2. Check it is ready** > > When it is ready, the overview shows **Your app is live** with the public URL of a small starter app Ankra deployed for you. Open it. > > How long the free playground lasts depends on where it runs - see [Playground](/guides/cluster-sandbox). As an Ankra Cloud dev cluster it lasts 24 hours without a payment method, and saving a card keeps it running, paid from your credit. Everything you do here works the same on your own cluster. > [tab] Connect your own cluster > > Any conformant cluster works: EKS, GKE, AKS, k3s, kind, minikube or on-premises. You need `helm` and a kubeconfig that can create a namespace. > > **1. Create the cluster in Ankra** > > Go to **Clusters**, click **Import**, give the cluster a name, and click **Import cluster**. The Git repository and add-on fields are optional - you can add both later. > > **2. Install the agent** > > Ankra shows a `helm` command for this cluster. Copy it exactly as shown - it carries your cluster's token and pins the agent version - and run it wherever your kubeconfig points at the cluster. It looks like this: > > ```bash theme={null} > helm upgrade --install ankra-agent oci://registry.ankra.cloud/ankra/ankra-agent \ > --version \ > --set config.ankra_url=https://platform.ankra.app \ > --set config.token= \ > --namespace=ankra --create-namespace > ``` > > The agent only makes outbound connections to Ankra, so it works behind firewalls, NAT and private networks. > > **3. Check it is connected** > > The cluster's status changes from **Awaiting Connection** to **Healthy**, usually within a minute. Open the cluster: you can now browse its workloads, events and logs. > > Not connecting? Run `kubectl get pods -n ankra` and see [Ankra Agent troubleshooting](/concepts/cluster-agent). > [!TIP] > > Want Ankra to build a new cluster on Hetzner, AWS, DigitalOcean or another provider, or create EKS, GKE or AKS for you? See [Add a cluster](/guides/import-cluster) - then come back to step 2. *** ## 2. Deploy your first Stack A **Stack** is what a cluster should run: Helm charts (called [add-ons](/concepts/addons)) and your own Kubernetes YAML ([manifests](/concepts/stacks#manifests)), deployed together in the right order. You will deploy [Grafana](https://grafana.com/oss/grafana/), the dashboarding tool, so there is something real to look at. **1. Open the Stack Builder** Open your cluster, go to **Stacks**, and click **+ Create**. The builder canvas opens. **2. Add the add-on** Click **Add Add-on**, search for **Grafana**, and select it. The defaults are fine - click the add-on on the canvas if you want to see or change its Helm values. **3. Deploy it** Name the Stack (for example `dashboards`) and click **Create Stack**. This saves and deploys it in one step, straight through the agent - no Git repository needed. **4. Check it is running** The Stack's status moves to deployed within a minute or two. Go to **Kubernetes** → **Workloads** → **Pods** and you will see the `grafana` pod **Running**. > [!NOTE] > > You can build the same Stack by asking the AI instead: press ⌘ + J (Ctrl + J on Windows and Linux) and type *"Add a Stack with Grafana to this cluster"*. The AI picks the chart, builds the Stack and deploys it for you. *** ## 3. Ask the AI about it Ankra's AI sees what you are looking at - the pod's logs, events, manifest and recent deploys - so you can ask plain questions without pasting anything. **1. Open the assistant on the pod** Open the `grafana` pod from step 2 and press ⌘ + J. Leave the mode on **Ask**, which reads and explains but does not change anything already running. **2. Ask a question** Try *"Is this pod healthy, and how do I open Grafana and log in?"* or *"What would happen if this pod restarted?"*. The answer is based on your cluster's actual state. When you want the AI to make changes, switch to **Agent** (it asks you to confirm each change) or **Plan** (you approve a whole plan once). See [AI Assistant](/platform/ai-assistant) for what each mode may do. *** ## 4. Optional: keep the cluster's configuration in Git Stacks work without Git. Connect a GitHub repository when you want every change to be reviewed, versioned and reversible - Ankra then commits each Stack change to the repository, and commits you push are deployed back to the cluster. **1. Connect a repository** Open the cluster, go to **Settings** → **GitOps**, choose a GitHub credential (or add one), and pick the repository. Nothing extra is installed on the cluster. **2. Check the commit** Your `dashboards` Stack appears in the repository under `clusters/`, as Helm values and manifests you can review like any other code. See [GitOps](/concepts/gitops) for how two-way sync and conflicts work. *** ## You are done You now have: * a cluster connected to Ankra, showing **Healthy** * a Stack deployed to it, with its pod **Running** * an answer from the AI based on your cluster's real state ## What's next > [card] [Ship your own app](/guides/deliver-your-app) > > Get a repository running on a cluster and redeployed on every merge. > [card] [Build a monitoring stack](/guides/monitoring-stack) > > Prometheus, Grafana and Loki as one Stack. > [card] [Use the CLI and your AI assistant](/integrations/ankra-cli) > > `ankra chat`, `ankra cluster apply`, and the MCP server for Claude and Cursor. > [card] [Reuse a Stack everywhere](/concepts/stack-profiles) > > Turn a working Stack into a template for every cluster. > [card] [Variables](/concepts/variables) > > One Stack, different values per cluster or environment. > [card] [Get alerted](/guides/alerts) > > Page your team in Slack when something in the cluster goes wrong. Stuck on a step? [Contact support](/platform/support) or ask in the [Ankra community Slack](https://join.slack.com/t/ankra-community/shared_invite/zt-3a5rem8f8-cUho4epX2MoLT83bFf~VSA). # Azure Kubernetes Service (AKS) Source: https://docs.ankra.ai/guides/aks-clusters Provision, import, and operate Azure Kubernetes Service (AKS) clusters with Ankra - region-scoped catalogue, availability zones, spot pools, VNet subnets, maintenance windows, retention-aware deletion, GitOps, and AI-assisted operations. [Azure Kubernetes Service (AKS)](https://azure.microsoft.com/en-us/products/kubernetes-service) is Microsoft Azure's managed Kubernetes service. Azure runs the control plane; Ankra provisions the cluster, then manages everything on top - node pools, Kubernetes upgrades, add-ons, Stacks, GitOps, and AI-assisted operations. This page covers AKS specifically. For the shared concepts across all managed providers - live options, discovery/import, and day-2 API patterns - see [Managed Kubernetes](/guides/managed-kubernetes). > [!TIP] > > **Already running AKS?** You can bring it into Ankra two ways. A **managed import** uses your Azure credential: in **Clusters**, click **Create cluster** and choose **Import existing**, or run `ankra cluster managed discover --provider aks` and `ankra cluster managed import --provider aks`. Ankra fetches the kubeconfig and installs the agent, so there is nothing to run on the cluster, and the cluster gains the same node-pool and upgrade operations as clusters Ankra created. The **Helm import** needs no cloud credential and works for any cluster: run the one `helm` command the Import dialog gives you - see [Import an existing cluster](/guides/import-cluster#import-an-existing-cluster). ## Why AKS with Ankra * **A catalogue for the location you pick** - Kubernetes versions, VM sizes, availability zones, subnets and prices are read for the selected Azure location, and sizes your subscription cannot launch there are left out. * **Spot node pools, zones and OS disks** - run a User pool on spot capacity with an optional price cap, spread a pool across availability zones, and choose a managed or ephemeral OS disk per pool. * **Bring your own network** - place every node pool in an existing virtual network subnet, or let AKS create its VNet. * **Planned maintenance** - pin the weekly window in which Azure may run control-plane upgrades and node reboots. * **Stop and start** - AKS is the only managed provider with native cluster stop/start, exposed in the portal, CLI, API and Ankra's AI. * **Retention-aware deletion** - keep or delete the resource group Ankra created for the cluster; a resource group you brought is never deleted. ## Prerequisites An **Azure service principal** stored in Ankra with the roles described in [Azure Credentials](/platform/credentials/azure). Store it from the portal or with `ankra credentials azure create`. GitOps is optional: connect a repository at creation and Ankra commits the cluster's Stack definitions to Git. ## Creating an AKS cluster > [tab] Portal > > **1. Create Cluster** > > Go to **Clusters** → **Create Cluster** and pick Azure's **Cloud Managed** action. > > **2. Credential, location and network** > > Select the Azure credential and a location (for example `westeurope`). The catalogue reloads for that location. Optionally pick a **virtual network subnet** for the node pools, and name an existing **resource group** to adopt - leave it empty and Ankra creates and owns one. > > **3. Node pools** > > Define one or more pools: name, VM size (with live pricing), count, labels, taints and autoscaling bounds. Per pool you can pick **availability zones**, the **OS disk** type and size, and - on every pool except the first - **spot capacity** with an optional hourly price cap. The first pool is the AKS System pool and always runs on regular capacity. > > **4. Kubernetes and control plane** > > Pick a Kubernetes version or keep the default, then the **network plugin** (`azure` or `kubenet`), the **SKU tier** (`Free`, `Standard` with the uptime SLA, or `Premium`), whether the API server is **private**, and an optional **maintenance window**. > > **5. GitOps (optional) and create** > > Optionally connect a Git repository, then create. Ankra runs the AKS preflight (credential, location, version, name, size availability, zones, OS disk support, vCPU quota, subnet), submits the cluster, shows it while Azure provisions it, retrieves the kubeconfig, and installs the Ankra Agent. > [tab] CLI > > ```bash theme={null} > ankra cluster managed create \ > --provider aks \ > --name my-aks-cluster \ > --credential-id \ > --location westeurope \ > --node-pool-name system \ > --node-pool-size Standard_D2s_v5 \ > --node-pool-count 2 \ > --zone 1,2,3 \ > --os-disk-type Ephemeral \ > --network-plugin azure \ > --sku-tier Standard \ > --vnet-subnet-id \ > --maintenance-window Saturday:22:4 > ``` > > Every `--` flag after `--node-pool-count` is optional. `--maintenance-window` takes `::` in UTC. Add spot pools after creation with `node-pool add --spot`. > [tab] API > > ```bash theme={null} > curl -X POST https://platform.ankra.app/api/v1/clusters/managed/aks \ > -H "Authorization: Bearer $ANKRA_API_TOKEN" \ > -H "Content-Type: application/json" \ > -d '{ > "name": "my-aks-cluster", > "credential_id": "", > "location": "westeurope", > "kubernetes_version": "1.31.1", > "node_pools": [ > {"name": "system", "size": "Standard_D2s_v5", "count": 2, "zone": "1,2,3", "root_volume_type": "Ephemeral"}, > {"name": "batch", "size": "Standard_E4s_v5", "count": 3, "spot": {"enabled": true, "max_price": 0.05}, > "autoscaling": {"enabled": true, "min_count": 1, "max_count": 10}} > ], > "aks": { > "network_plugin": "azure", > "sku_tier": "Standard", > "private_cluster": false, > "vnet_subnet_id": "", > "maintenance_window": {"day": "Saturday", "start_hour": 22, "duration_hours": 4} > } > }' > ``` ### AKS options | Field | Description | | - | - | | `aks.resource_group` | Existing resource group to adopt. Omit it and Ankra creates `ankra-`, tags it as Ankra-owned, and deletes it with the cluster. An adopted group is never modified or deleted. | | `aks.network_plugin` | `azure` (default; pods get VNet addresses, required with a customer subnet) or `kubenet` | | `aks.sku_tier` | `Free` (default, no control-plane fee), `Standard` (uptime SLA, about \$73 per month) or `Premium` | | `aks.private_cluster` | Private API server endpoint | | `aks.vnet_subnet_id` | ARM id of the subnet every node pool joins; pick one from the catalogue's `private_networks`. Omit for an AKS-managed VNet. | | `aks.maintenance_window` | `{day, start_hour, duration_hours}` - an English weekday, a 0-23 UTC hour and 4-24 hours | ### Node pool options | Field | Description | | - | - | | `zone` | Availability zone(s) from the catalogue's `zones`, comma-separated to spread the pool (`"1,2,3"`). Omit for a regional pool. | | `root_volume_type` | OS disk type, `Managed` or `Ephemeral`. Ephemeral needs a VM size that supports it; preflight checks. | | `root_volume_size_gb` | OS disk size, at least 30 GB | | `spot` | `{"enabled": true, "max_price": }` - spot capacity with an optional cap in USD per hour; omit `max_price` to pay up to the on-demand price. Refused on the first pool. | > [!NOTE] > > AKS node pool names are lowercase alphanumeric, start with a letter and are at most 12 characters; cluster names are at most 63 characters. The first pool is the cluster's System pool: AKS requires at least one System pool, so Ankra refuses to delete the only one and refuses spot on it. ### Preflight Every create runs a preflight you can also call on its own (`POST .../managed/aks/preflight` with the create body). It checks the service principal, pool names, the location and version, a name collision in the resource group, that each VM size is available to your subscription in that location and offered in the requested zones, ephemeral OS disk support, the subscription's regional and per-family vCPU quota against the pools' maximum node counts (and the separate regional spot vCPU quota for spot pools), the subnet, and the maintenance window. Catalogue reads the service principal lacks permission for become warnings; a failing item blocks the create. ## Verify The create progress view follows the control plane, the node pools, kubeconfig retrieval and the Ankra Agent install. The cluster shows **Online** once the agent has connected. Then point `kubectl` at it through Ankra - this needs a [Cluster Access](/guides/cluster-access) grant - and check that every node is `Ready`: ```bash theme={null} ankra cluster kubeconfig add --use kubectl get nodes ``` See [Accessing Clusters with kubectl](/guides/kubeconfig). ## Day-2 operations Node pools, upgrades, stop/start and deletion work from the CLI, portal or API. The CLI examples use `--provider aks`: ```bash theme={null} # add a spot pool spread over two zones ankra cluster managed node-pool add --provider aks \ --name batch --size Standard_E4s_v5 --count 3 --zone 1,2 \ --spot --spot-max-price 0.05 --autoscaling --autoscaling-min 1 --autoscaling-max 10 # scale a pool to a fixed count ankra cluster managed node-pool scale batch --provider aks --count 5 # turn autoscaling off again ankra cluster managed node-pool update batch --provider aks --autoscaling=false --count 3 # upgrade the cluster ankra cluster managed upgrade --provider aks --version 1.32.3 # delete the cluster ankra cluster managed delete --provider aks ``` In the portal, **Nodes → Node pools** lists every pool with its zones, OS disk and spot state; new pools pick their VM size from the location's catalogue. ### Upgrades An upgrade moves the control plane **and every node pool** to the target version in one operation - the same request `az aks upgrade` sends. AKS refuses a control plane more than two minor versions ahead of a pool, so Ankra never leaves pools behind. Available targets come from Azure's upgrade profile; preview versions are skipped. ### Stopping and starting AKS is the only managed provider with native cluster stop/start, and Ankra exposes it in the portal Danger Zone, CLI, API and Ankra's AI. Stopping deallocates the control plane and agent pools (Azure stops billing for compute) while keeping all configuration; starting brings the same cluster back. ```bash theme={null} ankra cluster managed stop --provider aks ankra cluster managed start --provider aks ``` Both operations are asynchronous on Azure's side - the cluster shows `stopped` or `running` once Azure completes the transition. Ankra advertises the capability per provider as `supports_stop_start`. ## Importing an existing AKS cluster Discover the clusters in your subscription and adopt one into Ankra without touching it - discovery and import run from the portal, the CLI (`ankra cluster managed discover|import --provider aks`) or the API (see [Managed Kubernetes - importing existing clusters](/guides/managed-kubernetes#importing-existing-clusters)). Import brings the cluster's node pools, their zones, OS disks, spot settings, labels and taints into Ankra, and records the subnet the pools use. > [!WARNING] > > Ankra needs the cluster's admin kubeconfig, which Azure only issues while **local accounts** are enabled. A cluster configured for Microsoft Entra ID only (`disableLocalAccounts: true`) is refused at import with an explanation; enable local accounts (`az aks update --enable-local-accounts`) before importing it. ## Deleting, disconnecting and retention These operations are deliberately different: * **Disconnect** removes the Ankra integration and cluster-local privileged material but never calls Azure. Use it for imported clusters. * **Delete at Azure** deletes the AKS cluster (Azure removes its `MC_` node resource group with it) and then removes the Ankra integration. It is allowed normally for clusters Ankra created; an imported cluster needs the explicit force option. With `retention_policy=delete` (the default) Ankra also deletes the resource group it created for the cluster. With `retention_policy=retain` that group survives and is reported in `retained_resources`. A resource group you named at creation is adopted: Ankra never deletes it and always reports it as retained. > [!WARNING] > > A deleted resource group takes every resource in it with it. Keep other workloads out of the `ankra-` group Ankra creates, or adopt a group of your own. ## Troubleshooting * **403 on the resource group or subscription** - the service principal lacks a role listed in [Azure Credentials](/platform/credentials/azure). Catalogue reads need `Reader` at the subscription; creating clusters needs the AKS and network roles on the resource group. * **Preflight fails on `quota` or `quota.spot`** - the pools' maximum node counts exceed the free regular or per-family vCPUs, or the spot pools exceed the regional spot (low-priority) vCPU quota, which Azure sizes separately and often at only a few vCPUs on new subscriptions. Request a quota increase for that location or pick smaller sizes. * **VM size "not available to this subscription"** - Azure restricts the SKU in that location for your subscription. The catalogue already omits such sizes; a size typed by hand is caught at preflight. * **"cannot run on spot capacity"** - spot was requested on the first pool. Put spot workloads in a later pool. * **Import refused with "local accounts disabled"** - see the warning under importing. * **Upgrade refused by Azure** - a pool is more than two minor versions behind the target. Upgrade in steps of one minor version. ## Related * [Managed Kubernetes overview](/guides/managed-kubernetes) * [Azure Credentials](/platform/credentials/azure) * [Cluster settings](/platform/cluster-settings) # Alerts Source: https://docs.ankra.ai/guides/alerts Monitor your clusters and resources with customizable alert rules > [!NOTE] > > Ankra Alerts help you stay informed about your cluster health, resource issues, and operational events with configurable notifications. ## What are Alerts? Alerts in Ankra let you define rules that automatically monitor your infrastructure and notify you when specific conditions are met. You can: * **Monitor Cluster Health:** Get notified when clusters go offline or agents disconnect * **Track Resource Status:** Watch for issues with GitOps repositories, add-ons, manifests, and stacks * **Configure Conditions:** Set up multiple conditions with AND/OR logic for precise alerting * **Receive Notifications:** Send alerts to any webhook-enabled service (Slack, Teams, PagerDuty, etc.) ### How Alerts Work ```mermaid theme={null} flowchart LR A[Resource State Change] --> B[Condition Evaluation] B --> C{Conditions Met?} C -->|Yes| D[Alert Triggered] C -->|No| E[No Action] D --> F[Webhook Notifications] D --> G[AI Incident Created] F --> H[Slack / Teams / PagerDuty] ``` *** ## Automatic AI Analysis Every time an alert triggers, Ankra automatically starts an AI-powered analysis to help you understand what went wrong. ```mermaid theme={null} flowchart LR A[Alert Triggers] --> B[AI Analysis Resource Created] B --> C[Analysis Job Scheduled] C --> D[Data Collection] D --> E[AI Analysis] E --> F[AI Incident Ready] ``` ### What Happens Behind the Scenes 1. **AI Analysis Resource** - When conditions are met, Ankra creates an AI Analysis resource to track the investigation 2. **Analysis Job** - A background job is scheduled to collect and analyze data 3. **Data Collection** - The job gathers: * Pod status and container states * Kubernetes events (warnings, errors) * Container logs (recent output) * Job results and error messages 4. **AI Analysis** - Ankra's AI processes the data to identify: * Root cause of the issue * Severity assessment * Affected resources * Recommended actions 5. **AI Incident** - Results are saved as an AI Incident for review > [!NOTE] > > View all AI Incidents in the **AI** → **Inbox**. Each incident includes the full analysis, affected resources, and an interactive checklist of recommended actions. *** ## Alerts Dashboard The Alerts page displays key metrics at a glance: | Metric | Description | | - | - | | **Total Alerts** | Number of configured alert rules | | **Active** | Alerts currently enabled and monitoring | | **Rules** | Total number of individual rules across all alerts | | **Triggers (24h)** | How many times alerts fired in the last 24 hours | You can filter alerts by status (Active/Disabled) using the quick filter pills on the dashboard. *** ## Alert Structure An alert consists of rules, conditions, and webhook integrations: ```mermaid theme={null} flowchart TD subgraph alert [Alert] A[Name / Description / Enabled] end subgraph rules [Rules] R1[Resource Type / Clusters / Severity / Cooldown] end subgraph conditions [Conditions] C1[Condition 1] C2[Condition 2] end subgraph webhooks [Webhooks] W1[Slack] W2[PagerDuty] end alert --> rules rules --> conditions C1 -->|AND / OR| C2 alert --> webhooks ``` *** ## Creating Alert Rules > [!TIP] > > **Ask Ankra AI instead.** In the chat, describe the alert you want - "page critical when any cluster goes offline", "warn when a production stack is down" - and the assistant proposes a **Create alert** action with every rule spelled out (severity, clusters, conditions, cooldown). It is created once you confirm. The assistant only writes conditions the evaluator implements, so an alert it creates always fires when its condition is met. It can also mute an alert (**Disable alert**) or remove one (**Delete alert**), and the same tools are on the [MCP server](/platform/mcp-tools) as `create_alert`, `set_alert_enabled` and `delete_alert`. **1. Name & Details** Configure the basic alert settings: * **Alert Name:** A descriptive name (e.g., "Production Resources Down") * **Severity:** Choose Critical, Warning, or Info * **Cooldown:** Time in minutes before the alert can trigger again (prevents alert storms) * **Clusters:** Select "All Clusters" or choose specific clusters to monitor **2. What to Monitor** Select the resource type to monitor: > [card] Cluster > > Monitor overall cluster health including: > > * Cluster connectivity state > * Agent status and availability > [card] Cluster Resource > > Monitor specific resource types: > > * **GitOps** - GitHub repository sync status > * **Addon** - Add-on deployment health > * **Manifest** - Raw manifest deployments > * **Stack** - Stack deployment status **3. When to Alert** Define one or more conditions that trigger the alert. Multiple conditions can be combined using **AND** or **OR** logic. **For Cluster monitoring:** * **Cluster State:** Alert when cluster goes offline/online **For Cluster Resource monitoring:** * **Resource State:** Alert on state changes (up, down, creating, updating, stopping) * **Job Status:** Alert on job outcomes (failed, timeout, blocked, etc.) **4. Notifications** Select which destinations (webhooks) should receive notifications when this alert fires. You can configure destinations in the [Webhooks](/guides/webhooks) section. In addition to the alert's own destinations, org-wide **routing rules** (Alerting → Destinations → Routing rules) can match alert firings by kind, severity, cluster, and source - for example, "every critical firing also goes to PagerDuty". A destination that the alert already notifies directly is skipped by routing rules. That is a guarantee, not a coincidence of timing: **a notification is delivered at most once per destination**, so you can safely keep a destination attached to an alert *and* pointed at by a routing rule. See [Notification routing](/guides/notification-routing) for how rules are ordered, what Exclude and Stop on match actually do, and how to preview which destinations a notification would reach. If you have set a [home channel](/guides/webhooks#home-channel), a critical or warning firing that no destination or routing rule handles falls back to the home so it is never missed. *** ## Condition Types Reference ### Cluster Conditions Use these when monitoring **Cluster** resource type: | Condition | Description | Values | | - | - | - | | **Cluster State** | Monitor cluster connectivity | `offline`, `online` | | **GitOps Sync** | Monitor the cluster's GitOps sync verdict | `failing`, `degraded`, `healthy` | | **Metric Threshold** | Compare a platform metric against a numeric threshold | `cpu_utilisation_pct`, `memory_utilisation_pct`, `disk_utilisation_pct`, `pod_restarts_15m`, `oom_kills_15m`, `nodes_not_ready`, `pods_pending` | | **Metrics Stopped Flowing** | Fire when the cluster has shipped no platform metrics for longer than the threshold (minutes) | `platform_metrics` | | **Security Findings** | Compare the Trivy vulnerability posture against a numeric threshold | `critical`, `high`, `severe`, `fixable_critical`, `fixable_high`, `new_severe_cves`, `new_kev_cves`, `kev`, `risk_score` | ### Cluster Resource Conditions Use these when monitoring **Cluster Resource** types (GitOps, Addon, Manifest, Stack): | Condition | Description | Values/Options | | - | - | - | | **Resource State** | Monitor resource health state | `all_up`, `up`, `creating`, `updating`, `stopping`, `down` | | **Job Status** | Monitor job execution status | `blocked`, `failed`, `pending`, `running`, `success`, `timeout`, `cancelling`, `cancelled` | ### Condition Operators For numeric threshold conditions, you can use these operators: | Operator | Description | | - | - | | `eq` | Equals | | `neq` | Not equals | | `gt` | Greater than | | `gte` | Greater than or equal | | `lt` | Less than | | `lte` | Less than or equal | *** ## Alert Severity Levels | Level | Description | Use Case | | - | - | - | | **Critical** | Immediate action required | Production outages, service unavailable | | **Warning** | Attention needed soon | Resource pressure, degraded performance | | **Info** | Informational updates | Successful deployments, routine events | *** ## Viewing Alert History Each alert tracks its trigger history. From the alert detail page, you can: * **View Statistics:** Total triggers, last check time, last triggered time * **Review History:** See when alerts triggered and what conditions matched * **Analyze Patterns:** Identify recurring issues across your infrastructure Navigate to **Alerts → \[Alert Name] → View History** to access the full timeline. *** ## Related * [Alert-triggered analysis](/platform/ai-insights#alert-triggered-analysis) - the AI analysis attached to every firing * [Webhooks](/guides/webhooks) - Configure notification endpoints for alerts *** Still have questions? [Join our Slack community](https://join.slack.com/t/ankra-community/shared_invite/zt-3a5rem8f8-cUho4epX2MoLT83bFf~VSA) and we'll help out. # Ankra CI starter pipelines Source: https://docs.ankra.ai/guides/ankra-ci-starter-pipelines Copy-paste .ankra/pipeline.yaml files for Node.js (npm, pnpm, Yarn), Python (pip, uv), Go and Rust, plus caches, a Postgres service, a version matrix, secrets, a nightly run and an image build that is scanned and gated before it is published. Each pipeline on this page is a complete `.ankra/pipeline.yaml`, or a stage you add to one. The test pipelines, the cache, the Postgres service, the matrix and the secret were each run on a real Ankra pipeline cluster before they were published, against a small project of each kind. New to Ankra CI? Start with [Get started with Ankra CI](/get-started/ankra-ci), which connects the repository and gets the first run green. ## Three rules every step follows A pipeline step is a container on your cluster, and three things about it differ from a CI runner or your laptop. Every recipe below already handles them, and anything you write yourself needs to as well: 1. **No network unless you ask.** A `run` stage has no network access by default. `defaults.network: "egress-https"` gives every stage HTTPS to the public internet, which is what installing packages needs. Private addresses stay out of reach unless an administrator allows them. 2. **A read-only root filesystem, and a non-root user.** A step cannot install system packages or write into the image, so `apt-get install` and `corepack enable` fail. Use an image that already has your tools in it. `HOME` is `/tmp`: writable, but 256 MiB and held in memory, so send package caches and build output to `/workspace` instead. 3. **One shared workspace.** `/workspace` holds the checkout and is shared by every stage of the run, so what one stage installs there, the next can use. `/tmp` is private to each step and starts empty. ## Test pipelines by toolchain All of these run on pushes to `main`, pull requests into `main` and manual runs. Change `branches` to match your default branch. > [tab] npm > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "node:24-alpine" > needs: ["checkout"] > env: > npm_config_cache: "/workspace/.ankra-node-cache/npm" > run: | > npm ci > npm test > ``` > > Needs a committed `package-lock.json`. > [tab] pnpm > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "node:24-alpine" > needs: ["checkout"] > env: > COREPACK_HOME: "/workspace/.ankra-node-cache/corepack" > COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" > run: | > corepack pnpm install --frozen-lockfile --store-dir /workspace/.ankra-node-cache/pnpm-store > corepack pnpm test > ``` > > Call pnpm as `corepack pnpm`. `corepack enable` tries to write into the image and fails on the read-only filesystem. Corepack installs the pnpm version your `package.json` pins in `packageManager`. > [tab] Yarn > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "node:24-alpine" > needs: ["checkout"] > env: > COREPACK_HOME: "/workspace/.ankra-node-cache/corepack" > COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" > YARN_CACHE_FOLDER: "/workspace/.ankra-node-cache/yarn" > YARN_GLOBAL_FOLDER: "/workspace/.ankra-node-cache/yarn-global" > run: | > case "$(corepack yarn --version)" in > 1.*) corepack yarn install --frozen-lockfile ;; > *) corepack yarn install --immutable ;; > esac > corepack yarn test > ``` > > The `case` picks the install flag your Yarn version accepts: Yarn 1 takes `--frozen-lockfile`, and Yarn 2 and later take `--immutable`. > [tab] Python (pip) > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "python:3.13-slim" > needs: ["checkout"] > env: > PIP_CACHE_DIR: "/workspace/.ankra-python-cache/pip" > run: | > python -m venv /workspace/.ankra-venv > . /workspace/.ankra-venv/bin/activate > pip install -r requirements.txt > pytest > ``` > > The virtual environment lives in the workspace because the image's own `site-packages` is read-only. `pytest` has to be in `requirements.txt`. > [tab] Python (uv) > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "ghcr.io/astral-sh/uv:python3.13-bookworm-slim" > needs: ["checkout"] > env: > UV_CACHE_DIR: "/workspace/.ankra-python-cache/uv" > UV_LINK_MODE: "copy" > run: | > uv sync --locked > uv run pytest > ``` > > Needs a committed `uv.lock`, with `pytest` in a dependency group `uv sync` installs, such as `dev`. > [tab] Go > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "golang:1.26" > needs: ["checkout"] > env: > GOPATH: "/workspace/.ankra-go" > GOCACHE: "/workspace/.ankra-go/cache" > GOMODCACHE: "/workspace/.ankra-go/mod" > GOTMPDIR: "/workspace/.ankra-go/tmp" > GOFLAGS: "-buildvcs=false" > run: | > mkdir -p "$GOTMPDIR" > go test ./... > ``` > > `-buildvcs=false` stops Go from asking git for version information. The checkout belongs to a different user than the step runs as, so git refuses to answer and the build would fail. > [tab] Rust > > ```yaml theme={null} > apiVersion: ankra.io/v1 > kind: Pipeline > metadata: > name: "my-repo" > on: > push: > branches: ["main"] > pull_request: > branches: ["main"] > manual: {} > defaults: > network: "egress-https" > stages: > - name: "checkout" > kind: "checkout" > - name: "test" > kind: "run" > image: "rust:1" > needs: ["checkout"] > env: > CARGO_HOME: "/workspace/.ankra-cargo/home" > CARGO_TARGET_DIR: "/workspace/.ankra-cargo/target" > run: | > cargo test --locked > ``` > > `--locked` needs a committed `Cargo.lock`. Drop it for a library that does not commit one. > [!TIP] > > Running a test suite in a different image? Use any image your organisation's image policy allows, and keep the three rules above: tools already in the image, caches and build output under `/workspace`, and `network: "egress-https"` when the step downloads anything. ## Make it faster with caches Without a cache, every run downloads its dependencies again. A `cache` block keeps directories of the workspace between runs, keyed on your lock file, so a run whose lock file did not change starts warm. Add it to the stage that installs, pointing at the same directories its `env` sends the package manager's cache to. For the npm pipeline above: ```yaml theme={null} - name: "test" kind: "run" image: "node:24-alpine" needs: ["checkout"] env: npm_config_cache: "/workspace/.ankra-node-cache/npm" cache: - key: "npm-${{ hashFiles('package-lock.json') }}" paths: [".ankra-node-cache/npm"] restore_keys: ["npm-"] fallback: "none" run: | npm ci npm test ``` The paths are relative to the workspace. The same pattern for the other toolchains: | Toolchain | `key` | `paths` | | - | - | - | | npm | `npm-${{ hashFiles('package-lock.json') }}` | `.ankra-node-cache/npm` | | pnpm | `pnpm-${{ hashFiles('pnpm-lock.yaml') }}` | `.ankra-node-cache/pnpm-store`, `.ankra-node-cache/corepack` | | Yarn | `yarn-${{ hashFiles('yarn.lock') }}` | `.ankra-node-cache/yarn`, `.ankra-node-cache/yarn-global`, `.ankra-node-cache/corepack` | | pip | `pip-${{ hashFiles('requirements.txt') }}` | `.ankra-python-cache/pip` | | uv | `uv-${{ hashFiles('uv.lock') }}` | `.ankra-python-cache/uv` | | Go | `go-${{ hashFiles('go.mod', 'go.sum') }}` | `.ankra-go/mod`, `.ankra-go/cache` | | Rust | `cargo-${{ hashFiles('Cargo.toml', 'Cargo.lock') }}` | `.ankra-cargo/home/registry`, `.ankra-cargo/home/git`, `.ankra-cargo/target` | Use the key's prefix as its `restore_keys` entry (`npm-`, `go-`), so a changed lock file still starts from the newest older cache. > [!NOTE] > > **Caches need approval.** A cache decides whose earlier output is copied into a run, so the `cache` block is a protected setting: it takes effect once an administrator approves the pipeline on the default branch. Until then the stage runs cold and the run says so. The step's **CACHE** column in `ankra pipeline get` shows `miss` on the first run and `hit` from then on. [Caches](/guides/pipeline-reference#caches) covers keys, scopes and retention. ## Give a step more memory or time Every step gets 500m of CPU, 1Gi of memory and 30 minutes unless the stage asks for more. A large JavaScript install, a TypeScript build or a big test suite often needs more memory - a step killed at its limit fails with the diagnosis `out_of_memory`. ```yaml theme={null} - name: "test" kind: "run" image: "node:24-alpine" needs: ["checkout"] resources: cpu: "2" memory: "4Gi" timeout: "45m" run: | npm ci npm test ``` `resources` and `timeout` are protected settings, so they apply once an administrator approves them. One step can ask for up to 8 CPU cores, 32Gi of memory and 6 hours. ## Test against a Postgres database A service is a sidecar container next to your test step, reachable by its name. This pipeline installs dependencies in one stage with internet access, then runs the tests in a second stage on the `services` network tier, which reaches the database and nothing else: ```yaml theme={null} apiVersion: ankra.io/v1 kind: Pipeline metadata: name: "my-repo" on: push: branches: ["main"] pull_request: branches: ["main"] manual: {} defaults: network: "egress-https" services: postgres: image: "postgres:17" env: PGDATA: "/var/lib/postgresql/data/pgdata" POSTGRES_USER: "app" POSTGRES_PASSWORD: "ci-only-password" POSTGRES_DB: "app_test" ports: ["5432"] ready: tcp: "5432" stages: - name: "checkout" kind: "checkout" - name: "install" kind: "run" image: "python:3.13-slim" needs: ["checkout"] env: PIP_CACHE_DIR: "/workspace/.ankra-python-cache/pip" cache: - key: "pip-${{ hashFiles('requirements.txt') }}" paths: [".ankra-python-cache/pip"] restore_keys: ["pip-"] fallback: "none" run: | python -m venv /workspace/.ankra-venv . /workspace/.ankra-venv/bin/activate pip install -r requirements.txt - name: "test" kind: "run" image: "python:3.13-slim" needs: ["install"] network: "services" services: ["postgres"] resources: cpu: "1" memory: "2Gi" env: DATABASE_URL: "postgres://app:ci-only-password@postgres:5432/app_test" run: | . /workspace/.ankra-venv/bin/activate pytest ``` Three details matter, and each one fails in a confusing way when it is missing: * **Use a Debian-based Postgres image** such as `postgres:17`, not an Alpine one. The sidecar also runs as a non-root user, and the Debian image's entrypoint is the one that can initialise a database that way. * **Set `PGDATA` to a subdirectory.** The image's own data directory belongs to another user, and Postgres cannot take it over, so the sidecar would restart in a loop and the step would never start. * **Install before you switch to `services`.** The `services` tier has no internet access, so a package install in the test stage fails. Install in an earlier stage on `egress-https` and reuse what it left in `/workspace`. The `ready` probe holds the test step until Postgres accepts connections. The password here is for this throwaway database only - use a pipeline secret (below) for anything real. `services`, the `services` network tier, `cache` and `resources` are protected, so approve the pipeline before you rely on them. ## Test against several versions A `matrix` runs one stage once per value, in parallel: ```yaml theme={null} - name: "test" kind: "run" image: "node:${{ matrix.node }}-alpine" needs: ["checkout"] matrix: node: ["20", "22", "24"] env: npm_config_cache: "/workspace/.ankra-node-cache/npm-${{ matrix.node }}" run: | node --version npm ci npm test ``` The run shows one step per value: `test:node=20`, `test:node=22`, `test:node=24`. Give each leg its own cache directory, as above, because the legs share the workspace and run at the same time. A matrix takes `include` and `exclude` lists as GitHub Actions does, up to 64 legs per stage. ## Use a secret in a step Store the value in Ankra once, as an organisation variable - **Variables and secrets** in the portal, or from the CLI, which reads the value from standard input so it stays out of your shell history: ```bash theme={null} ankra org variables set NPM_TOKEN - ``` Then declare it in the pipeline, and name it on the stage that needs it: ```yaml theme={null} secrets: - name: "npm_token" from: "org_variable" key: "NPM_TOKEN" stages: - name: "checkout" kind: "checkout" - name: "test" kind: "run" image: "node:24-alpine" needs: ["checkout"] secrets: ["npm_token"] run: | printf '//registry.npmjs.org/:_authToken=%s\n' "$(cat /run/agent-secrets/npm_token)" > "$HOME/.npmrc" npm ci npm test ``` A secret arrives as a **file**, `/run/agent-secrets/`, not as an environment variable, and never appears in the pipeline or a log. `from` is `org_variable` for an organisation or cluster variable, `app_env_secret` for the linked application's environment secret, or `registry` for a registry login. Only the stages that list a secret can read it, and pull requests from forks get none. `secrets` is a protected setting. Merge the declaration to the default branch and approve it before the first run that needs it - a pull request that adds a secret runs without it until then. ## Run every night Declare the schedule in the pipeline, then create it once: ```yaml theme={null} on: push: branches: ["main"] pull_request: branches: ["main"] schedule: - cron: "0 2 * * *" ``` ```bash theme={null} ankra pipeline schedules create --cron "0 2 * * *" --timezone Europe/Stockholm --repository ankra pipeline schedules list --repository ``` The cron has five numeric fields. It runs against the default branch unless the schedule names another `--ref`. To run only your slow suites at night, give the `schedule` entry a `stages` list, and include `checkout` and every stage those suites need. An application's **Pipelines** page in the portal has **Scheduled runs** as well, to create, pause or delete one. ## Build, scan and publish an image For a repository linked to an [Ankra application](/concepts/applications), the pipeline can build the container image, scan it and publish it to your organisation's registry. Nothing reaches the registry until the gate has passed the exact image that was scanned. Add these stages after your tests: ```yaml theme={null} - name: "build" kind: "build" needs: ["test"] build: dockerfile: "Dockerfile" provenance: true sbom: true - name: "scan" kind: "scan" needs: ["build"] scan: scanners: ["semgrep", "checkov", "trivy"] fail_on: semgrep: "error" - name: "gate" kind: "gate" needs: ["scan"] gate: require_stages: ["scan"] - name: "publish" kind: "publish" needs: ["gate"] when: events: ["push"] ``` What each stage does: * **`build`** builds the image rootless, inside your cluster, and stores it by digest in a staging area of your registry. A pull request's build is built and scanned but not published. * **`scan`** runs Semgrep and Checkov over your source and Trivy over the image. The reports go to the run and to the application's **Security** tab. * **`gate`** judges what the scanners found against your organisation's image policy - by default, fixable critical and high vulnerabilities in your own dependencies block. `fail_on` can make one pipeline stricter, never looser. * **`publish`** tags the exact digest the gate passed as `sha-` in your application's repository, only on pushes to the default branch. Your application deploys that tag. `build` and `scan` reach the internet by default, so they need no `network` line, and `gate` and `publish` run on the Ankra platform rather than in a pod. If your nodes stop the rootless builder from starting, Ankra builds the image on its own platform builders instead, and the scan and gate still run on your cluster. A new application's setup pull request commits these stages for you. ## A monorepo For a repository with several components, give each component its own test stage with a path filter for pull requests, so a pull request only tests what it touches and a push to `main` tests everything. [Monorepo: PR-only checks, full builds on push](/guides/ankra-pipelines#monorepo-pr-only-checks-full-builds-on-push) has the full pattern. ## Related * [Get started with Ankra CI](/get-started/ankra-ci) - connect a repository and get the first run green * [Troubleshooting](/guides/ankra-ci-troubleshooting) - what a failing first pipeline usually means * [Pipeline reference](/guides/pipeline-reference) - every key, trigger, stage kind and expression # Ankra CI troubleshooting Source: https://docs.ankra.ai/guides/ankra-ci-troubleshooting Fix what a new Ankra pipeline usually hits first: a push that started no run, a check waiting for approval, a step that waits, cannot reach the network, cannot write, runs out of memory or cannot find its secret. Find the symptom, then the fix. Most first-week problems are one of these, and the run usually names the cause itself: a failed or waiting step carries a **diagnosis** with what happened and what to do, shown above the steps on the run page and in `ankra pipeline get `. ```bash theme={null} ankra pipeline list --repository ankra pipeline get --repository ankra pipeline logs --repository --step ``` Inside a checkout of an [Ankra application](/concepts/applications)'s repository you can leave out `--repository`: the CLI finds the application from your `origin` remote. ## A push or pull request started no run Work down this list - each line is a reason Ankra records for an event that did not start a run. | Check | Fix | | - | - | | Is the repository connected? `ankra pipeline repositories list` | Connect it - [step 3 of Get started](/get-started/ankra-ci#3-connect-the-repository) | | Does it have a definition? `ankra pipeline definition get --repository ` | Store one: `ankra pipeline definition put .ankra/pipeline.yaml --repository `. Connecting only reads the file when it is already on the default branch. | | Is there a pipeline cluster? `ankra org ci-settings get` | `ankra org ci-settings set --cluster `. Until you do, the commit gets a failing check that asks you to choose one. | | Does `on:` match the event? | `on.push.branches` is the branch you pushed. `on.pull_request.branches` is the pull request's **base** branch, the one it merges into. | | Did a path filter exclude it? | A push that changed no file under `paths` starts nothing. The run list shows it as concluded `skipped` with the reason. | `ankra pipeline list` shows runs that were recorded as skipped, with the reason. ### No check appears on GitHub, but the run is there The Ankra GitHub App needs the **Checks: write** permission to post the `Ankra pipeline` check. Without it, Ankra reports the run in its status comment on the pull request instead, and the comment says which permission is missing. Grant it in the App's settings on GitHub. ## "No trigger this pipeline declares matches this event" `ankra pipeline run`, and the portal's **Run pipeline** button, answer this when the pipeline does not declare manual runs. Add it to `on:`: ```yaml theme={null} on: push: branches: ["main"] pull_request: branches: ["main"] manual: {} ``` ## The check says "Awaiting authority approval" The pipeline asks for a protected setting that no administrator has approved yet, and the check lists which. On a repository's first pipeline this is expected: the pull request trigger's `fork_policy: read_only` is one of them. An application created from the portal by a person with pipeline management access does not reach this state: Ankra approves the pipeline it generated in that person's name. It still appears for an application added with the CLI, an API token or the AI Assistant, for a repository connected on its own, and whenever a later change edits a protected setting. The steps still run, but the check stays in progress until an administrator approves. Approve once, in **Settings → Pipelines → Pending approvals**, with **Approve authority** on the check, or with the command the pull request comment prints: ```bash theme={null} ankra pipeline definitions approve ``` The check then settles. See [Approve the pipeline once](/get-started/ankra-ci#7-approve-the-pipeline-once). ## A setting in the file has no effect Secrets, credentials, caches, CPU and memory, a stage's timeout, services, a network tier above `egress-https` and step placement are **protected**. A run takes them only from the version of the pipeline on the default branch, and only once an administrator has approved it. Until then the run behaves as if they were not set - a cache does not restore, a memory limit stays at 1Gi, a secret's file is missing. `ankra pipeline validate` names every protected setting that will not apply yet, and prints the command to approve it: ```bash theme={null} ankra pipeline validate .ankra/pipeline.yaml --repository ``` Merge the change to the default branch, then approve it. The runs that waited re-run on their own. ## A step waits and does not start The run page and `ankra pipeline get` say why a step is waiting. The common reasons: | What it says | What to do | | - | - | | Waiting for a CI slot, or `waiting_on_ci_workers` | The cluster's agent has no free pipeline worker. On a new cluster it has none at all: `ankra cluster agent ci set --workers 2 --cluster `. Otherwise raise the count. | | `scale_up_in_progress` | A node is being added for the step. It usually joins in one to three minutes. | | `node_group_at_max` | Every node group that could run the step is at its maximum. Raise the maximum, or add another cluster to the CI pool. | | `no_node_group_fits`, `stage_exceeds_node_size` | No node is big enough for what the stage asks for. Lower its `resources`, or add a node group with bigger nodes. | | `provider_quota_exceeded` | Your cloud provider refused a new node for a quota. Raise the quota with the provider. | | `image_pull_auth`, `image_pull_failed` | The cluster cannot pull the step's image. Check the name and tag, and that the registry is reachable, or add the registry's credential to the organisation. | A step that never starts is eventually concluded with a message pointing at the cluster's agent and its worker count, so a run never stays open forever. [Diagnosis codes](/guides/ankra-pipelines#diagnosis-codes) lists every code. ## The step cannot reach the network A package install fails on a DNS lookup or a connection: ```text theme={null} npm error code EAI_AGAIN npm error request to https://registry.npmjs.org/... failed, reason: getaddrinfo EAI_AGAIN registry.npmjs.org ``` pip, Go, Cargo and curl report the same thing as `Temporary failure in name resolution`, `bad address` or `Could not resolve host`. A `run` stage has **no network by default**. Give it HTTPS to the public internet: ```yaml theme={null} defaults: network: "egress-https" ``` Or set `network: "egress-https"` on the one stage that needs it. Two more cases: * **A host on a private address**, such as an internal package mirror, stays out of reach on `egress-https`. An administrator can allow its range for every pipeline: `ankra org ci-settings set --egress-allowed-cidr 10.20.0.0/16`. * **A stage on `network: "services"`** reaches its sidecar services and nothing else. Install dependencies in an earlier stage on `egress-https`, and reuse them from `/workspace`. ## "Read-only file system" or "permission denied" A step runs as a non-root user (uid 65532) on a read-only root filesystem. It can write to three places: | Path | What it is | | - | - | | `/workspace` | The run's shared volume, on disk. The checkout lives here. Put caches, virtual environments and build output here. | | `/tmp` | Private to the step, 256 MiB, held in memory. `HOME` points here. | | `/dev/shm` | 64 MiB unless the stage sets `shm_size`. | The usual fixes: * **`corepack enable` fails with `EROFS`** - call the package manager through corepack instead: `corepack pnpm install`, `corepack yarn install`. * **`pip install` cannot write to `site-packages`** - create a virtual environment in the workspace first: `python -m venv /workspace/.ankra-venv`. * **`apt-get install`, `apk add` fail** - a step cannot install system packages. Use an image that already contains the tools, such as `mcr.microsoft.com/playwright` for browser tests. * **A compiler or test runner reports `No space left on device`** - it is filling `/tmp`. Point its temporary files into the workspace: `export TMPDIR=/workspace/.tmp && mkdir -p "$TMPDIR"` at the top of the `run` script. ## git: "detected dubious ownership in repository" The checkout belongs to a different user than the one later steps run as, so git refuses to work in it: ```text theme={null} fatal: detected dubious ownership in repository at '/workspace' ``` Trust the workspace on the command itself: ```bash theme={null} git -c safe.directory=/workspace rev-parse HEAD ``` Go builds hit the same thing when they stamp version information. Add `GOFLAGS: "-buildvcs=false"` to the stage's `env`, as the [Go starter](/guides/ankra-ci-starter-pipelines#test-pipelines-by-toolchain) does. The commit being built is in `$ANKRA_HEAD_SHA` if that is all you needed git for. ## The step was killed: `out_of_memory` The step used more memory than it asked for - 1Gi unless the stage says otherwise. Large JavaScript installs and TypeScript builds commonly need more. Raise it on the stage: ```yaml theme={null} resources: cpu: "2" memory: "4Gi" ``` `resources` is protected, so merge and approve it. `ankra pipeline get ` prints each step's memory peak against what it asked for, which tells you how much to ask for. ## The secret file is missing ```text theme={null} cat: can't open '/run/agent-secrets/npm_token': No such file or directory ``` Check, in order: 1. The pipeline declares it in the top-level `secrets` list, **and** the stage lists its name under `secrets`. 2. The declaration is on the default branch and approved. A pull request that adds a secret runs without it until then. 3. The run is not from a fork. Pull requests from forks get no secrets. 4. The value exists: `ankra org variables get ` for an organisation variable. ## The cache never restores The step's **CACHE** column in `ankra pipeline get` says what happened: * **Nothing shown, or the cache is ignored** - `cache` is protected. Merge and approve it. * **`miss` every time** - the key changes on every run, often because `hashFiles` matched nothing. Check the lock file name and that it is committed. * **`disabled`** - the cluster has no StorageClass to back the cache. Give the pipeline cluster a default StorageClass. Pull requests from forks and tag runs never share caches, by design. ## The build step fails before the Dockerfile is read `build_runtime_confined` means the cluster's nodes stop the rootless image builder from starting - common with AppArmor on Ubuntu or k3s nodes. No pipeline or cluster setting changes that. Ankra builds the image on its own platform builders instead, and the scan, gate and publish still run as usual. That is on by default: `ankra org ci-settings get` should show `Build fallback: platform_builders` and `Platform builds enabled: yes`. If either says otherwise, set `ankra org ci-settings set --build-fallback platform_builders`, or [contact support](/platform/support). ## The check says `action_required` The run failed because of Ankra or the cluster, not your code - an agent that went offline, a volume that would not attach, an image that could not be pulled. Ankra retries these once by itself. If the retry fails too, the check names the cause. Fix it if it is on your side, then press **Re-run** on the check, or: ```bash theme={null} ankra pipeline rerun --repository --failed-only ``` ## Still stuck Ask Ankra's AI in the portal chat - "why did run #12 of my-repo fail?" - and it reads the run, the step logs and the cluster's events. Or [contact support](/platform/support) with the run's link from the check. ## Related * [Get started with Ankra CI](/get-started/ankra-ci) * [Starter pipelines](/guides/ankra-ci-starter-pipelines) * [When something fails](/guides/ankra-pipelines#when-something-fails) - every error class and how retries work # Ankra Cloud Clusters Source: https://docs.ankra.ai/guides/ankra-cloud-clusters Build self-managed kubeadm or k3s clusters on Ankra Cloud servers with Ankra - a private network behind a NAT router, a bastion with public IPv4, IPv6-first nodes, and the Ankra Cloud CCM and CSI driver wired in. Ankra builds **Ankra Managed** Kubernetes clusters on [Ankra Cloud](https://cloud.ankra.app) servers: a private network with a NAT router, a bastion server, control plane and worker servers on the private network, kubeadm or k3s installed over SSH, and the Ankra Cloud cloud controller manager and CSI driver deployed as a stack. You own the control plane and every server; Ankra provisions, upgrades and repairs them. > [!WARNING] > > **Closed beta.** Ankra Cloud as a cluster provider is in closed beta. The workflow is stable but the surface may still change, and it is enabled per organisation on request - until it is, Ankra Cloud does not appear in the create-cluster dialog or among the credential providers, and its endpoints are not served. [Contact support](/platform/support) to have it turned on for your organisation. This is the self-managed lane. If you want the Ankra team to run the control plane for you, use [Ankra Cloud Kubernetes](/guides/ankra-cloud-kubernetes) instead; the [comparison table](/guides/managed-kubernetes#cloud-managed-or-ankra-managed) sets the two side by side. *** ## Before you start > [card] Ankra Cloud Credential > > An Ankra Cloud API token (`act_...`) from a user allowed to create servers, networks and routers. The same credential serves both self-managed clusters and Ankra Cloud Kubernetes. See [Ankra Cloud Credentials](/platform/credentials/ankra-cloud). > [card] SSH Key Credential > > An SSH public key for server access. You can provide your own or let Ankra generate one. See [SSH Key Credentials](/platform/credentials/ssh-key). *** ## Create the cluster ### Dashboard **1. Open the create dialog** Go to **Clusters**, click **Create cluster** and pick **Ankra Cloud** under **Ankra Managed** (kubeadm or k3s on Ankra Cloud servers). **2. Credentials & Zone** Pick the Ankra Cloud credential, or add one inline with **Add Ankra Cloud credential**. Choose the **Runtime credential** the in-cluster cloud controller and CSI driver use: the default reuses the provisioning credential, and a separate, dedicated credential limits what those components can reach. Pick an SSH key, then the zone. Zones, plans and templates load live from your Ankra Cloud account; a zone that runs no servers (a network-only location such as Stockholm) is refused. **3. Private Network & Bastion** Choose **Create a dedicated network** (Ankra creates and owns the network and its router; the wizard proposes `10.64.0.0/20`) or **Use an existing network** in the zone. Pick the bastion plan, and optionally restrict **Bastion allowed IPs** to the CIDRs that may reach the bastion over SSH; empty allows any address. **Storage retention on teardown** decides whether persistent volumes are kept (the default) or deleted when the cluster is deprovisioned. **4. Compute** Set the control-plane count (the wizard offers 1, 3 or 5; the API accepts any count from 1 to 9) and plan, then one or more worker node groups, each with its own plan, count, labels and taints. The cost summary prices every server from the live plan catalog, plus public IPv4. The router, storage, load balancers and transfer are not in the total; see [Cost](#cost). **5. Kubernetes** Choose the server template (default `debian-13`), the distribution and the Kubernetes version: * **kubeadm** (default) - vanilla Kubernetes with containerd and Cilium. Cilium is the only CNI kubeadm accepts on Ankra Cloud. etcd runs stacked on the control-plane nodes, or on 3 or 5 dedicated external etcd servers with their own plan. * **k3s** - a single binary with batteries included, with a choice of CNI. **6. GitOps** Optionally connect a Git repository, and Ankra commits the cluster's stacks to it. Two checkboxes are on by default: * **Include Networking Stack** - Traefik, cert-manager and a Let's Encrypt ClusterIssuer, with Traefik behind an Ankra Cloud load balancer. * **Include DNS Integration** - external-dns, so Ingress hostnames publish their DNS records automatically. Skipping GitOps still deploys the cloud controller manager and CSI driver; the `ankracloud-cloud-provider` stack is then not committed to Git. **7. Preflight & Review** Name the cluster and click **Run preflight**. The checks run against your account: the credential authenticates, the zone offers compute, every plan and the template exist, an existing network lives in the chosen zone, and kubeadm is paired with Cilium. Any failing check blocks creation and says why. Then click **Create cluster**; a progress view tracks the network, router, firewall, bastion, servers, Kubernetes installation and Ankra Agent. ### API ```bash theme={null} curl -X POST https://platform.ankra.app/api/v1/clusters/ankracloud \ -H "Authorization: Bearer $ANKRA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "my-cluster", "credential_id": "", "ssh_key_credential_id": "", "zone": "de-fsn1", "bastion_plan": "", "control_plane_count": 1, "control_plane_plan": "", "worker_count": 2, "worker_plan": "", "distribution": "kubeadm" }' ``` Plans are account-wide; list them, and the zones, templates and networks the wizard offers, with `GET /api/v1/clusters/ankracloud/plans?credential_id=` (and `/zones`, `/templates`, `/networks?zone=` alongside). `POST /api/v1/clusters/ankracloud/preflight` takes the same body and answers the checks without creating anything. | Field | Default | Description | | - | - | - | | `zone` | | Ankra Cloud zone the servers are built in, for example `de-fsn1` | | `template` | `debian-13` | Public server template: `debian-13`, `debian-12` or `ubuntu-24.04` | | `distribution` | `kubeadm` | `kubeadm` or `k3s` | | `kubernetes_version` | latest stable | Kubernetes version for the chosen distribution | | `cni` | | CNI for k3s; kubeadm accepts only Cilium | | `etcd_topology` | `stacked` | kubeadm only: `stacked` or `external`, with `etcd_node_count` (3 or 5) and `etcd_plan` | | `network_ip_range` | a derived `/20` | RFC 1918 range, `/16` to `/29`, for the network Ankra creates | | `private_network_id` | | Adopt an existing network in the zone instead; mutually exclusive with `network_ip_range` | | `bastion_plan` | | Plan of the bastion server | | `bastion_allowed_ips` | any | IPv4 or IPv6 addresses or CIDRs allowed to reach the bastion over SSH | | `control_plane_count` | | 1 to 9 control-plane servers. The wizard offers 1, 3 and 5 | | `control_plane_plan` | | Plan of the control-plane servers | | `worker_count`, `worker_plan` | | A single worker group; use `node_groups` (each with `name`, `instance_type`, `count`, labels and taints) for several | | `runtime_credential_id` | the provisioning credential | Ankra Cloud credential the in-cluster CCM and CSI use | | `retention_policy` | `retain` | `retain` or `delete` persistent volumes on deprovision | | `include_networking`, `include_dns` | `true` | Networking stack and DNS integration | | `gitops_credential_name`, `gitops_repository`, `gitops_branch` | branch `master` | Optional GitOps repository | A cluster has at most 100 workers across its node groups, and at most 64 nodes in all, control planes included, because of the pod range it is given (see [Network ranges and their limits](/platform/cluster-mesh#network-ranges-and-their-limits)). A request that plans more, counting each node group's autoscaling maximum, is refused before anything is provisioned. *** ## Verify 1. The cluster moves from **Provisioning** to **Online** in the clusters list once the Ankra Agent has connected. 2. Point `kubectl` at it through Ankra (this needs a [Cluster Access](/guides/cluster-access) grant) and check the nodes: ```bash theme={null} ankra cluster kubeconfig add my-cluster --use kubectl get nodes ``` Every control plane and worker should be `Ready`. See [Accessing Clusters with kubectl](/guides/kubeconfig). *** ## Network design ```mermaid theme={null} flowchart TB Internet((Internet)) Platform[Ankra Platform] subgraph Network [Ankra Cloud private network] Bastion[Bastion
public IPv4 + IPv6] CP[Control plane servers
private IPv4 + IPv6] Workers[Worker servers
private IPv4 + IPv6] LB[Ankra Cloud load balancers
created by the CCM] end Router[Router with NAT] Platform -->|SSH as root, port 22| Bastion Bastion -->|SSH jump| CP Bastion -->|SSH jump| Workers CP -->|IPv4 egress| Router Workers -->|IPv4 egress| Router Router --> Internet Internet --> LB LB --> Workers ``` | Component | What Ankra creates | | - | - | | **Private network** | An RFC 1918 network, named after the cluster, that every server joins. Kubernetes runs on it: each kubelet's node IP is its private IPv4 address. An adopted network is used as it is and left in place on deprovision. | | **Router with NAT** | A router attached to the private network with NAT enabled. It is the nodes' IPv4 path to the internet, and only that - it is never an SSH hop. | | **Bastion** | A plain server with a public IPv4 address. Ankra connects to it as `root` on port 22 and jumps through it to every other server to install Kubernetes, run upgrades and reconcile. | | **Control plane and workers** | Servers on the private network. They keep the public IPv6 address every Ankra Cloud server gets, behind the firewall, and have no public IPv4 address. The Kubernetes API is not published: Ankra reaches it through the bastion, and the Ankra Agent in the cluster connects out to Ankra. | | **Firewall** | Ankra Cloud has no security groups, so Ankra applies a firewall to each server: inbound traffic is dropped by default, the private network is trusted as a whole, and the bastion accepts SSH from `bastion_allowed_ips` (or anywhere when it is empty). | Ankra Cloud servers are IPv6-first. Nodes do not rely on the per-server IPv4 translation (CLAT) Ankra Cloud offers IPv6-only servers; pod and node IPv4 traffic leaves through the router's NAT instead. Every server Ankra creates carries the labels `platform.ankra.io/managed-by=ankra-platform`, `platform.ankra.io/cluster-id=` and `platform.ankra.io/role`, so you can find a cluster's servers in the Ankra Cloud console. ### Cost Ankra Cloud bills the cluster's resources to your Ankra Cloud account. A cluster is made of: * **Servers** - the bastion, the control planes and the workers, each at its plan price. * **Public IPv4** - one address, held by the bastion. The nodes hold none, and IPv6 is not billed. * **The router with NAT** - one for each cluster, billed monthly while NAT is enabled. * **Load balancers** - the ones the cloud controller creates for `LoadBalancer` Services, the networking stack's Traefik included. * **Persistent volumes** - the storages the CSI driver provisions, by tier and size. * **Transfer** - outbound traffic beyond what each server includes. The wizard's estimate covers the servers and the public IPv4 address only. For the current price of every line, see [Billing](https://cloud.ankra.app/docs/billing) in the Ankra Cloud documentation. *** ## Cloud controller and storage After the Ankra Agent is installed, Ankra deploys the `ankracloud-cloud-provider` stack into `kube-system`: the `ankra-cloud-ccm` and `ankra-cloud-csi` charts, sharing the runtime credential's token through the Secret `ankra-cloud` (SOPS-encrypted when the stack is committed to Git). Every kubelet runs with `--cloud-provider=external`, and the controller links each node to its server as `ankracloud:///`. The charts' own behaviour, values and annotations are documented by Ankra Cloud: [Kubernetes cloud controller manager](https://cloud.ankra.app/docs/kubernetes-ccm) and [Kubernetes CSI driver](https://cloud.ankra.app/docs/kubernetes-csi). **LoadBalancer Services.** `ankra-cloud-ccm` gives each Service of type `LoadBalancer` an Ankra Cloud load balancer on the cluster's private network, with the nodes' private addresses as members. The networking stack's Traefik uses one, with `externalTrafficPolicy: Local` so the client address is preserved. Deleting the Service deletes its load balancer; Ankra itself never deletes one, so remove your `LoadBalancer` Services before you [terminate the cluster](#operate-it), or a load balancer still bound then stays in your Ankra Cloud account. **Persistent volumes.** `ankra-cloud-csi` provisions Ankra Cloud storages. It installs four StorageClasses: | StorageClass | Tier | Use | | - | - | - | | `ankra-standard` (default) | `standard` | General purpose, replicated | | `ankra-maxiops` | `maxiops` | Databases and other latency-sensitive workloads | | `ankra-hdd` | `hdd` | Bulk data | | `ankra-local-nvme` | `local-nvme` | The compute node's own disks: fastest, single copy, pinned to one server | Volumes are ReadWriteOnce and created in the zone of the node their pod is scheduled on. *** ## Operate it Day-2 tasks work as on every Ankra Managed provider, from the dashboard or the API - the released CLI has no Ankra Cloud commands yet: * [Node groups](/guides/operate-ankra-managed-clusters#node-groups) - **Nodes → Node groups** * [Control plane](/guides/operate-ankra-managed-clusters#control-plane), [restart a node](/guides/operate-ankra-managed-clusters#restart-a-node) and [the bastion](/guides/operate-ankra-managed-clusters#bastion) * [Upgrade Kubernetes](/guides/operate-ankra-managed-clusters#upgrade-kubernetes) * [Stop and start](/guides/operate-ankra-managed-clusters#stop-and-start) - a stop powers the servers off and keeps them with their disks; a start powers them on again * [Terminate a cluster](/guides/operate-ankra-managed-clusters#terminate-a-cluster) - deletes the servers, the bastion, the router, the firewall rules and the network Ankra created. A network you adopted with `private_network_id` is left in place, and persistent volumes follow the retention policy, deleted only once you accept it ## Troubleshooting | Issue | Solution | | - | - | | Preflight: `zone "..." is a network-only location and runs no servers` | Pick a zone that offers compute, for example `de-fsn1` | | Preflight: `plan "..." is not available to this account` | Pick a plan from the live catalog in the wizard | | Preflight: `template "..." is not a public Ankra Cloud template` | Use `debian-13`, `debian-12` or `ubuntu-24.04`; custom templates are not supported | | Preflight: `kubeadm supports only Cilium on Ankra Cloud` | Choose Cilium, or switch to k3s for another CNI | | Preflight: `network ... lives in zone ..., not ...` | Adopt a network in the cluster's zone, or let Ankra create one | | Create fails with a quota error | Ankra Cloud enforces account quotas when servers are created; raise them in the Ankra Cloud console or pick smaller plans | | Credential refused | Check the token in the Ankra Cloud console and [rotate it](/platform/credentials/ankra-cloud) | ## Related * [Ankra Cloud Kubernetes](/guides/ankra-cloud-kubernetes) * [Ankra Cloud Credentials](/platform/credentials/ankra-cloud) * [Cluster settings](/platform/cluster-settings) # Ankra Cloud Kubernetes Source: https://docs.ankra.ai/guides/ankra-cloud-kubernetes Provision, import, and operate Ankra Cloud Kubernetes clusters with Ankra - a control plane the Ankra team runs in Ankra Cloud, node pools with autoscaling, upgrades, GitOps, and AI-assisted operations. Ankra Cloud Kubernetes is the managed Kubernetes service of [Ankra Cloud](https://cloud.ankra.app). The Ankra team runs the control plane inside Ankra Cloud; Ankra creates the cluster through the Ankra Cloud API, installs the Ankra Agent, and manages everything on top - node pools, Kubernetes upgrades, add-ons, stacks, GitOps, and AI-assisted operations. > [!WARNING] > > **Closed beta.** Ankra Cloud as a cluster provider is in closed beta. The workflow is stable but the surface may still change, and it is enabled per organisation on request - until it is, Ankra Cloud Kubernetes does not appear in the create-cluster dialog or among the import providers. [Contact support](/platform/support) to have it turned on for your organisation. This page covers Ankra Cloud Kubernetes specifically. For the shared concepts across all managed providers - live options, discovery and import, and day-2 API patterns - see [Managed Kubernetes](/guides/managed-kubernetes). To run kubeadm or k3s yourself on Ankra Cloud servers instead, see [Ankra Cloud Clusters](/guides/ankra-cloud-clusters). ## Prerequisites An **Ankra Cloud credential** stored in Ankra - the same credential the self-managed clusters use. See [Ankra Cloud Credentials](/platform/credentials/ankra-cloud). No SSH key is needed: there is no bastion and no server access. GitOps is optional: connect a repository at creation and Ankra commits the cluster's stack definitions to Git. ## Choosing a control plane A cluster comes in one of three shapes. You choose when you create it, and the choice cannot be changed afterwards. | | Production control plane | Development control plane | Dev cluster | | - | - | - | - | | Control plane | Three replicas, run by the Ankra team | One replica, run by the Ankra team | k3s on the cluster's one server | | Highly available | Yes | No | No | | Control-plane fee | €40 a month per cluster | None | None | | Nodes | Node pools, billed as servers | Node pools, billed as servers | One server, billed as a server | | Autoscaling | Yes | Yes | No | | Use it for | Anything that must stay up | Development, test and preview environments | A small cluster to try something on | The **production** control plane is the default. Its three replicas keep the Kubernetes API available through an upgrade or the loss of a host node. The **development** control plane has no fee of its own: you pay for the nodes only. It is not for production. With one replica, an upgrade or a host node failure interrupts the Kubernetes API while the control plane restarts, and you cannot deploy or change anything until it is back. A **dev cluster** is a different shape, described in [Dev clusters](#dev-clusters). ## Creating a cluster > [tab] Portal > > **1. Create Cluster** > > Go to **Clusters** → **Create Cluster** and pick **Ankra Cloud Kubernetes** under **Cloud Managed**. > > **2. Credential & Zone** > > Select the Ankra Cloud credential, or add one inline, and a zone. Zones, versions and plans load live from Ankra Cloud. > > **3. Node Pools** > > Define one or more node pools: name, plan (with live pricing), count, labels and taints, and optionally autoscaling bounds. > > **4. Kubernetes** > > Pick a Kubernetes version or keep Ankra Cloud's default, and choose the control plane: production or development. Production is selected to begin with. The cost summary's control-plane row shows the fee of the one you chose, which is none for development. > > **5. GitOps (optional) & Create** > > Optionally connect a Git repository, then create. Ankra runs the preflight checks, creates the cluster, fetches a kubeconfig, and installs the Ankra Agent. > [tab] API > > ```bash theme={null} > curl -X POST https://platform.ankra.app/api/v1/clusters/managed/ankracloud_k8s \ > -H "Authorization: Bearer $ANKRA_API_TOKEN" \ > -H "Content-Type: application/json" \ > -d '{ > "name": "my-cluster", > "credential_id": "", > "location": "de-fsn1", > "node_pools": [ > {"name": "workers", "size": "", "count": 2, > "autoscaling": {"enabled": true, "min_count": 2, "max_count": 5}} > ] > }' > ``` > > `location` is the Ankra Cloud zone and `size` is the plan name. This request creates a production control plane. For a development one, add `"ankracloud_k8s": {"control_plane": "development"}` to the body. > > `GET /api/v1/clusters/managed/ankracloud_k8s/options?credential_id=` lists the zones, versions and plans, and the control planes on offer in `control_planes`, each with its replica count and monthly fee. > [tab] CLI > > ```bash theme={null} > ankra cluster managed create \ > --provider ankracloud_k8s \ > --name my-cluster \ > --credential-id \ > --location de-fsn1 \ > --node-pool-name workers \ > --node-pool-size \ > --node-pool-count 2 > ``` > > This creates a production control plane. `--control-plane development` asks for a development one: > > ```bash theme={null} > ankra cluster managed create \ > --provider ankracloud_k8s \ > --name my-test-cluster \ > --credential-id \ > --location de-fsn1 \ > --node-pool-size \ > --node-pool-count 2 \ > --control-plane development > ``` ### Ankra Cloud Kubernetes options Every option is optional and goes in an `ankracloud_k8s` block on the create request: | Field | Default | Description | | - | - | - | | `private_network_id` | | An existing Ankra Cloud private network the nodes join | | `network_cidr` | `10.0.0.0/24` | The RFC 1918 range of the network Ankra creates when `private_network_id` is not set. It must not overlap the Ankra Cloud public range `10.100.0.0/20`, and a range that does is refused | | `public_ipv4` | `true` | Give the cluster's API endpoint a public IPv4 address, at €4 a month. The endpoint is IPv6 otherwise, and Ankra installs the agent over IPv4 | | `control_plane` | `production` | `production` or `development`. See [Choosing a control plane](#choosing-a-control-plane). Any other value is refused, and so is `control_plane` together with `dev_cluster` | | `dev_cluster` | `false` | Create a [dev cluster](#dev-clusters) on one server instead of a cluster with a managed control plane | Without `private_network_id`, Ankra creates a network named `ankra-` and deletes it again when the cluster is deleted. A network you named is never deleted. ### Limits | Limit | Value | | - | - | | Clusters per Ankra Cloud account | 5 | | Cluster name | 2 to 40 lowercase letters, digits or hyphens, starting with a letter | | Node pools at creation | 8 | | Node pool name | 2 to 32 lowercase letters, digits or hyphens, starting with a letter | | Nodes per pool | 0 to 100 | Preflight checks the name, zone, version and every pool's plan, and counts the account's live clusters, so a sixth cluster is refused before anything is created. The control plane is chosen when the cluster is created and cannot be changed afterwards. To move from development to production, or back, create a new cluster and redeploy your workloads to it. ## Dev clusters A dev cluster is one Ankra Cloud server running k3s. That server is the control plane, the only node and the cluster's own network edge, so there is no managed control plane and no control-plane fee: you pay for the one server at its plan price. It cannot autoscale and it joins no private network. Use it to try something out, not to run anything that has to stay up. > [!WARNING] > > Ankra Cloud says this about every dev cluster, and the options call returns it as `production_warning`: "A dev cluster is one server: it is the Kubernetes API, the data store, every workload and the network edge. It has no high availability, no upgrades in place and no snapshots. Run production on a managed Kubernetes cluster." Dev clusters are offered zone by zone. The `dev_cluster` block of the options call says where: | Field | Meaning | | - | - | | `is_available` | Whether any zone offers dev clusters to your organisation | | `zones` | The zones that offer one. `all_zones` is `true` when every zone does | | `minimum_memory_mebibytes` | The memory of the smallest plan a dev cluster runs on | | `ipv4_price_monthly` | The monthly price of a public IPv4 address for the server | | `production_warning` | The warning above | Create one from **Create Cluster** with the **Ankra Cloud** card labelled **Dev cluster on one server**, under **Cloud Managed**. The wizard limits the zones to the ones that offer dev clusters, asks for one server plan in place of node pools, and uses the latest stable Kubernetes release Ankra Cloud offers for dev clusters. Its cost summary prices the server and the public IPv4 address from the same options. Where no zone offers dev clusters, the wizard says they are not available for your organisation yet and creates a managed Kubernetes cluster instead. From the CLI: ```bash theme={null} ankra cluster managed create \ --provider ankracloud_k8s \ --name my-dev-cluster \ --location \ --node-pool-size \ --dev-cluster ``` `--location` is a zone from `dev_cluster.zones` and `--node-pool-size` is the plan of the one server. `--dev-cluster` is refused together with `--autoscaling`, `--network-cidr`, `--private-network-id`, a `--node-pool-count` other than 1, and `--control-plane`. On the API, set `ankracloud_k8s.dev_cluster` to `true` and send exactly one node pool with a `count` of 1, whose size is the server's plan. The request is refused when that pool enables autoscaling, when `cni` is set, and when `ankracloud_k8s.network_cidr` or `ankracloud_k8s.control_plane` is set. ## How Ankra connects Ankra Cloud hands out a kubeconfig whose token expires eight hours after it is issued. Ankra never stores it: it fetches a fresh one each time it needs the Kubernetes API directly, installs the Ankra Agent with it at creation, and from then on works through the agent. For your own access, use [Ankra's kubeconfig](/guides/kubeconfig) or download one from the Ankra Cloud console. Ankra labels the cluster in Ankra Cloud with `platform.ankra.io/managed-by=ankra-platform` and `platform.ankra.io/cluster-id=`, so Ankra Cloud knows the platform already manages it. Ankra Cloud installs its own cloud controller manager and CSI driver in the cluster, so `LoadBalancer` Services get Ankra Cloud load balancers and persistent volumes use Ankra Cloud storage, with the same StorageClasses as a [self-managed cluster](/guides/ankra-cloud-clusters#cloud-controller-and-storage). Ankra does not add a cloud-provider stack of its own. ## Verify The create progress view follows the control plane, the node pools, kubeconfig retrieval and the Ankra Agent install. The cluster shows **Online** once the agent has connected. Then point `kubectl` at it through Ankra - this needs a [Cluster Access](/guides/cluster-access) grant - and check that every node is `Ready`: ```bash theme={null} ankra cluster kubeconfig add --use kubectl get nodes ``` See [Accessing Clusters with kubectl](/guides/kubeconfig). ## Day-2 operations * **Node pools** - add, scale and delete pools from cluster settings or the API. Labels and taints are set when a pool is created. * **Autoscaling** - set `min_count` and `max_count` on a pool and Ankra Cloud scales it between them; clear autoscaling to return to a fixed count. See [Managed Kubernetes - day-2 operations](/guides/managed-kubernetes#day-2-operations). * **Upgrades** - Ankra Cloud upgrades one minor version at a time; cluster settings offer the next version when one is available. * **Deletion** - deleting the cluster waits for Ankra Cloud to release its nodes, volumes and load balancers, then removes the network Ankra created for it. ## Cost What a cluster costs depends on its control plane: * **Production control plane**: €40 a month per cluster, for three replicas that keep the Kubernetes API highly available. * **Development control plane**: no control-plane fee. You pay for the nodes only. It runs one replica and is not for production, because an upgrade or a host node failure interrupts the Kubernetes API while it restarts. * **Dev cluster**: no control-plane fee. You pay for its one server. Nodes are billed by Ankra Cloud as servers, at their plan price, whichever control plane you choose. A public IPv4 address on the API endpoint costs €4 a month on top, on both control planes. The wizard reads the fees live from Ankra Cloud and includes them in the cost summary. ## Importing an existing cluster Already running Ankra Cloud Kubernetes? Discover the clusters in your Ankra Cloud account and adopt them into Ankra without touching them. See [Managed Kubernetes - importing existing clusters](/guides/managed-kubernetes#importing-existing-clusters). ## Related * [Managed Kubernetes overview](/guides/managed-kubernetes) * [Ankra Cloud Clusters](/guides/ankra-cloud-clusters) * [Ankra Cloud Credentials](/platform/credentials/ankra-cloud) # Ankra Pipelines Source: https://docs.ankra.ai/guides/ankra-pipelines Connect a repository, push a commit, and watch it get built, scanned, gated and published by digest inside your own cluster - triggers, a hardened Job per stage, check runs, artifacts, and what a pull request can and cannot change. > [!NOTE] > > Ankra Pipelines runs a repository's CI as Kubernetes Jobs inside a cluster you already operate through Ankra. One file, `.ankra/pipeline.yaml`, declares the triggers and the stages; a push, a pull request or a tag starts a run, every in-cluster stage runs as a hardened Job in the `ankra-ci` namespace of your CI cluster, and the result lands as an `Ankra pipeline` check on the commit and a run you can follow with `ankra pipeline logs --follow`. There are no GitHub-hosted or self-hosted runners to keep alive. New to Ankra CI? [Get started with Ankra CI](/get-started/ankra-ci) is the short tutorial, [Starter pipelines](/guides/ankra-ci-starter-pipelines) has copy-paste pipelines per language, and the [Ankra Pipelines overview](/pipelines/overview) explains the system in diagrams: [how it works](/pipelines/how-it-works), [fast CI](/pipelines/fast-ci), [caching](/pipelines/caching), the [merge train](/pipelines/merge-train) and [AI in pull requests](/pipelines/ai-in-pull-requests). This page gets a repository connected, its first commit through a run, and the result on your pull request - then says exactly what is and is not in place yet, so nothing here claims more than the platform does today. ## Before your first run The first four are preconditions, not preferences, and the failure modes differ: three of them stop a run before anything is dispatched, and one lets it run and then quietly strip every protected section out of it. The two vault entries at the end are not preconditions at all - they are where a run's logs and reports go if you do nothing, and why you might want somewhere else. * **A connected repository.** The repository is connected through the [Ankra GitHub App](/integrations/github), GitLab, or [Bitbucket Cloud](/integrations/bitbucket-cloud), so Ankra receives its webhooks - and then connected to Ankra Pipelines itself, which is self-service: automatically when it backs an Ankra Application, or with one call for a bare repository (below). Pipelines are repository-scoped with an optional link to an [Application](/concepts/applications); a repository with nothing to build can have a pipeline too. A Bitbucket Cloud repository gets its webhook when you connect it to Ankra Pipelines with a Bitbucket credential, and its pull requests start runs only once that credential grants Bitbucket's `pullrequest` scope - see [Granting the pull request permission](/integrations/bitbucket-cloud#granting-the-pull-request-permission). * **A CI cluster.** An organisation administrator sets `ci_cluster_id` in the organisation's CI settings. Without one, the staging cluster the organisation nominated for AI work is used; with neither, a provider event records `no_ci_cluster` and nothing runs. * **An agent that advertises pipeline steps.** The CI cluster's agent runs pipeline steps in a third scheduler that is off by default - `ci_worker_count` (`AGENT_CI_WORKER_COUNT`) is `0` on every cluster until someone raises it. An agent with the scheduler off does not advertise the pipeline job names at all, so the platform refuses a step for that cluster up front instead of dispatching it. Set the count through Ankra, per cluster, and Ankra re-renders that agent's own release; never `helm upgrade` the agent by hand (below). * **An approved definition.** A pipeline's protected sections - its `secrets`, `credentials`, `permissions`, network tier, placement, resources, timeout, caches, `services` and a `gate` stage's `with:` - carry no authority until an organisation administrator approves the definition on the default branch. Until one does, a run still executes, with **none** of them: the check run and the pull request comment carry "No administrator has approved any authority for this repository yet, so this run executes with none." Approve before the first run that needs any of it (below). * **For check runs, `checks:write`.** Without it the GitHub App cannot create a check run, and the run's status is carried by a comment on the pull request instead - the comment says so and names the permission. * **Somewhere to keep the objects - and you already have it.** A `scan` stage's reports, the findings a `gate` reads, the complete step log and every declared artifact are objects, and they need a bucket. An organisation with no ready [backup vault](/concepts/backup-vaults) of its own is lent one: Ankra keeps them in a vault the platform owns, under a key prefix of your organisation's own, with the same retention, the same per-object download links and the same reaper. Nothing to provision before a first run. * **A vault of your own, when where the objects live matters.** Register one for data residency, for a bucket only your own credentials reach, or simply because cluster backups, restore points and `ankra migrate` data need one - those never use the platform's vault. A ready vault of your own takes precedence automatically, from the next step onward (below). ## Connect a repository, push, see the run **1. Connect the repository** Two paths, both self-service: * **An Ankra Application.** Creating or reconciling an Application connects its repository and decides its pipeline automatically. For a genuinely new application, the setup pull request now commits `.ankra/pipeline.yaml` instead of a generated GitHub Actions workflow - unless the repository already carries an Ankra-authored workflow (which keeps healing rather than switching underneath you) or you asked for `github_actions` explicitly. A monorepo gets one file with each component's stages suffixed by its name, not one workflow per app. Ankra AI's proposal tool on the [MCP server](/platform/mcp-server) takes the same choice as `target: "ankra_pipeline"` or `"github_actions"`. * **A bare repository** - nothing to build, or you'd rather write the file yourself: ```bash cURL theme={null} curl -X POST https://platform.ankra.app/api/v1/org/pipelines/repositories \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "provider": "github", "owner": "my-org", "name": "my-repo", "default_branch": "main" }' ``` Needs `pipelines.manage`. For a **GitHub** repository, Ankra reads `.ankra/pipeline.yaml` at `default_branch` itself, through the organisation's stored Git credential, and stores a valid file as the definition of record in the same call - connect a repository that already commits one and the first webhook after connecting has something to run. GitLab and Bitbucket Cloud repositories connect the same way but need `ankra pipeline definition put` afterwards (step three, below): reading the committed file at connect time is GitHub-only today. `default_branch` defaults to `main`; `credential_name` is optional for a public repository. Connecting a repository the organisation already connected answers `409` with the existing repository's id, not a second row. For a **Bitbucket Cloud** repository, send `"provider": "bitbucket_cloud"`, the workspace as `owner`, the repository slug as `name`, and the Bitbucket credential as `credential_name`, public repository or not: once the connect commits, Ankra registers the repository's webhook with that credential, unless the organisation already has one on it. Connected without a credential, it gets no webhook from the connect, so unless the organisation already has one on the repository, no push, tag or pull request reaches its pipeline. Two more fields on the same call, both optional and both addressed by id rather than name: `application_id` links the repository to an existing Ankra Application without changing what that application deploys, and `cluster_id` overrides the organisation's CI cluster for this repository alone, instead of the organisation-wide `ci_cluster_id` (below). Disconnect a repository with `DELETE /org/pipelines/repositories/{id}` - `pipelines.manage`, `204` on success, `409` while a run is still live; it is a soft delete, so reconnecting the same identity later revives the row rather than creating a second one. `GET /org/pipelines/repositories` lists what is connected, keyset paginated; `GET /org/pipelines/repositories/{id}` reads one. The CLI does the same with `ankra pipeline repositories connect|list|get|disconnect` (CLI v0.15.0 or later), and the portal's **Settings → Pipelines** page lists connected repositories. **2. Optionally, give the organisation a vault of its own** Every report a `scan` stage writes, every finding a `gate` reads, every step log archive and every declared artifact is a presigned upload into the organisation's oldest ready [backup vault](/concepts/backup-vaults) - or, for an organisation that has none, into the Ankra-managed default vault. So this step is optional: skip it and the first run still keeps its logs and its reports. Do it when the objects should live in a bucket you own, or when you want cluster backups and `ankra migrate` as well, which never use the platform's vault. One vault serves the whole organisation, and one it already uses for cluster backups serves pipelines too - if `ankra backup vaults list` already shows a `ready` row, this step is done. Let Ankra create the bucket from a Hetzner, UpCloud, DigitalOcean or Scaleway credential: ```bash theme={null} ankra backup vaults provision ci-artifacts --credential upcloud-main --region europe-1 --wait ``` Every argument is an override: `ankra backup vaults provision` on its own names the vault, picks the only credential it can provision from and takes that provider's usual region, printing all three before it creates anything. A Hetzner credential is the one exception - Hetzner issues Object Storage keys in its Cloud Console (**Object Storage > Manage credentials**) and its API cannot mint them, so the command prompts for the pair. Or register a bucket you already run, on any S3-compatible service: ```bash theme={null} ankra backup vaults create ci-artifacts --endpoint https://s3.example.com --bucket ankra-ci ``` Either way the vault has to reach `ready` before it counts: a vault still provisioning, or one whose last verification failed, has no bucket a presigned URL would reach, so the platform treats it as absent and keeps using its own default vault. Registering a vault changes nothing about what is already stored - objects stay where they were written, and are still downloadable and still reaped; the *next* step's objects go to yours. `ankra backup vaults get ci-artifacts` shows the status, and [Backups](/guides/backups) covers the rest. **3. Choose the cluster that runs pipelines, and turn on its agent's pipeline-step workers** An organisation administrator sets the CI cluster. Any member can read the settings; changing them answers `403` for everyone else. ```bash cURL theme={null} curl -X PUT https://platform.ankra.app/api/v1/org/ci-settings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"ci_cluster_id": ""}' ``` The same request takes the rest of the CI settings - see [Organisation CI settings](#organisation-ci-settings). Leave `ci_cluster_id` unset and the organisation's AI staging cluster is used instead. Then raise that cluster's pipeline-step worker count above zero - the number of pipeline steps its agent runs at once, in slots separate from its read and write pools. It is `0` on every cluster until you change it, and an agent at zero does not advertise the pipeline job names at all, so a step for that cluster is refused before it is dispatched. ```bash cURL theme={null} curl -X PUT https://platform.ankra.app/api/v1/org/clusters//agent/ci-settings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"ci_worker_count": 2}' ``` ```bash theme={null} ankra cluster agent ci set --workers 2 --cluster ``` This is a setting Ankra owns on the agent's behalf, not a chart value you apply yourself: the write is stored, and Ankra hands it to the agent through the agent's own upgrade lane so the running release re-renders with it. An online agent new enough to take chart values picks it up immediately, an older one at its next upgrade, and an offline one when it reconnects - the response says which. Never `helm upgrade` the agent by hand to set it: Ankra re-renders that release, so a hand-set value is overwritten by the next reconcile. `ci_storage_class` on the same call names the StorageClass for cache volumes and for a workspace claim that names none; leave it empty for the cluster default. The agent creates the `ankra-ci` and `ankra-ci-build` namespaces on first use, with their Pod Security admission labels, a `ResourceQuota` and a `LimitRange`. See the [agent Helm values](/reference/agent-helm-values) for what the release carries. **4. Write and validate .ankra/pipeline.yaml** This is the shape Ankra's own setup pull request commits for a new Go application - reuse it as a starting point. Every stage below now executes (see [What is in place today](#what-is-in-place-today)): `checkout` clones the commit, `test` runs your script, `build` produces a real digest in a rootless BuildKit pod, `scan` runs Trivy, Semgrep and Checkov against that digest and your source, `gate` judges what they found against your organisation's policy, and `publish` re-tags the one digest `gate` approved into your real repository. The image is never rebuilt and never a mutable tag until that last step - the same digest the whole way from `build` to the registry your application deploys from. ```yaml theme={null} apiVersion: ankra.io/v1 kind: Pipeline metadata: name: "orders-api" on: push: branches: - "main" pull_request: branches: - "main" fork_policy: "read_only" concurrency: group: "${{ ankra.repository.provider }}:${{ ankra.repository.owner }}/${{ ankra.repository.name }}-${{ ankra.ref }}" cancel_in_progress: true workspace: size: "10Gi" access: "rwo" defaults: image: "golang:1.26" timeout: "30m" network: "egress-https" permissions: contents: "read" stages: - name: "checkout" kind: "checkout" - name: "test" kind: "run" image: "golang:1.26" run: "go run gotest.tools/gotestsum@v1.13.0 --format testname --jsonfile test-results.json -- ./..." needs: - "checkout" test_results: - format: "go-test" path: "test-results.json" - name: "build" kind: "build" needs: - "test" build: dockerfile: "Dockerfile" platforms: - "linux/amd64" build_args: - "VERSION" provenance: true sbom: true - name: "scan" kind: "scan" needs: - "build" scan: scanners: - "semgrep" - "checkov" - "trivy" fail_on: checkov: "none" semgrep: "error" trivy: "app" - name: "gate" kind: "gate" needs: - "scan" gate: require_stages: - "scan" - name: "publish" kind: "publish" with: image: "harbor.ankra.cloud/orders/orders-api" needs: - "gate" when: events: - "push" ``` The `fail_on` map above reads per scanner and only ever tightens the gate: `semgrep: "error"` raises Semgrep's own floor to `error`, while `checkov: "none"` and `trivy: "app"` are not severities in those tools' vocabularies, so they carry no floor at all and leave the organisation's `ci_image_gate` policy to decide. Nothing a pull request writes here can loosen that policy. Ask Ankra AI to validate it - in the portal chat or through the [MCP server](/platform/mcp-server), the `cicd_validate_pipeline_yaml` tool answers with a severity (`ok`, `warn` or `fatal`) and every violation with its key. A `fatal` definition cannot be planned; fix it before committing. A `warn` is worth reading rather than skipping past: a stage whose `kind` has no executor on this build reports one, and that stage will be skipped. ```bash theme={null} ankra pipeline validate --application my-service ``` Reads `.ankra/pipeline.yaml` from the current directory (or the file you name), plans it for a synthetic push and a synthetic pull request, and prints the steps that would run, the stages that would be skipped and why, and every diagnostic - no repository connection required for this step alone. `--repository ` validates for a bare repository instead of `--application`. **5. Store the definition, commit and push** ```bash theme={null} ankra pipeline definition put .ankra/pipeline.yaml --application my-service git add .ankra/pipeline.yaml && git commit -m "Add the Ankra pipeline" && git push ``` `definition put` needs `pipelines.manage` and stores the definition server-side; it does not touch the repository. On **GitHub**, a run no longer needs that stored copy to be current: the planner fetches `.ankra/pipeline.yaml` at the run's own head commit and plans the run's *logic* from it, so editing the file in a pull request takes effect on that pull request's next run. On **GitLab** and **Bitbucket Cloud** the stored definition is still what runs, so keep it identical to the committed file there. Either way the run's *authority* - the protected sections - still comes from the default branch's approved definition, never from the head file (see [Logic is open, authority is closed](#logic-is-open-authority-is-closed)). **6. Approve the definition's protected authority** An organisation administrator does this once per definition, and again whenever a protected section changes on the default branch. The id to approve is the repository's **current default-branch** definition, and the run's own status is where it is named: the check run summary and the pull request status comment carry it inside the approve command they suggest. `ankra pipeline get ` reports the authority state and says where to read the id, deliberately without guessing one - the definition a run drew its *current* authority from is rarely the one waiting to be approved, and approving that would answer `409`. With the id in hand, read what you are about to grant, then approve it: ```bash theme={null} ankra pipeline definitions get ankra pipeline definitions approve ``` `definitions get` prints the definition's protected hash and whether it is the approved one; when it is not, it names the exact `approve` command to run. `approve` needs `pipelines.manage` and a **human actor** - a service token or agent token is refused, because approving grants whatever permissions, credentials, secrets scope and network tier the definition declares. It confirms first; `--yes` skips the prompt for scripted use. Only the repository's *current* default-branch definition can be approved, and only once: a definition the default branch has since moved past, or one already approved, answers `409`. The same act is `POST /api/v1/org/pipelines/definitions/{definition_id}/approve` on the API. In the portal, an administrator approves from **Settings → Pipelines → Pending approvals** or from the run page, and an administrator whose GitHub account is linked to their Ankra profile can press **Approve authority** on the GitHub check run. Approving re-runs the runs that waited for it. Skipping this does not stop a run - it strips it. A pipeline whose default-branch definition declares any protected section executes with **none** of them until an administrator approves, and the check run and pull request comment say so verbatim: "No administrator has approved any authority for this repository yet, so this run executes with none." A definition that declares no protected section at all needs no approval, because there is nothing to grant. **7. Watch the run** The push you just made triggers a run on a connected repository. To dispatch one by hand, name the commit - and it has to be one of the repository's own. Ankra resolves `--ref` (or the repository's default branch when you name none) through the repository's own host before writing anything, and accepts your `--sha` only when it is that reference's current tip or - on GitHub, through a compare read - a provable ancestor of it. Anything else is refused by name, and so is a question the host could not answer at all. GitLab and Bitbucket Cloud have no compare read, so a commit you name there has to be the exact tip. ```bash theme={null} ankra pipeline run --wait ankra pipeline list ankra pipeline logs --step checkout --follow ``` Run from inside the checkout, the CLI reads what it can see: the application is the one bound to the repository your `origin` remote points at, and the commit is your `HEAD`. It says which it used. Name them yourself with `--application` and `--sha` when you are outside a checkout, when the repository has more than one application, or when you mean a commit other than the one in front of you: ```bash theme={null} ankra pipeline run --application my-service --sha "$(git rev-parse HEAD)" --wait ``` `--wait` follows the run to its conclusion and exits non-zero on anything but `success`. Every stage runs for real: `checkout` clones the commit, `test` runs your script, `build` produces a digest in a rootless BuildKit pod, `scan` runs Trivy, Semgrep and Checkov against it, `gate` judges what they found, and `publish` re-tags the digest `gate` approved to `sha-<7>` in your real repository - nothing is published that was not scanned and gated first. Because `--wait` already blocked until the run concluded, the `logs --step checkout --follow` command above is reading `checkout`'s log back after the fact, not watching it live - from the CLI's next release that reads the step's complete, archived output; today it reads only whatever a fresh connection to the live relay happens to catch from an already-finished step, which for a step this far in the past is usually nothing. `--follow` earns its name against a step that is still running, which is the case whenever you check on a pipeline without also passing `--wait`. On GitHub the commit carries an `Ankra pipeline` check with a step table and a Cancel button, and on a pull request the same run populates the `pipeline` section of Ankra's one status comment; the portal's **Pipelines** section shows the same run with a timeline and an in-app log viewer, and **Settings → Pipelines** lists the repository you connected in step one. **8. Wait on a run a push or pull request started** Most runs are not dispatched by you: the push or pull request webhook starts them, so a CI job that has to wait for the result holds no run id. `ankra pipeline get` selects the run by what the job does know instead - `--head-sha` (the full sha), `--branch` and `--trigger` narrow it, and `--latest` takes the newest match (on its own, the pipeline's newest run). Several matches without `--latest` are refused and listed, exit `2`; no match is exit `3`. ```bash theme={null} ankra pipeline get --head-sha "$(git rev-parse HEAD)" --trigger pull_request --latest --wait --timeout 45m ``` `--wait` blocks until the run concludes, then prints its final detail, exactly as `pipeline run --wait` does. The webhook can land a moment after the job asks, so under `--wait` or `--watch` a selection that matches nothing yet waits for the run to appear, up to `--timeout` or ten minutes when none is set. `--timeout` takes a duration and bounds `pipeline run --wait` and `pipeline rerun --wait` the same way. On GitHub Actions, a `pull_request` job's checkout is a merge commit rather than the head the run was started for, so pass `${{ github.event.pull_request.head.sha }}` there instead of `git rev-parse HEAD`. To react while the run moves instead of only when it ends, `--watch` prints each run and step state change until it concludes: ```bash theme={null} ankra pipeline get --watch -o json ``` With `-o json` that is one JSON object per line: `event` (`run` or `step`), `observed_at`, `pipeline_run_id`, `run_number`, `step_key`, `status`, `previous_status` (`null` the first time a run or step is seen), `outcome`, and the whole `run` or `step` object. The first lines are the full current state, and the run's conclusion is always the last line. With `--wait` or `--watch` the exit code is the verdict: `0` when the run succeeded, `1` when it concluded any other way or the platform could not be read, and `5` when `--timeout` ran out. A plain `get` still exits `0` whatever the outcome; add `--exit-code` to a single read to branch on it without waiting - `0` or `1` for a concluded run, `5` while it has not concluded yet. That loop - webhook, trigger decision, plan, dispatch, a real checkout, your steps, a check run, the portal - is what the rest of this page documents in depth: the mechanics behind it, why CI belongs in your cluster at all, exactly which stage kinds execute today, what a pull request can and cannot change, and the limits every run is held to. ## How a run happens ```mermaid theme={null} flowchart LR Event[Push, pull request, tag or dispatch] --> Trigger[Trigger decision] Trigger -->|enqueued| Plan[Plan: stages, matrix, needs, skipped] Trigger -->|no run| Reason[Recorded reason] Plan --> Dispatch[Org-fair dispatch to the CI cluster] Dispatch --> Job[One Job per step in ankra-ci] Job --> Settle[Settle: outcome, unblock, conclude] Settle --> Check[Check run and commit status] Settle --> Vault[Logs and artifacts in your vault] ``` 1. **Trigger.** A verified delivery from GitHub, GitLab or Bitbucket Cloud - a branch push, a pull request opened, synchronised or reopened, or a tag - is normalised into one event and decided in a fixed order: is the repository connected, is the head trusted, is there a cluster to run on, is there a definition, does the definition's `on:` block accept the event. A dispatch from the CLI, the API or an agent goes through the same decision, after one of its own: a delivery's commit arrives proven by the provider, a dispatch's does not, so the ref it names is resolved against the repository's own host and a caller-supplied `head_sha` is accepted only as that reference's tip or a provable ancestor of it. Every answer is recorded, so a push that started nothing still says why ([the reason tokens](/guides/pipeline-reference#triggers-and-reasons)). 2. **Plan.** The planner first decides *which* definition it is planning: on GitHub it fetches `.ankra/pipeline.yaml` at the run's own head commit and uses that for the run's logic, falling back to the stored definition (with a diagnostic) if the read fails, and recording which source won; on GitLab and Bitbucket Cloud the stored definition is always what runs. A dispatch that carried its own definition (`--spec-file`, `spec_yaml`) outranks both: the override is stored verbatim on the run, and the planner and the dispatcher both render from it. Protected authority is resolved separately and never from the head file - nor from an override, which is content nobody committed. It then turns that definition and the event into the step DAG: which stages survive the trigger and stage filters, how a matrix fans out (at most 64 legs per stage), what each step depends on, which `if:` conditions can be settled now, and - for a pull request that touches a protected section - which authority the run actually executes under (see [Logic is open, authority is closed](#logic-is-open-authority-is-closed)). Nothing is dropped silently - a filtered stage is recorded with a reason code, and a run whose every stage is filtered out is recorded as concluded with outcome `skipped`. A stage whose `kind` has no executor on this build - `preview`, `agent`, `approval`, `webhook`, `automation`, `ankra`, `external` and `verify` - is skipped right here, with reason `kind_unavailable` and a sentence naming the kind and the wave that ships its executor, so a run that declares one still concludes instead of holding itself and its concurrency group open forever. 3. **Dispatch.** Steps are claimed org-fairly, never global FIFO: organisations are served in order of how many steps they already have running, and a run already at its own step cap is passed over for the next run's step rather than holding the queue (see [Capacity and concurrency](#capacity-and-concurrency)). The dispatcher resolves the cluster per step - the organisation's CI cluster, otherwise the staging cluster it already nominated for AI work - and probes that the cluster's agent advertises the `run_pipeline_step` job before sending anything. Dispatch is also the point where a step's deferred `if:` is evaluated and its remaining `${{ steps..outputs. }}` and `needs..*` references are interpolated into `run`, `env`, `with`, `image`, `working_directory` and the stage's own `outputs:`, now that the steps it waited for have concluded: a condition that reads false concludes the step `skipped`, and a reference that still cannot be resolved concludes it `infra_error` naming the field, rather than handing a container a literal `${{ }}`. Both happen *before* anything is revealed, so a step about to be skipped spends no secret. Declared secrets and credentials are resolved into one-time, per-step reveal grants at this point, never written into the payload itself. 4. **Run.** The agent renders the step's own Kubernetes objects from the signed payload - it never applies what the platform sent verbatim - and runs a hardened batch/v1 Job. A `checkout` step clones the run's commit with a pinned public git image, reading a revealed credential only if the host it is cloning from matches; a `run` step executes your script. Output streams live while the step runs; the complete log and the declared artifacts are uploaded to your backup vault when it concludes, through presigned URLs minted at dispatch. The vault is the organisation's own oldest ready one, and an organisation with none is granted uploads into the Ankra-managed default vault instead; the step's execution says which - `pipeline_artifacts: "granted"` or `pipeline_artifacts: "granted (Ankra-managed default vault)"`. Only when there is no ready vault on either side does a step get no upload grants at all, recording `pipeline_artifacts: "unavailable: This organisation has no ready backup vault, so pipeline step logs and artifacts have nowhere to be stored"` - live output is unaffected, and everything archival is lost. 5. **Settle.** The settle loop records the verdict, unblocks the steps that waited on it and concludes the run when nothing is left. Each pass re-stamps the run's own heartbeat, so a build that legitimately takes hours is no longer failed out from under you by the platform's stale-run reaper at twenty minutes. Only Ankra's own failures are retried, once; a non-zero exit is the step's verdict and is final. A concluded `build` step's digest is stamped as the application's build observation, ranked below your own CI's, so push-to-deploy, previews and rollback already read a pipeline-built image. `gate` and `publish` settle in the same pass: `gate` reads the run's own persisted scan findings against your organisation's policy, tightened per scanner by the `fail_on` each required `scan` stage declared and recording the digest it actually judged, and `publish` re-tags that digest into your real repository as `sha-<7>` - refusing outright if the build's digest is a different one, or if the gate recorded none. 6. **Report.** The `Ankra pipeline` check run (or commit status) is updated on every change, naming the authority the run executed under when it differs from what the head asked for. On Bitbucket Cloud each kind of run keeps a commit status key of its own, so a tag, pull request or dispatched run on a commit no longer overwrites the branch run's result there: `ankra/pipeline` for a push, `ankra/pipeline/pull-request`, `ankra/pipeline/tag`, `ankra/pipeline/dispatch` for a manual, API or agent dispatch, and `ankra/pipeline/schedule`, and a re-run keeps its original's key. On a pull request the same run state and the same authority sentence populate the `pipeline` section of Ankra's one status comment. The run's outcome also leaves the pull request: `pipeline_run_failed`, `pipeline_run_succeeded`, `image_gate_blocked`, `preview_ready` and `preview_failed` are ordinary Ankra notification kinds, so they reach whichever Slack, Teams, PagerDuty or webhook destination your [routing rules](/guides/notification-routing) send them to. Deploying the result follows straight from what `publish` just did: an Ankra Application deploys the `sha-<7>` image `publish` tagged into your real repository, exactly as it would deploy an image your own CI published. A pull request gets its own preview from the same pipeline, before `publish` ever runs (`publish` fires only on `push`, per the example above) - the application's preview deploys the digest `build` produced for that head, by digest, never a mutable tag, and GitLab and Bitbucket Cloud pull requests now get previews the same way GitHub ones always could. The [generated GitHub Actions workflow](#bring-your-own-ci-github-actions) still works and is still what an application with no pipeline connected uses; nothing forces you off it. > [!NOTE] > > **A build you start by hand does not go through the gate.** The two paths publish to different places, deliberately: > > * a **pipeline** build stages the digest under `ankra-ci//`, and `publish` re-tags it into `/` only after `gate` has judged it; > * **`ankra application build start`** publishes straight to `/` - no `scan`, no `gate`. > > So a manual build writes directly to the repository your Application deploys from. That is what it is for: it is the recovery route for when the pipeline itself is broken, which is exactly when the gated path is unavailable. Use it knowing the image has not been scanned or gated, and let the pipeline publish anything you intend to keep. ### Which component a build belongs to A `kind: build` stage of an application's pipeline belongs to one of the application's components, and that decides where it pushes and where `publish` re-tags it. Ankra reads it in this order: 1. **`build.component`**, when the stage declares it. The value is the name of a component the application records. 2. **The stage name**, otherwise: a stage called `build-` belongs to that component. 3. **The application's one component**, for a single-component application, whatever the stage is called. Declare `build.component` when one component has more than one build, or when you want to name a stage for what it builds. A typical case is a component that ships both a container image and a release bundle: ```yaml theme={null} stages: - name: build-portal # release bundle for host deploys, attributed by its name kind: build build: bundle: path: dist/portal manifest: deploy/portal/ankra-release.yaml - name: build-portal-image # container image for Kubernetes, attributed explicitly kind: build build: component: portal dockerfile: portal/Dockerfile context: portal - name: build-node-agent # release bundle that belongs to no component kind: build build: bundle: path: dist/node-agent manifest: deploy/node-agent/ankra-release.yaml ``` Each stage pushes to its own repository, so an image and a bundle of the same commit never overwrite each other's `sha-<7>` tag: | The stage builds | It publishes to | | - | - | | an image of a component | the component's repository, `/` | | a release bundle of a component | the component's bundle repository, `/-bundle` | | a release bundle that belongs to no component | `/`, for example `/node-agent` | An image has to belong to a component, because a deploy of the application only reads component repositories. A pipeline that declares no `build.component` anywhere keeps working exactly as before. In a repository with no linked application, `build.component` has no effect, and every stage still publishes as `/`. `ankra pipeline validate` checks the stages against the application before a run. It reports a stage that belongs to no component, a `build.component` the application does not record, and two stages that would publish to the same repository. That last finding is fatal when both stages always run, because each run's commit tag would be overwritten by whichever stage publishes last, and a warning when one of them runs only under a condition. The components an application records come from its [`.ankra/ankra.yaml`](/reference/application-descriptor#components) when it declares them. When a run reaches a stage it cannot attribute, the step concludes `step_refused`, and the message lists the components the application records. ### Monorepo: PR-only checks, full builds on push A monorepo usually wants a pull request to check only the components it touches, while a push to the default branch builds everything. Write **one stage per component**, and give each stage an [event-scoped `when`](/guides/pipeline-reference#event-scoped-filters) block: the pull request filters on paths, and the push does not. Do not duplicate stages per event. ```yaml theme={null} on: push: branches: [main] pull_request: branches: [main] stages: - name: checkout kind: checkout - name: test-web kind: run image: node:22 run: pnpm -C web test needs: [checkout] when: pull_request: paths: ["web/**", "packages/ui/**", ".ankra/**"] push: {} - name: test-api kind: run image: golang:1.26 run: go test ./... working_directory: api needs: [checkout] when: pull_request: paths: ["api/**", "go.work", ".ankra/**"] push: {} - name: report kind: run image: alpine:3 run: ./scripts/summarise.sh needs: [test-web, test-api] if: always() ``` A pull request that touches only `web/` runs `checkout` and `test-web`. `test-api` is skipped as `path_filter` and recorded as skipped on the run. A push to `main` runs every stage. Put the pipeline file (`.ankra/**`) in each list so a change to the pipeline checks everything it touches. * A pull request's changed files are read from your provider when the run is triggered, up to 300 paths. When they cannot be read, or the change is larger than that, no stage is skipped for its paths, and the run's `paths_unverified` says why. * A stage that needs a component stage keeps the usual rule: it is skipped when that stage is left out. To run it anyway, name the stage in [`skippable_needs`](/guides/pipeline-reference#fields-common-to-every-stage), so it waits on what the skipped stage waited on, or give it an `if:` with a status function such as `always()` (above), so its condition decides and `needs.test-api.result` reads `skipped`. * Check the shape before you commit it. `ankra pipeline validate --changed-file web/app.ts` prints each stage against push and pull request, so you can see the plan for that change. * Write each list once. When several stages share a list, or every list carries `.ankra/**`, name it under [`path_sets:`](/guides/pipeline-reference#path-sets) and write `{set: }` where the list was, for example `path_sets: {pipeline: [".ankra/**"], web: ["web/**", "packages/ui/**", {set: pipeline}]}` and `paths: [{set: web}]`. ### Affected-only stages: skip what a change did not touch A change that touches only `web/` should not wait for the API's tests, and it should still build, scan and gate, and on a push publish. Give each component's test stage `paths:`. A stage with `paths:` is **affected-only**: when no changed file matches, it is skipped as `path_filter` and the stages that need it run without it, waiting on what it waited on. ```yaml theme={null} stages: - name: checkout kind: checkout - name: test-web kind: run image: node:24 run: pnpm -C web test needs: [checkout] paths: ["web/**", "packages/ui/**", ".ankra/**"] - name: test-api kind: run image: golang:1.26 run: go test ./... working_directory: api needs: [checkout] paths: ["api/**", "go.work", ".ankra/**"] - name: build kind: build needs: [test-web, test-api] build: dockerfile: Dockerfile - name: scan kind: scan needs: [build] scan: scanners: [semgrep, trivy] - name: gate kind: gate needs: [scan] gate: require_stages: [scan] - name: publish kind: publish needs: [gate] with: image: registry.example.com/acme/shop when: events: [push] ``` A push that changes `web/app.ts` runs `checkout`, `test-web`, `build`, `scan`, `gate` and `publish`; `test-api` is skipped and `build` waits on `test-web` and `checkout`. A pull request with the same change runs the same stages except `publish`, which runs on pushes only. A push that changes only `README.md` skips both tests and still builds and publishes; that is deliberate here, because the image is built from the whole repository either way. If a documentation change should not publish, give `build` and the stages after it a `when.paths` filter, which skips them together. A change to `.ankra/pipeline.yaml` runs everything. * **`paths:` or `when.paths`.** `when.paths` skips the stages behind a stage it leaves out, so a filtered test stops the publish behind it; that is the right rule for a component's whole chain (its build, scan, gate and publish skipped together), and it is what the generated monorepo pipeline uses. `paths:` is for a stage whose absence should not hold anything back. One stage declares one of them, never both. * **Every event.** `paths:` applies to pushes as well as pull requests, so a push to `main` also runs only the affected tests. For "pull requests check what they touch, pushes check everything", use the [event-scoped `when`](#monorepo-pr-only-checks-full-builds-on-push) recipe with `skippable_needs`. * **What it refuses.** A stage that is left out produced nothing, so `ankra pipeline validate` refuses (`pipeline_paths:`) a stage that reads its outputs, a `build` that another stage needs (what needs a build reads its image), a `checkout`, `gate` or `approval` with `paths:`, and a scan a gate names in `require_stages`. It warns about an affected-only scan (a change outside its paths is not scanned) and an affected-only `deploy`, `publish` or `preview` that another stage needs. * **When the change cannot be seen** (no change list, or more than 300 files), no stage is left out, and the run's `paths_unverified` says why. * Check a change before you commit it: `ankra pipeline validate --changed-file web/app.ts` prints which stages run and which are skipped. ### Affected from the dependency graph: `affected:` Path globs have one blind spot: a package a stage's tests import from somewhere else in the repository. `test-api` lists `api/**`, a change to `libs/auth` that `api` imports matches nothing, and the tests that would have caught the break are skipped. `affected:` names the packages instead of the files, and Ankra follows their imports. ```yaml theme={null} stages: - name: checkout kind: checkout - name: test-api kind: run image: golang:1.26 run: go test ./... working_directory: api needs: [checkout] affected: go: ["./..."] paths: ["migrations/**"] - name: test-web kind: run image: node:24 run: corepack enable && pnpm install --frozen-lockfile && pnpm --filter @acme/web... test needs: [checkout] affected: pnpm: ["@acme/web"] - name: build kind: build needs: [test-api, test-web] build: dockerfile: Dockerfile ``` A stage with `affected:` runs when a changed file is in the dependency graph of what it names, its tests included, or matches its `paths:`. Otherwise it is skipped as **not affected**, and the stages that need it run without it, exactly as for an affected-only stage left out by its `paths:`. * **`go`** names Go packages the way the stage's own `go test` does: `./...` and `./internal/api` are read from the stage's `working_directory`, and an import path such as `example.com/shop/libs/...` works too. Ankra reads every `go.mod` in the repository, so a module another one pulls in through a local `replace` directive counts. A change to `go.mod`, `go.sum` or `vendor/` reaches every package of that module. * **`pnpm`** names workspace packages by name (`@acme/web`) or by directory (`./apps/web`). A dependency on another workspace package, by name, `workspace:` alias or `file:` path, is followed. A change to a file at the repository root, such as `pnpm-lock.yaml`, the root `package.json` or `pnpm-workspace.yaml`, or to a pnpm patch (any file under `patches/` and any `.patch` file), reaches every pnpm stage. Documentation and Go files at the root do not: `*.md`, `LICENSE`, `CHANGELOG`, `.gitignore`, `.gitattributes`, `*.go`, `go.mod`, `go.sum`, `go.work` and `go.work.sum`. * **`paths:` beside `affected:`** adds files no import graph sees: migrations, fixtures a test opens by path, a script the stage runs. A matching file runs the stage. * **Where the graph comes from.** The run's checkout step reads the graph of the exact commit it checked out, in a few seconds, and records an answer for each stage. Nothing is cached and there is no extra step. The planner decides only what it can without the code: a change list it could not read in full, a change to `.ankra/pipeline.yaml` or a matching `paths:` glob runs the stage, and a change of no files leaves it out. * **A skip needs an answer.** The stage is skipped only when the checkout answered that no changed file reaches it. No answer at all (a pattern that names nothing, an analysis that failed or took over two minutes) runs it, and the checkout's log says why. A pipeline with no checkout stage never reads the graph: its `affected:` stages run on every change, `ankra pipeline validate` warns about it (`pipeline_paths:`), and the run's plan records it (`plan_paths:`). * **A skip never waits for capacity.** A not-affected stage is concluded as soon as the checkout has answered, without waiting for a free CI slot on a busy cluster, and the stages that need it start as soon as they have one of their own. The time it saved is recorded with reason `not_affected`. * **Promotion.** A merge that [promotes](/guides/pipeline-reference#promote-a-tested-image-instead-of-rebuilding) the pull request run's image counts a stage that run skipped as not affected, when the merge changes no file that run's graph was not asked about. Otherwise the merge builds in full and reads the graph itself. * **The same rules as `paths:`.** `affected:` makes a stage affected-only, so it is refused where a skip would be unsafe: a stage whose outputs another reads, a `build` another stage needs, a `checkout`, `gate` or `approval`, a scan a gate judges. A false `if:` is still the condition's own skip and skips what needs the stage. * Check a change before you commit it: inside your checkout, `ankra pipeline validate --changed-file libs/auth/token.go` reads the same graph from your working tree and shows which stages the change reaches. ### Newest commit wins A `concurrency.group` decides which runs serialise. `cancel_in_progress` decides what happens when a newer one arrives: with it, the new commit supersedes the run it replaces; without it, the new run waits for the older one to finish - and on a group that varies per branch, that is a branch queueing behind itself while the older commit's run keeps your step slots. ```yaml theme={null} concurrency: group: "${{ ankra.repository.provider }}:${{ ankra.repository.owner }}/${{ ankra.repository.name }}-${{ ankra.ref }}" cancel_in_progress: true ``` Every pipeline Ankra generates ships exactly that. A definition you wrote keeps whatever it says - Ankra never flips the flag for you, because that would cancel runs your own file says should finish. What Ankra does instead is tell you: * `ankra pipeline validate` (and `ankra application pipeline validate`) reports `pipeline_concurrency_queues_behind:` at severity `advice`, naming the group and the one line that changes it. Advice never blocks a run and never fails the command. * A queued run whose `Waiting:` line names an older run of the *same* branch or pull request that will not be superseded carries the same recommendation. A holder on another branch does not - that is the group serialising across branches, which is what a group is for. * A pipeline [converted from a GitHub Actions workflow](/guides/migrate-from-github-actions) keeps the workflow's own queue-behind behaviour, with a conversion note recommending the flag for a per-branch group on a `pull_request` trigger. A branch whose every commit must end in a verdict, such as the default branch that deploys or writes the caches pull requests read, wants the opposite trade from a pull request. You don't need two files for that: each flag can be one expression that is settled per run, for example `cancel_in_progress: "${{ event.kind != 'push' }}"` with `cancel_queued: "${{ event.kind == 'push' }}"`. See [A concurrency policy per event](/guides/pipeline-reference#a-concurrency-policy-per-event). Superseding is a soft stop, not a failure. The older run concludes as cancelled and is shown as **Superseded** in grey rather than red, the commit status it leaves on GitHub, GitLab or Bitbucket is neutral rather than a failed check, and no failure notification is raised for it - attention follows the newest run of the branch. ## What a pull request gets A connected pull request carries one comment, not several: a run, its preview and Ankra AI's own review each write into that comment's own `pipeline`, `preview` and `review` sections in place rather than posting separately - see [One status comment per pull request](/changelog#one-status-comment-per-pull-request). The pipeline section is the run's own state, plus the authority sentence whenever the run executed under anything but the plainly approved definition; the preview section is the throwaway environment deployed from the run's own digest; the review section is Ankra AI's verdict on the diff. ### The review reads your pipeline, not just GitHub Actions Ankra's pull request review used to read a GitHub check run for its "is this commit passing" signal, so a repository already running on Ankra Pipelines still got a review that could not see its own pipeline. The review now resolves the newest pipeline run of the reviewed commit first, and falls back to GitHub's own check runs only for an application whose real build authority is still its own CI or the generated workflow, or for a head no pipeline run has touched yet. When a pipeline run answers, the review's prompt carries a bounded set of facts about it, each independently sourced so an absent one never reads as a clean one: * the failing step's name, kind and error message, when the run is failing - not its log, which stays behind `ankra pipeline logs` * the run's scan findings, by tool and severity, with the highest-severity individual findings named * the `gate` step's own verdict and blocking count, once one has concluded * the `build` step's reported image digest, once one has concluded * the authority sentence, when the run executed under anything other than the plainly approved definition The branch **Code review** lane (no pull request involved, see [Application AI Settings](/platform/application-ai)) does not read this block - only the pull request review does. ### A human can say "merge when green" Mention **`@ankraai merge when green`** in a pull request comment, ask Ankra AI in chat, or call the API, and Ankra records the request and watches the pull request, merging it (squash) the moment every condition holds: * the requester is an active member of your Ankra organisation holding **`pipelines.operate`**, and the binding's [AI mode](/integrations/github#ask-ankra-in-a-comment) is **Agent** - in **Ask** mode the request is refused, naming the setting to change * the GitHub login that commented is linked to that member through an **OAuth-verified** identity. A login claimed by hand, or auto-filled from an Auth0 GitHub sign-in, proves the account exists and not that the commenter owns it, so it no longer counts as verified; the refusal says to link GitHub again. A service principal is refused outright - merging is a human act * the requester still holds `pipelines.operate`, is still an active member and is still OAuth-verified when the sweep actually merges, not only when the comment was written. A request can wait up to 24 hours, and an administrator can revoke the permission inside that window * the pull request's head is not a fork * the newest pipeline run for the head concluded successfully, and its `gate` step, if the definition declares one, passed * the run executed under plainly approved authority - one that fell back to an earlier approval, or none, is refused rather than merged under it * Ankra's own review has not requested changes on that exact commit * no human reviewer's most recent review is **Changes requested** - unlike the AI review's check, this one is not scoped to the current commit, so a changes-requested left on an earlier push still blocks until the same reviewer clears it with a later review The request expires 24 hours after it is made, and a new push to the pull request invalidates it. Every refusal is posted once, naming the exact condition that failed. This works on GitHub only today: a request against GitLab or Bitbucket Cloud is accepted and then refused, because neither provider adapter reports the capability to merge yet. ### The merge train: test every pull request as it will merge A pull request that is behind its base branch merges as a tree nobody tested: the pull request's own run tested its head, not its head on top of everything that landed since. Turn on the **merge train** for a repository and Ankra tests each pull request **as the commit its merge will produce** before it merges it - the way GitLab's merge trains work, and without GitHub's merge queue, which Team-plan private repositories do not have. ```bash theme={null} ankra pipeline train enable --application orders-api # pipelines.manage; --max-cars 3 to test three at once ankra pipeline train add 412 --application orders-api # pipelines.operate; --head-sha pins the commit you reviewed ankra pipeline train list --application orders-api # the train in order, and what recently left it ankra pipeline train remove --application orders-api ankra pipeline train front 412 --application orders-api # CLI 0.47.0+; organisation administrators only ``` For each pull request in the train, Ankra writes a **car**: the pull request squashed onto the base branch - or onto the car ahead of it, when more than one is tested at once - exactly as GitHub's squash merge will produce it, on a branch of its own (`ankra-merge-train/`). A pipeline run tests that commit. It is planned as a pull request into the base branch, so it runs only when the pipeline declares `on.pull_request` and its `branches` match the base branch, `when.events: [pull_request]` stages run and push-only `publish` and deploy stages do not, under the base branch's approved authority. When the car's run passes and the base branch has not moved, Ankra squash-merges the pull request through GitHub's own merge - branch protection, required reviews and required checks apply exactly as for a person's merge, and Ankra never pushes to or force-pushes the base branch. Because the merge is exactly the tree the car tested, a definition whose `publish` declares [`promote_from: pull_request`](/guides/pipeline-reference#promote-a-tested-image-instead-of-rebuilding) publishes the car run's tested image on the merge instead of building and testing it again: merge to published is a re-tag. What happens when something moves: * **A car fails.** The pull request leaves the train with the failing run linked; every car behind it, which included it, is rebuilt without it. Each car tests everything ahead of it, so the pull request that failed is the one that broke, with no bisection. * **Somebody merges around the train.** Every car built on the old base branch head is rebuilt on the new one, and nothing is merged onto a base its car did not test. * **The pull request is pushed to, retargeted or closed.** It leaves the train: the train merges the head a person put in it, never one that arrived later. * **GitHub declines the merge** - usually because the pull request's own required check is still running. The car waits, passed, and the merge is retried every pass, while the cars behind it keep building and testing. After three hours it leaves the train with GitHub's reason, or after 15 minutes when GitHub says a required check is failing, because a failed check does not turn green by waiting. See [Merges GitHub declines](/pipelines/merge-train#merges-github-declines). * **A fork.** Refused, as for merge when green. * **An administrator moves a pull request to the front.** With CLI 0.47.0 or later, `ankra pipeline train front` puts it ahead of every car that is still queued, building or testing; cars that already passed or are being merged stay ahead. Every car it passes is rebuilt on top of it, so those pull requests wait longer. That is why only an organisation administrator can do it: being the pull request's author or having write access is not enough. Ankra posts an **Ankra merge train** check on every pull request it holds, and concludes it `success` only immediately before it merges. Make that check required on the base branch, and every merge goes through the train. The train writes with a GitHub App token minted for that one repository with only `contents`, `pull_requests` and `checks` permissions. It works on GitHub only today. ### Chat watches a run, and a failed build gets a fix Ask Ankra AI to watch a run - or dispatch one, which registers the watch itself - and the `watch_pipeline_run` tool wakes the same conversation the moment it concludes, with the failing step, a bounded tail of its log, the findings and the gate verdict already in the prompt. Separately and unattended, a failed `build` step (never `infra_error`, a stranded step, or one a protected policy refused - only a genuine script failure) on a **direct push** to the tracked branch, in **Agent** mode, dispatches the same auto-fix mission the generated GitHub Actions workflow's own failed-build webhook already dispatches, with a log excerpt as evidence. A failed build on a **pull request** never gets an unattended commit from this lane, in either mode - it gets its diagnosis (the failing step, its error class, a log excerpt) posted into the status comment's `pipeline` section instead, pointing at Agent mode's **Propose a fix** on the [pull request review](/integrations/github#ask-ankra-in-a-comment) as the actual route to a fix. ## Why CI belongs in your cluster The generated GitHub Actions workflow Ankra Applications ship with works, and keeps working - see [Bring your own CI](#bring-your-own-ci-github-actions) below. Ankra Pipelines exists because of what that model cannot fix: * **Runners are a second fleet.** GitHub-hosted runners are shared and rate limited; a self-hosted runner pool is one more set of machines to size, patch and pay for. A pipeline step is a Job on nodes you already have, scheduled under the quota and node selectors you set, and never at the expense of a deploy - the agent runs pipeline work in its own worker pool. * **Credentials live in CI secrets.** A workflow needs registry logins, cloud credentials and deploy tokens as repository secrets, copied per repository and readable by any workflow that names them. In a pipeline definition those are *references* the platform resolves and delivers as a one-time, per-step reveal - never written into a job spec or a log - and the sections that grant them are protected: a pull request can rewrite what a pipeline does, and cannot widen what it may touch. * **Publish happens before the gate.** A workflow that pushes an image and scans it afterwards has already published a vulnerable image by the time the scan fails. The pipeline vocabulary separates `build`, `scan`, `gate` and `publish` so that nothing is published that the gate did not pass - enforced end to end today: a `publish` step refuses by name when its run has no successful `gate` step immediately upstream, and it re-tags only the exact digest that gate evaluated. * **The evidence is somewhere else.** Run history, logs, scan artifacts and the check on the commit all lived on the CI provider. A pipeline run is an Ankra object: `ankra pipeline get`, live logs, artifacts in a backup vault - the organisation's own, or the Ankra-managed default one - and the same status on GitHub, GitLab or Bitbucket. ## What is in place today Ankra Pipelines is landing in pieces, and this page is honest about the line. Everything in the first list is on the platform and the agent now; everything in the second is not, and is described in [What comes next](#what-comes-next). **In place** * The definition: `.ankra/pipeline.yaml` (`apiVersion: ankra.io/v1`, `kind: Pipeline`) with its parser, validator and dry-run planner - see the [pipeline.yaml reference](/guides/pipeline-reference). * Conversion of an existing GitHub Actions, GitLab CI, Bitbucket Pipelines or CircleCI definition into a pipeline, from chat or the MCP server - see [Migrate from GitHub Actions](/guides/migrate-from-github-actions). * The organisation's CI settings (which cluster runs pipelines, parallelism, image allow-list, retention, the image gate) and its pipeline policy (the authority ceiling an administrator sets). The image gate is no longer only a setting: `gate` steps (below) evaluate every run against it. * **Connecting a repository**, with an optional link to an application and a CI-cluster override, and **disconnecting** one. `POST /org/pipelines/repositories` connects a bare repository and, on GitHub, reads its committed `.ankra/pipeline.yaml` as the definition of record in the same call; `DELETE /org/pipelines/repositories/{id}` disconnects it, `409` while a run is still live. An Ankra Application connects and decides its own repository automatically on create and on every reconcile. A new application's setup pull request commits `.ankra/pipeline.yaml` by default instead of a generated workflow. * **Migrating an existing application onto a pipeline with one call** - see [Migrate an existing application](/guides/migrate-from-github-actions#migrate-an-existing-application-with-one-call). * The runs API and the `ankra pipeline` commands: dispatch, list, get, cancel, re-run, live step logs, artifacts, the stored definition, schedules. * Triggers from push, pull request and tag deliveries on GitHub, GitLab and Bitbucket Cloud, deduplicated across redeliveries, with a recorded reason whenever nothing runs. A Bitbucket Cloud push that updates several branches or tags at once starts each branch's and each tag's pipelines, and a Bitbucket pull request's abbreviated head commit is resolved to the full commit through the repository's credential before its run is created; a head that cannot be resolved starts no run. A **private GitLab repository** builds through the organisation's stored source credential the same way GitHub and Bitbucket Cloud ones do, and a GitLab project under a **nested group** (`group/subgroup/project`) resolves to a clone URL instead of being refused by the owner grammar. * **A stage kind nothing can run is skipped, not left pending.** The kinds this build has no executor for - `preview`, `agent`, `approval`, `webhook`, `automation`, `ankra`, `external` and `verify` - now report a validation **warning** naming the stage, its kind and the wave that ships its executor (severity `warn`, so the pipeline still plans and every other stage still runs), and the planner skips exactly those stages with reason `kind_unavailable`. A run that declares one concludes on its own; before this it sat `pending` forever, holding its concurrency group with it. A `pending` step whose run has outlived the sum of every step's `timeout` is additionally reaped as `infra_error` / `step_stranded`, so a step that stranded for a reason no static check can see - a cluster that never resolved, an agent that never advertised support - cannot pin a run open either. * **`uses:` is refused by name, at validate time.** Any `uses:` reference - `ankra/*`, `custom_tool/*` or `repo:/@#`, however it is spelled - is fatal with one sentence, "Reusable steps are not available yet; write the step as `run:`", until the step library ships. That is deliberately louder than the old behaviour, where a `uses:` stage validated and then failed at dispatch with an error that read as "you forgot to write a script". * **Step outputs and deferred conditions are resolved at dispatch.** A stage's `if:` that depends on a step's result, and every `${{ steps..outputs. }}` or `needs..*` reference in `run`, `env`, `with`, `image`, `working_directory` and `outputs:`, are evaluated once the steps they wait on have concluded - a false condition concludes the step `skipped` before any secret is revealed, and a reference dispatch cannot resolve concludes it `infra_error` naming the field rather than passing a literal `${{ }}` into a script. * **`on_failure` and `finally` stages run when their names say.** An `on_failure` stage runs after the main stages have all concluded and only when one of them failed; after a green run it is skipped with its reason. A `finally` stage runs after everything before it has concluded, whatever the outcome, and also after a run that had started is cancelled (`ankra pipeline cancel --force` skips it). See [Failure and cleanup stages](/guides/pipeline-reference#failure-and-cleanup-stages) for the exact rules. * **In-cluster execution of `checkout` and `run` stages** on the organisation's CI cluster: a hardened Job per step in `ankra-ci`, a per-run workspace volume, cache volumes, service sidecars, network tiers, resource ceilings, timeouts, live logs, and one automatic retry of Ankra's own failures. `checkout` clones with a pinned public git image, a shallow fetch of the exact commit with a fallback to fetching its ref, and refuses an `lfs:` request by name rather than silently checking out pointer files. * **Caches survive between runs, inside their own trust scope.** A stage's `cache:` block resolves to the same PersistentVolumeClaim on every run that declares the same key, scoped to the repository, cluster, cache path **and the trust of the run resolving it** - the second run's `test` stage mounts what the first run's left behind, not an empty volume. A miss on the exact key that matches a `restore_keys` prefix restores the most recently used claim under that prefix instead of starting cold, the same semantics as GitHub Actions' own cache action, and that prefix search stays inside the caller's own scope too. Retention is unchanged by any of this: a claim nothing has resolved to for `ci_cache_retention_days` (below) is still deleted by the same sweep, per claim. * **A fork's run resolves no shared cache at all, and a branch never resolves the default branch's.** Two holes, closed together. A pull request from a fork that runs restricted - the ordinary `fork_policy: read_only` case - is refused a shared cache at dispatch, on the same predicate that already withholds its secrets and credentials: every declared cache degrades to the pod-lifetime volume a cluster with no usable StorageClass already gets, and the step's execution metadata carries `pipeline_caches: "caches are not shared with fork runs"` so an operator can see why the build was cold rather than guessing. Such a step never reaches the cache store at all. Separately, every claim now carries a **trust scope**: `default_branch` for a run on the repository's own recorded default branch, `branch` for everything else - a feature branch push, a pull request (fork or not), a tag, a manual dispatch of a non-default ref. The two scopes never share a claim, and neither an exact-key hit nor a `restore_keys` prefix match crosses between them, so a less-trusted ref can no longer poison what a production build restores. It is deliberately coarser than per-branch isolation: the threat is an untrusted ref writing into what a trusted one reads, not one feature branch reading another's warm cache. **Claims that existed before this deploy were backfilled into `branch`, the untrusted scope** - nothing on a row records which run wrote it, so the true value cannot be recovered, and defaulting the other way would have carried the hole forward for every cache written before the fix. The cost is one cold build per cache key on your default branch, once. * **A restricted fork run is budgeted, not only restricted.** It is clamped to at most **8 matrix legs per stage**, a **60-minute timeout per step**, **2 CPU and 4Gi of memory per step**, **no sidecar services**, **no cache volumes** and **256 MiB of artifacts and logs across the whole run** - fractions of the platform-wide ceilings of 64 legs, 6 hours, 8 cores, 32 GiB and 2 GiB, because a drive-by pull request triggers this with nobody having reviewed it first. Matrix legs, timeout, compute and sidecars are clamped in the **planner**, so the plan `ankra pipeline validate`, the dry run and the pull request's status comment render is the run that actually executes, rather than one silently rendered smaller at dispatch; caches are refused at dispatch, and the artifact budget bounds what the run is granted, from the sizes its steps declare. The matching ceiling on what actually *arrived* - the one number a step cannot lie about - is written into the artifact store but has no settle lane calling it yet, so today the budget is enforced on the grant rather than on the upload. A `plan_fork_budget:` diagnostic states the ceilings once per run, plus one more for each stage that actually hit one, and a declared quantity the planner cannot read is clamped rather than given the benefit of the doubt. An organisation's own policy may lower these numbers further; nothing raises them for a fork. * **`build` produces a real digest.** A rootless BuildKit pod in its own `ankra-ci-build` namespace resolves its own builder, source and push target at dispatch and pushes the image it builds, by digest, to the organisation's CI staging repository; a bare repository with no application link, or a component with no resolvable registry, gets a named refusal instead of a guess. `provenance: true` and `sbom: true` ask BuildKit to attest the digest it pushes. * **Ankra platform builders build when your cluster cannot.** When the CI cluster cannot build, the organisation set `ci_build_fallback: platform_builders`, **and** Ankra has granted the organisation platform builds, a `build` step runs on Ankra platform builders instead - the same staging-repository push and the same `scan`/`gate`/`publish` downstream, with the step's `executor` recorded as whichever lane actually ran it. What "cannot build" covers, how to confirm and request the grant, and what a fallback build looks like from your side are all in [Ankra platform builders](#ankra-platform-builders) below. An application that publishes to [a registry you operate yourself](/concepts/applications#using-your-own-registry) is pushed by this lane with the push robot Ankra minted for it there, exactly as the in-cluster build is. * **`scan` runs three tools for real, one step per tool.** Trivy (filesystem, a CycloneDX SBOM, and - once a build produced one - the image itself), Semgrep and Checkov, each from a pinned, digest-referenced public image the organisation's own `ci_allowed_image_prefixes` cannot turn off, writing the reports `gate` and the application's **Security** tab both read. `with.semgrep_config` names the registry packs to run (`p/default p/docker …`); undeclared, Semgrep runs its own `--config=auto`. * **`gate` judges what `scan` found, and `scan.fail_on` is part of the judgement.** It reads the run's own persisted findings against the organisation's `ci_image_gate` policy (below), which a `gate` stage's own `with:` may only tighten, never loosen; a declared scanner that recorded nothing fails the gate by name rather than passing as clean. Each required `scan` stage's own `fail_on` is folded in on top, per scanner and **tighten-only**: a pull request head may add scrutiny to its own pipeline, never remove the organisation's. A `fail_on` value that is not that scanner's own severity vocabulary at all - Checkov's `none`, a Trivy image-gate word such as `app` - carries no floor rather than the loosest one. * **`publish` re-tags only the digest `gate` approved, and proves it.** A `publish` step refuses by name when its run has no successful `gate` step; otherwise it re-tags the exact digest `build` pushed to staging as `sha-<7>` in the application's real repository - never rebuilt, never a different digest than the one `scan` and `gate` judged. The gate now records the digest it actually judged on its own step outputs, and `publish` refuses when the build's digest is anything else - **including when the gate recorded none at all**, because "no record" is unknown, not agreement. Before this the guarantee rested on ordering alone: a successful gate existed upstream, so surely it had judged this image. * **`deploy` rolls a release out to your own hosts.** A `kind: deploy` stage releases the signed release bundle a `publish` stage of the same run produced to the active host targets of its `environment`: machines outside Kubernetes that run `ankra-host-agent` and connect outbound only (see [`ankra targets`](/reference/cli/targets)). The platform settles the step from what each host reports, and [`ankra deployments`](/reference/cli/deployments) shows how every host fared. The stage's `environment` and `deploy` block are protected, a pull request or fork run never deploys (`deploy_untrusted_event`), and the step refuses an artifact this run did not publish. It does not deploy to a Kubernetes cluster: an application's own push to deploy does that. See the [`deploy` row](/guides/pipeline-reference#stages) of the reference. * **A monorepo's components are tracked separately.** A `publish` step resolves the `build` and `gate` steps carrying its own stage suffix rather than whichever succeeded first, so `publish-web` can no longer re-tag `api`'s digest into web's repository, and a stage name following neither convention is refused by name instead of guessed at. A `scan` stage honours `working_directory`, so a component's scan reads the tree its build and test stages do rather than the whole checkout. Build observation is recorded per component, with only the primary component also moving the application-level record, so push-to-deploy, previews and rollback keep converging on the image they always did. * **A run plans from `.ankra/pipeline.yaml` at its own commit** (GitHub). The planner fetches the file at the run's head sha and plans the run's logic from it, records which source won (`definition_source`: `head_file`, `stored` or `default`), and falls back to the stored definition with a diagnostic when GitHub is briefly unreachable - a transient read failure must never stop a push running its own pipeline. Only a head file that really was fetched and is then unparseable or fatally invalid refuses the run. Protected **authority** is unaffected: it still resolves from the default branch's approved definition, never from the head file. A **re-run** re-plans from the *original* run's definition, so re-running an old run reproduces it rather than silently executing whatever the pipeline has become since. * **Previews deploy the pipeline's own digest.** An application whose pipeline source is `ankra_pipeline` gets its pull-request preview from the `build` step's own digest for that head - by digest, never a mutable tag - on GitHub, GitLab and Bitbucket Cloud alike; the gateway also re-drives a stalled preview the moment a pipeline run concludes, on top of the existing poll fallback. * **Fork pull requests run, restricted.** `checkout` and `build` clone the fork's own head anonymously - never the pipeline's own repository - `build` renders with no push target so nothing it produces can reach a registry, and every stage but `checkout`, `build` and `scan` runs with no network at all regardless of what the fork's own copy of the definition asks for. Such a run also resolves no shared cache and is held to fixed matrix, timeout, compute and artifact budgets (above). See [Forks](#what-a-step-runs-as). * **Secrets and credentials delivered into steps** as one-time, per-step reveal grants collected by the agent at step start: a `secrets` binding sourced `app_env_secret`, `org_variable` or `registry` resolves, and a `credentials` binding sourced `git` or `registry` resolves; `secrets: [{from: credential}]` validates but is not deliverable yet, and `cloud` and `object_storage` credentials are refused by name until the run gets its own scoped identity (below). * **Schedules fire.** The cron trigger dispatches through the same trigger every push and pull request uses, advancing from a stored watermark so a loop that was down for a while does not replay every occurrence it missed. * **Protected authority is enforced, not only modelled.** Whichever definition supplied a run's *logic*, its *authority* resolves from the default branch's admin-approved definition; a pull request that changes a protected section (below) executes under the last approved authority instead, and the check run, commit status, the definition response and the pull request's own status comment all say so. The last-approved fallback only ever offers a definition that was itself, at some point, the default branch's own head - proved from the repository's run history, never from the approval columns on the definition row alone. * **One approval route, whichever id you hold.** The application-addressed approval (`POST /org/applications/{id}/pipeline/approve-definition`) is now an alias for the definition-addressed one, so both run the same checks - a human actor rather than a service principal, the definition must be the repository's *current* default-branch definition, a second approval of an already-approved definition is a conflict rather than a silent yes - and both write the same audit row. Before this the application-addressed route had none of them: a caller holding that application's `pipelines.manage` could approve any definition row belonging to its repository, including one sourced from a pull request head that never was, and might never be, the default branch's own content. * **A dispatch proves its commit belongs to the repository.** `POST …/pipeline-runs` resolves the `ref` it was given - or the repository's default branch - through the repository's own host before anything is written, and accepts a caller-supplied `head_sha` only when it is that reference's current tip or, on GitHub, a provable ancestor of it through a compare read. Anything else is refused by name, and so is a transport failure that leaves the question unanswered: an unanswerable question is not a yes. GitLab and Bitbucket Cloud have no compare read, so a commit named there is held to the exact tip. **Re-runs keep working** and are deliberately exempt - a re-run's commit is one Ankra itself recorded when the original run was created, not a claim arriving with this call, which is what lets a fork pull request's run be repeated at all. * **A dispatch's own definition is what actually runs.** `--spec-file` (`spec_yaml` on the API) is persisted verbatim on the run, and both the planner and the dispatcher render every step from it, instead of shaping the plan and then silently re-rendering each step from the stored definition. Only the *logic* moves: an override is content nobody committed, so its protected sections are compared against the default branch's approved authority exactly as a pull request head's are. * **Five more sections are protected authority.** `services`, `cache`, `resources.cpu` and `resources.memory`, `timeout` and a stage's `with:` block now hash, resolve in both directions and surface by name, so a head can no longer ask for far more compute than the organisation meant to grant, remove its own or the trusted definition's timeout, change which run's output a cache restores into the workspace, or rewrite a `gate` stage's own scan-tool configuration. **This moves every definition's protected hash** - see the re-approval note under [Logic is open, authority is closed](#logic-is-open-authority-is-closed). * **A sidecar is held to the step's own boundary.** A named `services:` entry's image goes through the organisation's image policy and its `env` through the reserved-name check, both as a refusal of the whole step rather than a diagnostic that runs it anyway; `ankra pipeline validate` names the same violation at `services[].image`. A sidecar could previously pull any image and set `ANKRA_*`, the loader and `PATH` variables, or the image builder's own `BUILDCTL`, `BUILDKIT_HOST` and `DOCKER_CONFIG`. * **Two organisation ceilings that nothing could set now bite.** `ci_ignore_unfixed` (below) is an organisation-wide floor a `gate` stage's own `with:` may only tighten, and `max_cache_size` on the pipeline policy is stored and enforced - every declared cache had been held to it since the ceiling was written, with nothing able to set it, so every organisation silently resolved "no ceiling". * **`egress-https` reaches the public internet, and nothing private.** The tier's 443 rule now excludes RFC1918 (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`), the whole of link-local `169.254.0.0/16` rather than only the `169.254.169.254` endpoint that used to be its single exception (AWS ECS serves task metadata at `169.254.170.2`), and carrier-grade NAT `100.64.0.0/10` (Alibaba Cloud answers at `100.100.100.200`, inside it). There is an IPv6 rule at all now, excluding `fd00::/8`, `fe80::/10` and AWS's `fd00:ec2::254`. DNS stopped being "port 53 to any pod in the cluster": it is the `kube-system` namespace plus `169.254.0.0/16` for a cluster running NodeLocal DNSCache, and the `services` tier - which carried no DNS rule at all - gained the same one. **This breaks a pipeline that reaches a private HTTPS endpoint**, and the symptom is a connection timeout rather than a refusal, so name the range in `ci_egress_allowed_cidrs` (below) *before* the step runs: a `build` that pushes to a registry on a private address - an in-cluster registry, a Harbor inside your VPC - cannot reach it until that entry exists. The agent renders a step's NetworkPolicy itself, so the tightened tier arrives with the agent release of 5 September 2026; a cluster still on an older agent keeps the previous tier until the fleet roll reaches it. * **An image policy prefix ending in a separator means that separator.** An entry ending in `/`, `:` or `@` used to match nothing at all, because the matcher requires one of those three characters *after* the prefix - the boundary check that stops `ghcr.io/my-org` admitting `ghcr.io/my-org-evil/api`. Each now admits exactly its own boundary: `ghcr.io/my-org/` admits `ghcr.io/my-org/api` and refuses `ghcr.io/my-org` and `ghcr.io/my-org:v1`, `ghcr.io/my-org:` admits a tag and not a path, `ghcr.io/my-org@` admits a digest, and `registry.example.com/` admits paths on that host and never `registry.example.com:5000/anything`, which is a different endpoint. Trimming the separator away would have widened each of those rather than narrowing it, so that is not what happens, and an entry that is only separators - or ends in two of them - matches nothing rather than becoming the empty prefix every image starts with. The settings API refuses a trailing separator at write time and always has, so a stored `ci_allowed_image_prefixes` cannot carry one: this is defence in depth for the prefixes Ankra sets in code and for the copy of the policy a step's payload carries, not a change to what an administrator can store. * SCM feedback: one `Ankra pipeline` check run per run on GitHub with a real conclusion and a Cancel button, a commit status on GitLab and Bitbucket, a comment fallback when the GitHub App lacks `checks:write`, and - on a pull request - the same run state and authority sentence in the `pipeline` section of Ankra's one status comment. * **The AI review reads your pipeline, and a human can say "merge when green".** See [What a pull request gets](#what-a-pull-request-gets) above: a bounded pipeline-facts block in the review prompt, a `merge when green` pull request comment gated on the run, its gate, both reviews and its authority, and a `watch_pipeline_run` chat tool whose unattended fix loop reacts to a failed `build` step. * Step logs and declared artifacts stored in the organisation's oldest ready backup vault - or, for an organisation with none, in the Ankra-managed default vault - listed and downloaded through the API today; `ankra pipeline artifacts` and `artifacts download` speak the same routes and read the same way from the CLI's next release (see [Commands and API](#commands-and-api)). **A vault is no longer something to arrange first** - a `scan` stage's reports travel the same path, so a fresh organisation records findings and passes its gate without provisioning a bucket; bringing your own decides where the objects live, not whether they are kept (see [Before your first run](#before-your-first-run)). * **Long runs stay alive.** Every settle pass re-stamps the run's heartbeat, so the platform's twenty-minute stale-run reaper no longer fails a pipeline that is still working. A run whose steps genuinely stopped reporting is still reaped, and the reason is recorded. * **Run history is retained by policy, not forever.** `ci_run_retention_days` (default 90, 7 to 365, below) bounds how long a concluded run is kept, and an hourly sweep deletes past the window in batches. It never deletes a live run, never a repository's own newest concluded run, and never a run still carrying an artifact the artifact reaper has yet to remove from the vault. * **Run outcomes reach your destinations.** `pipeline_run_failed`, `pipeline_run_succeeded`, `image_gate_blocked`, `preview_ready` and `preview_failed` are notification kinds like any other, produced once per run (or once per gate verdict, or when the preview row itself flips), so [notification routing](/guides/notification-routing) sends them to Slack, Teams, PagerDuty or a webhook alongside everything else Ankra reports. * **A truncated log says so, and the failing step names its own error.** The agent reports how many log lines it had to drop to backpressure; that count is recorded on the step and shown as a notice under its row on both the check run and the pull request status comment, so a step whose output was silently cut no longer looks like one whose log arrived whole. The check run's step table also carries the failing step's own error message (truncated to 200 characters) in its Outcome cell, instead of only the run's error in one blockquote at the end. * **The portal.** The application's **Pipelines** section shows Ankra pipeline runs with a timeline and an in-app log viewer beside the GitHub Actions view; organisation **Settings → Pipelines** lists connected repositories; the application's **Security** section reads a pipeline's own findings once a run has concluded for its repository, falling back to the GitHub-artifact reader until one has. **Not in place yet** * **The runner for `verify`.** No runner image exists, so a `probe`, `smoke`, `load_test`, `assert_promql` or `restore_verify` sub-block never runs. The stage is skipped with `kind_unavailable` rather than dispatched into an image pull that cannot succeed, so it no longer holds the run open - but it also verifies nothing, and a pipeline that relies on it is not being verified. * The executors for the remaining stage kinds - `preview` (a per-pull-request environment deployed from *inside* the pipeline DAG, not to be confused with an Application's own pull-request preview, which already deploys a pipeline's digest, above), `agent`, `approval`, `webhook`, `automation`, `ankra` and `external`. Each validates with a warning and is skipped, so a run containing one concludes; none of them does the work its name promises. In particular an `approval` stage does **not** block a run on a human today - it is skipped, and everything downstream of it runs. * The per-run identity `permissions` mints, so a step can call the `ankra` CLI with exactly the authority the default branch granted - a converted `github.token` reference already diagnoses itself as "the run-scoped `ANKRA_TOKEN`", naming what does not exist yet rather than resolving to nothing; `cloud` and `object_storage` credentials wait on the same identity. * The built-in `ankra/*` step library behind `uses:`, organisation step templates, and `custom_tool/` steps. Until it ships, any `uses:` is fatal at validate time with one sentence telling you to write the step as `run:`. * **Nothing pins the step keys an expression names.** `${{ steps..outputs. }}` resolves at dispatch against the run's real steps, but a key that does not exist - a typo, or a matrix leg the writer guessed at - interpolates to empty text rather than being refused. The validator declares expression *roots*, not key sets, and a step key is a matrix-expansion product it cannot know; closing this belongs in the planner, which does know the expanded keys. * **The user-declared `retry` block is validated and carried to the agent, not applied.** A step's own `retry:` is parsed and stored on the plan, but nothing dispatches a second attempt from it - only Ankra's own infrastructure failure classes retry automatically (see [When something fails](#when-something-fails)); a script that exits non-zero is final today regardless of `retry.max_attempts`. Dynamic matrices (`matrix: ${{ fromJSON(steps.plan.outputs.legs) }}`, fanning out over a previous step's own output) are not available either - every axis must be written out in the file. * **Attestations and SBOM referrers on the image you actually deploy.** `build`'s `provenance`/`sbom` flags attest the digest it pushes to the CI staging repository; `publish`'s re-tag copies that one image manifest, not the attestation manifests beside it, so they are not on the digest your application deploys. Trivy's SBOM is an artifact in the vault today, not an OCI referrer on any image. * **Merge-when-green and automatic disposition re-evaluation are GitHub only.** A GitLab or Bitbucket Cloud `merge when green` request is accepted and then refused, because neither provider adapter reports the capability to merge yet; a push to a GitLab or Bitbucket Cloud default branch that adds or removes `.ankra/pipeline.yaml` answers `insufficient evidence` rather than re-evaluating the application's pipeline source, because the raw-file read that evaluation needs has no GitLab or Bitbucket equivalent wired in yet. * **A dedicated log stream for pipeline output, on the agent side.** The platform half is in place: the `pipeline_output` JetStream stream exists with its own capacity and retention, the agent is granted publish on its own cluster's subject, dispatch renders that subject onto every step, and the log relay reads the dedicated stream first and falls back to the shared one. What has not shipped is the agent release that actually publishes there, so live output still rides the platform-wide `execution_output` stream until your cluster's agent is upgraded to a release carrying that change. Nothing about `ankra pipeline logs` or the check run changes when it does - the relay already reads either. * **Planning from the committed file is GitHub-only.** A GitLab or Bitbucket Cloud run plans from the stored definition permanently, because the bounded raw-file read the planner uses has no equivalent wired in for those providers yet; the run records `definition_source: stored` and says so in a diagnostic rather than pretending it read your commit. * **Annotations and metrics.** A step's stdout can already carry `$ANKRA_OUTPUT` key=value lines, read back onto the step; the equivalent for check-run annotations (`::ankra::notice file=…,line=…::msg`) and custom metrics (`::ankra::metric name=…,value=…`) is not ingested anywhere yet. Test reports are: see [Test results](#test-results). * Environments hydrated from restore points, verification stages, promotions with approvals, and canary rollouts - see [What comes next](#what-comes-next). ## Test results A stage that runs tests can declare the reports its test command writes, and Ankra reads every test's outcome from them: ```yaml theme={null} - name: "e2e" kind: "run" run: "pnpm test:e2e" artifacts: - name: "junit" paths: - "reports/junit" test_results: - format: "junit" path: "reports/junit" # a file, or a directory of report files ``` After the step, the agent uploads each report to your organisation's [backup vault](/concepts/backup-vaults) like an artifact (named `ankra-tests..` in the run's artifact list), and Ankra parses it. The step's own exit code stays its verdict: a report Ankra cannot read never fails or passes the step. **Formats.** | `format` | What to point it at | | - | - | | `junit`, `pytest` | JUnit XML: `go-junit-report` and `gotestsum --junitfile`, pytest `--junitxml`, Jest's `jest-junit`, Vitest's `junit` reporter, Maven Surefire, Playwright's `junit` reporter. A directory is read for every `.xml` file in it. | | `playwright` | Playwright's JSON reporter (`reporter: [['json', { outputFile: 'results.json' }]]`). | | `go-test` | `go test -json` output, or `gotestsum --jsonfile`. | **Your command writes the report.** `test_results` only names where the report is; the stage's own command has to write it there, or there is nothing to read and the run's test summary lists the report as never uploaded. For example: `gotestsum --jsonfile test-results.json -- ./...` (or `go test -json ./... > test-results.json`, which leaves the step's log as raw JSON), `pytest --junitxml=reports/junit.xml`, `vitest run --reporter=junit --outputFile=reports/junit.xml`. A path is relative to the workspace, not to a directory the command `cd`s into or the stage's `working_directory`. A pipeline Ankra generates for you declares a report only when its test command writes it. **Flaky tests.** A test that failed and then passed on a retry is recorded as `flaky`, not passed, with the number of attempts. Ankra reads retries from Playwright's JSON reporter, from a JUnit file's Surefire `flakyFailure`/`rerunFailure` elements, and from a test that appears more than once in one report (`gotestsum --rerun-fails`). Playwright's `junit` reporter only writes retries with `includeRetries: true` (or `PLAYWRIGHT_JUNIT_INCLUDE_RETRIES=1`); without it a flaky test reads as passed, so use the JSON reporter or turn that on. **What you get.** * The run page's **Tests** section: failed tests first, then flaky ones, then the slowest, each with its duration, attempts and failure message. A report that was declared but not uploaded or could not be read is listed with the reason. * `GET …/pipeline-runs/{run_id}/tests`: the same summary over the API. * `GET …/pipeline-tests/history?test_key=…`: one test's outcomes in the newest runs of your default branch (or `branch=`), with its flaky rate. * `GET …/pipeline-tests/timings?stage=…&group_by=file|test`: each file's or test's mean recorded duration on the default branch, to balance shards by time. `runs_sampled: 0` means there is no data yet; fall back to an even split. All three need `pipelines.read`, and each is addressed by repository or by application, for example `/api/v1/org/pipeline-repositories/{repository_id}/pipeline-tests/history` and `/api/v1/org/applications/{application_id}/pipeline-runs/{run_id}/tests`. **When a report is missing.** Declaring `test_results` never fails a step that passed. Today Ankra uploads a declared report only when the stage also lists the same path under `artifacts:`, as the example above does: a path the stage already declares is one the step promised to write. A report that is declared only under `test_results` is not uploaded, and the run's test summary says so and why. If your test command never wrote the report (the suite crashed before its reporter ran), the summary says that too, rather than reading as a run with no tests. ### Quarantining a flaky test A test that keeps failing on and off should not fail every run while someone fixes it. Each repository can **quarantine** such tests. A quarantined test still runs and is still reported; what changes is that a step whose **only** failed tests are quarantined passes. Quarantine is off on every repository until you turn it on, and only a member with `pipelines.manage` can change it. **When a failure is forgiven.** A failing step passes because of the quarantine only when all of these hold. Otherwise it fails exactly as it would without one: 1. It is a `kind: run` step that failed on its own (`step_failed`). A timeout, an infrastructure error, a refusal, and any build, scan, publish, deploy or verify step always fail as before. 2. Quarantine is on for the repository. 3. The step declared at least one test report, and every one was uploaded and read whole. 4. The reports hold at least one failed test, and every failed test is quarantined. A test that passed on a retry counts as `flaky`, not failed. A forgiven step reads as passed with a note of how many quarantined tests failed in it, its dependents run, and the run's test summary always lists the quarantined failures under **Quarantined failures**, with whether each one rescued its step. Nothing is hidden: the failed tests are still in the failed list too. Write test steps so that a failure after the tests (a `;` instead of `&&`) fails the step on its own, because the quarantine only reads the step's test reports. **Which tests can be quarantined.** A test qualifies when, over your default branch's own runs in the last 30 days (never pull request checks), it ran in at least `min_samples` runs and was `flaky` in at least `min_flaky_rate` of them. | Setting | Default | Range | | - | - | - | | `enabled` | `false` | | | `min_flaky_rate` | `0.1` (10%) | above 0, up to 1 | | `min_samples` | `20` | 3 to 1000 | | `release_after_passes` | `10` | 1 to 100 | **Leaving quarantine.** A quarantined test is released when someone releases it, or on its own after it passes `release_after_passes` default-branch runs in a row; a failure or a flake on the default branch starts the count again. Turning quarantine on or changing a setting, quarantining a test and releasing one are each recorded in the organisation's audit log, an automatic release under `system`. **Commands.** ```bash theme={null} ankra pipeline tests quarantine settings --enable # turn it on for this repository ankra pipeline tests quarantine candidates # the tests that qualify now ankra pipeline tests quarantine add --reason "flaky since the cache change, ENG-123" ankra pipeline tests quarantine # settings, quarantined and recently released tests ankra pipeline tests quarantine release --reason "fixed in #42" ``` Over the API, on the same repository or application paths as the other test routes: `GET …/pipeline-tests/quarantine`, `PUT …/pipeline-tests/quarantine/settings`, `GET …/pipeline-tests/quarantine/candidates`, `POST …/pipeline-tests/quarantine` with `{"test_key", "reason"}`, and `POST …/pipeline-tests/quarantine/{id}/release`. Quarantining a test that does not qualify, or while quarantine is off, is refused with the reason, naming the test's flaky rate and runs against your settings. **Limits.** A report is at most 32 MiB, and a directory at most 256 report files and 64 MiB unpacked. At most 100,000 tests are read from one report, and 20,000 per report and 50,000 per run are stored, failed and flaky ones first; the counts always cover every test read. Failure messages are kept to 2,000 characters, and anything that looks like a credential is redacted. A report with a `DOCTYPE` is refused. Test results are kept as long as the run they belong to. ## Organisation CI settings `GET` and `PUT /api/v1/org/ci-settings` (and `/org/ci-settings` in the portal session) carry the organisation-level settings every pipeline runs under. Reading them is open to every member; changing them is an organisation administrator's act. | Setting | Default | Meaning | | - | - | - | | `ci_cluster_id` | none | The cluster pipeline steps execute on. Unset, the organisation's AI staging cluster is used. | | `ci_build_fallback` | `platform_builders` | Whether [Ankra platform builders](#ankra-platform-builders) may build when your own cluster cannot. `none` refuses the build instead, for source that may not leave your infrastructure. The setting is necessary but not sufficient: the fallback also needs the grant `platform_builds_enabled` reports. | | `platform_builds_enabled` | `true` | Read-only, and not a setting: whether platform builds are on for your organisation. They are on for every organisation unless Ankra has switched them off for yours. `ankra org ci-settings get` prints it as `Platform builds enabled: yes` or `no` (CLI v0.17.0 or newer). While it reads `false`, a build `ci_build_fallback: platform_builders` would route to it is refused as `build_fallback_none` with a sentence saying the fallback is not enabled for this organisation yet. A `PUT` naming it is refused, because only Ankra can change it. | | `ci_max_parallel_runs` | `16` | Runs of this organisation in flight at once (1 to 64). | | `ci_max_parallel_steps` | `8` | Steps of **one run** in flight at once (1 to 256). It is not an organisation-wide total; see [Capacity and concurrency](#capacity-and-concurrency). | | `ci_allowed_image_prefixes` | empty | Image prefixes a step may name, matched at a path boundary (up to 50 prefixes of up to 200 characters). Empty means images are not checked, and each run records that. | | `ci_artifact_retention_days` | `30` | How long step logs and artifacts are kept (1 to 365). | | `ci_cache_retention_days` | `14` | How long a cache nothing restored is kept (1 to 365). | | `ci_run_retention_days` | `90` | How long a concluded run and its steps are kept (7 to 365 - a longer floor than artifacts and caches, because run history is what a person reads back after the fact). An hourly sweep deletes past the window; it never removes a live run, a repository's newest concluded run, or a run whose artifacts the vault still holds. | | `ci_image_gate` | `app` | Which image findings block a `publish`: `app` blocks on fixable CRITICAL and HIGH findings in your own dependencies, `all` also on what the base image carries, `off` on nothing - the scan still runs and still reports either way. Only the built image is gated: what Trivy finds scanning the checkout itself (`trivy-fs.json`, a lockfile entry or a build tool) is recorded and reported but never blocks a publish, because it is not in the image. A `gate` stage's own `with:` may only move this stricter for its one pipeline, never looser. | | `ci_ignore_unfixed` | `true` | Whether a finding with no published fix is excluded from blocking the gate - Trivy's own lenient default. Set it `false` and every unfixed finding keeps blocking whatever any pipeline asks for: like the two settings above, a `gate` stage's own `with.ignore_unfixed` may only move toward the strict direction from here, never back. | | `ci_egress_allowed_cidrs` | empty | Private ranges a step on the `egress-https` tier may still reach on 443 (at most 32). Each entry is a CIDR written as its own network address - no host bits - sitting wholly inside `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` or `fd00::/8`, and covering no cloud metadata endpoint: AWS answers on `fd00:ec2::254`, which is inside `fd00::/8`, so `fd00::/8` itself is refused and you name your own pod or Service CIDR instead. Entries are trimmed and de-duplicated, and a refusal names which entry it refused by position. Empty - the default, and where every organisation starts - is strict. API only today; the portal has no field for it. | | `ci_always_run_stages` | empty | Stages that run whatever their path filters, or their trigger's, say, together with every stage they need (at most 50 stage names). A pull request cannot filter them away, because this list lives here rather than in the repository. See [Setting path filters aside](/guides/pipeline-reference#setting-path-filters-aside). | | `ci_full_run_on_default_branch` | `false` | Run every push to a repository's default branch in full: no path filter applies there, and each such run records `organisation_policy` as why. | | `ci_skip_markers` | `except_default_branch` | Where a `[skip ci]` style marker in a pushed commit's message skips the run: `except_default_branch`, `everywhere` or `never`. A tag push, a pull request run and a merge train car never honour one. See [Skipping a push with \[skip ci\]](/guides/pipeline-reference#skipping-a-push-with-skip-ci). | Only the members you send are changed, so re-opening one private range is a single call that leaves the rest of the table alone: ```bash theme={null} curl -X PUT https://platform.ankra.app/api/v1/org/ci-settings \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"ci_egress_allowed_cidrs": ["10.42.0.0/16", "192.168.10.0/24"]}' ``` The pipeline *policy* (`/api/v1/org/pipeline-policy`, `pipelines.manage` for read and write) is the ceiling above these: allowed network tiers, credentials, node selectors, tolerations and runtime classes, maximum CPU, memory and GPU per step, the largest a declared cache volume may ask for (`max_cache_size`, which the route now stores and every declared cache is held to), the hosts a `webhook` stage may post to, and the external CI providers a pipeline may wait on. ### Capacity and concurrency How much pipeline work runs at once is decided by two organisation limits and one property of the cluster the steps run on. The narrowest one wins. | Limit | Bounds | Default | Range | Read it | Change it | | - | - | - | - | - | - | | `ci_max_parallel_runs` | Runs of the organisation in flight at once | `16` | 1 to 64 | `ankra org ci-settings get` | `ankra org ci-settings set --max-parallel-runs N` | | `ci_max_parallel_steps` | Steps of **one run** in flight at once | `8` | 1 to 256 | `ankra org ci-settings get` | `ankra org ci-settings set --max-parallel-steps N` | | `ci_worker_count` | CI slots: steps the CI cluster's agent runs at once, across every run on that cluster | `0` (pipelines off) | 0 to 128 (0 to 32 on older agents) | `ankra cluster agent ci get --cluster ` | `ankra cluster agent ci set --workers N --cluster ` | The two organisation limits need an organisation administrator, and the portal's **Pipelines** settings page edits both. The same page edits the pipeline cluster's CI slots and workspace storage class in its **Workers and step placement** card. The run limit is the one that caps throughput: every run past it waits as `queued`. The step limit only shapes how wide one run fans out. It is not an organisation-wide total (it was until 14 September 2026), so raising it never drains a queue of other runs. **A CI slot is not a node.** A slot is one step the agent on the CI cluster watches at once, from the moment it creates the step's Job until the step concludes. Nodes are a separate question, answered by the cluster's node-group autoscaler: when steps ask for more CPU and memory than the current nodes have free, it adds nodes, up to the group's autoscaling maximum. So the slot count is how many steps may be live, and the node group is whether there is room to place them. A cluster with plenty of slots and a node group at its maximum still queues, and so does a large node group behind a small slot count. **Changing the slot count.** An agent new enough to resize live applies a new `ci_worker_count` within a few seconds, without a restart, and takes up to 128 slots; `ankra cluster agent ci set` says it was applied live. An older agent takes up to 32, and a change re-renders its release instead: at once when it is online, at its next upgrade otherwise, and the command says which happened. Turning pipelines on or off (to or from `0`) always re-renders the agent. **Ankra does not over-fill a cluster.** Ankra stops handing a cluster steps once its agent has as many in flight as it has slots, so the extra steps wait in Ankra's own queue, where organisations are served fairly and the run can say why it is waiting. Time spent waiting for a slot is not time spent running: a step's timeout starts when the agent begins it, and the step's duration on the run page and in `ankra pipeline get` counts from the same moment. The run page shows the wait separately as the step's queue wait, and `ankra pipeline get` prints it beside the step. **Steps of one run share a node.** A run's workspace is one `rwo` volume (see [Limits and defaults](#limits-and-defaults)), so every step of one run, and the helpers that restore and save its caches and upload its artifacts, is pinned to the node that volume is attached to. A step that cannot fit there waits for its siblings to finish and is then queued again, rather than landing on another node and waiting for the volume to detach and re-attach. When the node is gone, cordoned or not Ready the pin is dropped and the step log says so; when a step does wait on a volume held by another node, its log names that node. That node's free CPU and memory against what each step reserves is a physical limit of its own: more nodes elsewhere do not widen one run, while a larger node does, and so does a stage that declares what it needs rather than a round number. When a run fans out, its first step prefers the emptiest node so its siblings have room beside it. A step reserves half of its `resources.cpu` and all of its `resources.memory` (250m and 1Gi when the stage sets no `resources`). The CPU figure is a ceiling the step grows into while its node has CPU free, so a lint step that declares two cores and uses a fraction of one does not keep a build off the node; when every step on a node is busy at once, the node's CPU is shared in proportion to what the stages declared and the steps run slower instead of waiting. Memory is different: a node cannot take memory back from a running step without killing it, so each step's memory is set aside in full, and on most nodes memory is what decides how many steps fit. A stage whose `memory` is padded costs you a step's worth of concurrency; one whose `memory` is too low is killed with exit status 137. **Sizing a stage from what it used.** Ankra records, for every step the agent runs, what its container asked for and what it used: the highest memory the kernel charged it, how often it met its memory limit, whether a process was killed for memory, its CPU time and how long it was held back at its CPU limit. `ankra pipeline get -o json` carries these per step (`memory_peak_bytes`, `cpu_usage_usec` and the rest), next to its requests (`memory_request_bytes`, `cpu_request_millicores`). The API's resource suggestions for a run (`GET .../pipeline-runs//resource-suggestions`) turn them into one suggestion per stage, from that stage's latest 20 successful attempts under the same pipeline definition: the 95th percentile plus 25% headroom, rounded up, and only once a stage has at least 5 attempts to go on. Each suggestion says how many attempts it rests on. Nothing is ever applied for you; change `resources` in your pipeline file if you agree. Two readings need care. A memory peak counts page cache, which the kernel uses to fill whatever room a step has, so a stage whose peak reached its limit is reported `at_limit` with no suggestion: its peak says it used the room, not that it needed it. CPU has no peak the kernel keeps, so the CPU suggestion is the request a stage should reserve, sized from the cores it used on average, written as the `resources.cpu` that reserves it; a stage that spent a tenth or more of its demand held at its limit is `at_limit` instead. Lowering `resources.cpu` lowers the ceiling too, so a stage that uses its CPU in bursts may run slower on the suggestion. **A step that cannot fit beside its run goes back to the queue.** When the node holding a run's workspace is full, a step that has to run there would otherwise sit waiting on that node while holding a CI slot. After a minute of that, the agent gives the slot back and Ankra returns the step to the queue; it is handed out again once the run's other live steps have finished and the node has room. The run page says how many times a step was returned to the queue, and `ankra pipeline get` prints the count. **A run can reserve its room before it starts.** With the run reservation on for a pipeline cluster (`ankra cluster agent ci set --run-reservation on --cluster `), Ankra works out from the plan how much CPU and memory the run's steps will need side by side at their peak. Before the first step starts, it asks the cluster to hold that much room on one node with a placeholder pod. The run's steps then take that room over as they start, so another run cannot fill the node between two steps. When no node has that much room free, the placeholder waits like any pod, and the cluster autoscaler can add a node for it. When the run concludes, the room is released. That has a cost. While a reserved run is in progress it keeps its room on the node even between steps, and that can **add a node** or **keep one from scaling down** that an unreserved run would have let go. `shadow` works out and records each run's reservation without holding any room, so you can compare the estimate with what the steps used before you turn it on. `off` reserves nothing. Each run's reservation is on its detail as `reservation` (mode, the estimated CPU and memory, and the peak its steps were observed to use), and the run page shows it under **Reservation**. **A step that can never be scheduled fails fast.** If no node the step may run on is large enough for what it asks for, and the autoscaler cannot add one that is, the step fails after about two minutes with the scheduler's own reason, instead of holding a slot until its timeout and reporting a timeout for a step that never ran. Lower the stage's `resources`, or add a node group with larger nodes. #### Why a run is waiting A queued run says which limit it is behind. The run page shows it under the run's status, `ankra pipeline get` prints a `Waiting:` line (CLI v0.17.0 or newer), and the run detail carries it as `queue_reason` with a sentence in `queue_reason_message`: | `queue_reason` | What it means | What moves it | | - | - | - | | `planning` | Ankra has not planned the run's steps yet. Clears within a few seconds | Nothing to do | | `waiting_on_concurrency_group` | Another run holds the run's concurrency group, and the message names it | `cancel_in_progress: true` (see [Newest commit wins](#newest-commit-wins)), or wait | | `waiting_on_run_cap` | The organisation already has `ci_max_parallel_runs` runs in flight | Raise `ci_max_parallel_runs` | | `waiting_on_step_cap` | The run already has `ci_max_parallel_steps` steps running | Raise `ci_max_parallel_steps` if one run should fan out wider | | `waiting_on_ci_workers` | The CI cluster's agent runs no pipeline steps, is offline, or is missing | The message names the fix, usually `ankra cluster agent ci set --workers N` | | `waiting_on_ci_slots` | Every CI slot on the cluster is taken; the message says how many slots it has and how many steps hold them | Raise `ci_worker_count`, and check the node group can grow to match | | `waiting_for_room_on_cluster` | With the run reservation on, the cluster caps how many runs it admits at once while their reservations are still being placed, and it is at that cap. This run has not been admitted yet, so it is still queued | It is admitted as earlier runs' reservations are placed | | `waiting_on_run_node` | The node the run's workspace lives on has no room for the run's next step | It starts when the run's other steps on that node finish | | `waiting_on_capacity` | The run's steps are queued behind other work on the cluster | They start as slots free up | Once a run's first step has started it shows no `Waiting:` line, whichever of these limits holds its next step back: its pending steps start as the limit frees up. A step that Ankra has handed to the agent and that is waiting for a free slot shows as waiting for a CI slot on the run page. **A run waiting for room for its reservation** has been admitted and is already running, so it carries no `queue_reason`. It says so in its own field instead. While the run's first step waits for a node with room for the run's reservation, the run page shows **Waiting for a node with room for this run**, with the room it is holding out for, how long it has waited, and what the autoscaler is doing about it (adding a node, at its node group's maximum, refused by the provider). `ankra pipeline get` prints the same sentence on its `Waiting:` line and a `Reserving:` line with the room and the wait. The run detail carries it as `reservation_wait` (`state` `waiting` or `returned_to_queue`, `since`, `waited_seconds`, the reserved `cpu_millicores` and `memory_bytes`, and the `message`). If no node has room within 10 minutes, the first step goes back to the queue and is sent again, and the state reads `returned_to_queue` with the count. When Ankra could not read the cluster, `reservation_wait_unavailable` says so, and while it is set the wait is unknown: a missing `reservation_wait` then does not mean nothing is holding the run back. The step itself carries the diagnosis `run_reservation_pending` (see [Diagnosis codes](#diagnosis-codes)). #### Seeing and raising capacity The **Capacity** panel on the portal's **Pipelines** settings page shows, refreshed every 15 seconds: the CI slots on your pipeline cluster, the steps in flight there, your organisation's queued runs, its runs in flight against `ci_max_parallel_runs`, its pending steps, and how much of its work is waiting for a CI slot. It also names the limit that is full. To raise capacity, change the limit that is full: * **Runs queue as `waiting_on_run_cap`.** `ankra org ci-settings set --max-parallel-runs N` (up to 64). * **Runs queue as `waiting_on_ci_slots`.** `ankra cluster agent ci set --workers N --cluster ` (up to 128 on current agents). * **Steps are slow to start although slots are free.** The cluster has no room for the pods: raise the node group's autoscaling maximum (`ankra cluster node-group autoscaling get` shows it), or use larger nodes. More slots without more nodes only means more steps waiting for a node. The panel also says what is stopping each CI cluster from running your waiting steps, as a code and the one thing to do about it: a node the cloud provider refused for a quota, a node group at its maximum, no node type big enough for the step, an image the registry refused. `ankra org ci-settings get -o json` carries the same list per pool member as `scale_up_blockers`, with `scale_up_blockers_state` saying whether an empty list means nothing is wrong (`none_observed`) or the cluster's events could not be read (`unknown`). The codes are listed in [Diagnosis codes](#diagnosis-codes). Ankra also watches every pipeline cluster for you. When a cluster's steps in flight have stayed at or above its CI slots for fifteen minutes, Ankra raises a saturation alert for that cluster: pipelines there are queueing for slots while everything else reads healthy. The remedy is the same as above, more slots, or a larger node group maximum when the nodes are what is full. ### Step placement By default a pipeline pod runs on any node of the CI cluster that will take it. To keep CI on nodes of its own, for example a dedicated node group tainted so nothing else lands there, give the cluster a **step placement**: ```bash theme={null} ankra cluster agent ci set --cluster so-upcloud-infrastructure --node-group pipelines ``` Every pipeline pod of that cluster then carries it: the step pods, their cache restore and save helpers, the artifact uploader, and image builds in `ankra-ci-build`. Naming a node group is enough on a cluster Ankra built: the pods select the group's nodes by the `ankra.cloud/node-group` label every node Ankra spawns carries (or by the labels the group declares, on a group from before that label), and tolerate every taint the group has. A group the cluster does not have is refused, and the refusal lists the groups it does have. On a cluster without Ankra node groups, name the nodes yourself: ```bash theme={null} ankra cluster agent ci set --node-selector pool=ci --toleration dedicated=ci:NoSchedule ``` `--node-selector key=value` and `--toleration key[=value]:Effect` repeat, and add to `--node-group` when you give both. A toleration without a value tolerates any value of the key; the effect is `NoSchedule`, `PreferNoSchedule` or `NoExecute`. | Mode | What the pods do | | - | - | | `required` (default) | Run only on nodes carrying the selector. When none has room they wait, and the group's autoscaler adds a node if it can. | | `preferred` | Favour those nodes, and run elsewhere when they have no room. The tolerations are carried either way, so the group's nodes stay open to them. | Set the mode with `--placement required|preferred`, and remove the placement with `--clear-placement`. A placement flag replaces the whole placement; the worker count and storage class are left alone, and `--workers` is not needed to change only the placement. The change applies to the next step Ankra sends the cluster, with no agent restart. `ankra cluster agent ci get` prints the placement and how many Ready nodes it admits right now, so a placement no node matches is visible before a run waits on it. The portal's **Workers and step placement** card on the **Pipelines** settings page edits the same setting, with the cluster's node groups to choose from. The placement is set by whoever may manage the cluster's agent, outside any pipeline definition, so it needs no [authority approval](#logic-is-open-authority-is-closed) and is not held to the pipeline policy's node selector and toleration allow-lists - those bound what a pipeline's own `runs_on` may ask for. A stage's `runs_on` still applies on top of it: its node selector is merged with a `required` placement's, and a stage asking for a different value of a key the placement sets is refused before it runs, naming both. On a cluster where a Kyverno or Gatekeeper policy mutated pipeline pods onto a dedicated group, the placement replaces that policy, and unlike it also covers the helper pods. To create such a group, `ankra cluster node-group add --name pipelines --instance-type --taints dedicated=ci:NoSchedule` taints it from its first node. An agent that predates the placement ignores it and places pods by each stage's own `runs_on` alone, so upgrade the cluster's agent (`ankra cluster agent upgrade`) before relying on it. ### Ankra platform builders Ankra platform builders are the Ankra-operated build lane a `build` step falls back to when the pipeline cluster cannot build it. The fallback covers two causes, and nothing else: * **The cluster's agent does not run pipeline steps.** It predates the capability, or no agent has checked in at all. Every stage of the run is affected. `build` runs on Ankra platform builders, the `checkout` in front of it is settled for that build, and the run's `scan` stages run on Ankra's platform scanner (below). Stages that need your cluster, such as your own tests and deploys, are still refused, with a sentence naming the agent to upgrade. * **The cluster's node runtime confines the rootless image builder.** Typical for AppArmor on k3s or Ubuntu nodes, and for a kubelet that defaults seccomp to `RuntimeDefault`. Every other stage runs in-cluster as normal and only `build` is routed away; [`build_runtime_confined`](#when-something-fails) has the mechanics. Three things have to hold for the fallback to build: `ci_build_fallback` reads `platform_builders` (the default), platform builds are on for the organisation (the default), and the stage is one the lane can accept (below). With platform builds switched off, the step concludes `build_fallback_none` with a sentence saying Ankra's platform-operated build fallback is not enabled for this organisation yet, whatever the setting says. **Confirm the grant.** `ankra org ci-settings get` prints `Platform builds enabled: yes` or `no` (CLI v0.17.0 or newer; `-o json` carries it as `platform_builds_enabled`), and when the answer is `no` while the fallback reads `platform_builders`, the command says the grant is what is missing, not the setting. `GET /api/v1/org/ci-settings` returns the same field as `platform_builds_enabled`. Any member of the organisation can read it; nobody in the organisation can write it. **If the grant reads no.** Platform builds are on for every organisation unless Ankra has switched them off for yours. [Open a support ticket](/platform/support) naming the organisation and the pipeline cluster (`ci_cluster_id`, or the cluster's name) whose builds should fall back, and the next run uses the grant once it is back on, with no change on your side. **Sharing the pool.** Ankra platform builders are shared by every organisation, so one organisation runs at most three builds on them at a time. A fallback build beyond that waits in order and starts when one of your running builds finishes; it is not failed and does not use the step's retry. [Waiting for a builder](/concepts/applications#waiting-for-a-builder) has the details. **Cost.** Platform builds carry no charge. Nothing about the grant or the setting changes your billing: no usage is metered for a build that runs on Ankra platform builders, and a build that runs in-cluster costs what your cluster already costs. **What you see during a fallback build.** The step's `executor` records `platform_builders`, and the run's step table shows the step running on Ankra platform builders rather than on your cluster. There is no live log stream to tail, because the step was never dispatched to an agent: `ankra pipeline logs --follow` has nothing to attach to while the build runs, and the archived log appears when the build concludes. The image is pushed by digest to the organisation's CI staging repository, exactly where an in-cluster build pushes, so the pipeline's own `scan`, `gate` and `publish` steps still decide whether it is promoted. **Scans of a run handed to the builders.** When the whole run went to Ankra platform builders because the cluster's agent does not run pipeline steps, its `scan` stages run on Ankra's platform scanner instead of being skipped. Each scanner (Semgrep, Checkov and Trivy) runs the same pinned image and the same script it runs in your cluster, against the commit and the image the platform builders built. Its reports are recorded exactly as an in-cluster scan's are. The `gate` then judges them under the same image gate policy and the same evidence rules, and `publish` proceeds on the gate's verdict alone. A fallback image is held to the same bar as one your cluster built. The scan step's `executor` records `platform_builders`, and its message says why it ran there. A scan the platform scanner cannot run concludes `infra_error` with a sentence naming why. That happens when no scanner is available (`platform_scanner_unavailable`), when the scan stopped reporting, or when the organisation has nowhere to keep the reports. The scan is never skipped, so the gate never approves an image nobody scanned, nothing is published, and the run is not green. Start the run again once the scanner is back, or upgrade the cluster's agent with `ankra cluster agent upgrade --cluster ` so the whole pipeline runs in-cluster. **What the lane refuses.** Ankra platform builders build what the in-cluster pod would have, from the same stage, but two things about a stage's layout have to be right, or the step concludes without a build and its sentence names the fix: * In a multi-component (monorepo) application, a build stage must belong to a component the application records: either it declares `build.component`, or it is called `build-` ([which component a build belongs to](#which-component-a-build-belongs-to)). A stage that does neither, or that declares a component the application does not record (a typo, or a component renamed since the run was planned), is refused as `platform_build_unavailable`, and the message lists the components the application knows. A single-component application's build stage may be called anything. A repository with no linked application is refused the same way, because it has nowhere to push whoever builds it. * Every `build_args` entry must carry a literal value, written `NAME=value`. In-cluster, a bare `NAME` lets BuildKit read the argument from the build pod's own environment; the platform builder has no such environment, so a bare entry is refused as `build_fallback_unsupported` rather than built silently without the argument. ## Logic is open, authority is closed Anyone who can push can change what a pipeline *does*: add stages, rewrite scripts, pick any image the organisation's policy allows, change conditions, matrices, `needs`, caches, artifacts and timeouts. What a run may *touch* is not the pull request's to decide. These sections are protected - they are resolved from the organisation's policy and the definition on the default branch, never from a pull request's copy of the file: * `permissions` - the API scopes the run identity is minted with. * `secrets` and `credentials` - each declared binding, by name, source and key. * `on.pull_request.fork_policy`. * `defaults.network` and a stage's `network` when it is above `egress-https`. * `runs_on` on every stage - cluster, node selector, tolerations, runtime class - and the whole of `resources`: `gpu`, `cpu` and `memory`, so a head cannot ask for far more compute than the organisation meant to grant. * A stage's `timeout`, so a head cannot remove its own or the trusted definition's and let a step run far past what was approved. * A stage's `cache` block - `key`, `paths`, `size`, `restore_keys` and `fallback` - which decides whose earlier output is restored into this run's workspace. * A stage's `with:` block, which is where a `gate` stage's own scan-tool configuration lives. * The `services` block - each sidecar's image, environment and ports. * A `gate` stage's approval roles and required stages, an `approval` stage's roles and minimum approvals, an `agent` stage in `agent` mode and its tool profile, a `webhook` stage's URL, an `external` stage's provider and workflow. * The `environments` block - data policy, verification, rollout and gate per environment. A stage the default-branch definition does not have gets no authority at all: adding a stage is free, adding a privileged stage is impossible. Every run plans under one trusted authority, resolved in a fixed order: the default branch's own definition, once it declares no protected section at all (there is nothing to approve) or an administrator has approved that exact protected hash; failing that, the newest authority an administrator ever did approve on this repository; failing that, none. A pull request's *logic* always executes as written - its stages, scripts and images run - but its *authority* is whichever of those three the resolution landed on, never the pull request's own copy: * **`approved`** - the head declares exactly the trusted authority. An ordinary pull request that changed no protected section reads this way. * **`unapproved`** - a push to the default branch itself changed a protected section nobody has approved yet; the run executes under the last approved authority (or none) until an administrator does. * **`changed_on_head`** - a pull request added a credential, widened its egress, or moved a step onto another node pool. The change is ignored for this run and surfaced, never silently granted because a commit landed. The check run's summary and the commit status description lead with the authority sentence whenever a run is not `approved`, naming what changed and what to do about it; `GET /org/pipelines/definitions/{id}` reports the definition's `protected_hash` and whether it is the currently approved one. An administrator approves a repository's current protected sections with: ```bash theme={null} curl -X POST https://platform.ankra.app/api/v1/org/pipelines/definitions//approve \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" ``` Needs `pipelines.manage` and a human actor - a service token cannot approve. Answers `409` rather than silently agreeing when the definition is no longer the repository's current default-branch definition (a later push replaced it) or is already approved. `POST /org/applications/{id}/pipeline/approve-definition` is the same act addressed by the application whose repository owns the definition, for a caller who holds the application's id and not the definition's; it resolves that address form and then runs exactly the checks above, mapping their three refusals onto the same `403` and `409`. `ankra pipeline definitions get ` and `ankra pipeline definitions approve ` are the CLI form of the same two acts, and are the ones the platform's own call to action names when a run executes under anything but plainly approved authority. The portal approves the same way, from **Settings → Pipelines → Pending approvals** or the run page, and so does the **Approve authority** button on the GitHub check run. Narrowing the image allow-list afterwards does not invalidate an approval, because the policy is applied at plan time and is not part of the hash. > [!WARNING] > > **This release moves every definition's protected hash, so re-approve.** `services`, `cache`, `resources.cpu`/`resources.memory`, `timeout` and a stage's `with:` became protected in the same deploy, so the hash is now computed over more of the document and moves for every definition - including ones whose content did not change. Three consequences, all deliberate: > > * **Existing approvals keep applying, and still need re-approving.** A stored approval is read from the approval columns rather than re-derived, so it keeps granting authority and its own content supplies the trusted values for the newly protected sections too - an administrator did approve a committed file, and those sections were in the file they read. But the stored hash no longer describes what it fingerprints, and nothing backfills it, because re-stamping would assert an administrator reviewed five sections they have never seen. Approve the repository's current default-branch definition again to make the column true. > * **A run planned before the deploy and dispatched after it refuses.** The dispatcher re-derives the trusted definition's hash and compares it against the hash the run recorded when it was planned, so a run whose steps were still pending across the restart concludes with an authority-hash mismatch and has to be re-run. The window is one restart wide; no compatibility shim carries the old form, deliberately, because the form will move again as more sections become authority. > * **An old approval whose proving runs were pruned stops counting.** The last-approved fallback answers from the run history, so an approval with no surviving default-branch run to prove it is no longer a candidate. It fails closed and says so rather than silently: the run resolves to the baseline and its own status carries "No administrator has approved any authority for this repository yet" with the approve action beside it. Re-approving fixes it, because only the current default-branch definition can be approved and that definition is the newest default-branch run's own. ### What a step runs as Every step is a hardened batch/v1 Job in the `ankra-ci` namespace, which enforces the Pod Security Standards' Restricted profile: uid and gid 65532, the `RuntimeDefault` seccomp profile, no privilege escalation, a read-only root filesystem, every capability dropped, no service account token, no service links, one attempt, an explicit deadline and a TTL. A pipeline cannot ask for host privilege, host namespaces, host paths or control-plane placement; the request is refused at plan time. The one relaxation is the rootless BuildKit pod a `build` stage runs, in its own `ankra-ci-build` namespace under the Baseline profile - never privileged, never root, and no pipeline can name its own build or checkout image: both are Ankra's own, pinned by digest. That build pod names no seccomp or AppArmor profile at all, which is the only spelling of "leave the builder alone" Baseline admits, and nothing can change it to `Unconfined`. On nodes whose runtime reads that as a confining default the builder cannot start, and the build is routed to Ankra's own builders instead ([`build_runtime_confined`](#when-something-fails)). With the root filesystem read-only, a step can write to three places, and they differ in size and lifetime: * **`/workspace`** is the run's shared volume: the checkout lands there, and it carries files from one step to the next. Anything large belongs here. * **`/tmp`** is a 256 MiB memory-backed tmpfs, private to the step's pod and empty again at the next step. It counts against the step's memory, and a compiler or linker that stages intermediate files in `/tmp` runs out of space long before the workspace would. Point `TMPDIR` into the workspace for anything that writes big temporary files, with `export TMPDIR=/workspace/.tmp && mkdir -p "$TMPDIR"` at the top of the step's `run` script. * **`/dev/shm`** is 64 MiB unless the stage sets `shm_size`. uid 65532 has no `/etc/passwd` entry in most images, so every step gets `USER` and `LOGNAME` set to `ankra`, which is what lets `getpass.getuser()`, git and similar lookups resolve. `HOME` is `/tmp`, so a tool that writes a per-user config there works, but the 256 MiB limit above applies to it: point package caches and build output at `/workspace` instead. `HOME` and `TMPDIR` cannot be set in a stage's `env` (see reserved names below), so export them in the `run` script when a tool needs them elsewhere, where the change is visible in the step you wrote. Beyond containment: * **Network tiers.** `none` denies all egress, and is what a stage gets when neither it nor the pipeline's `defaults` names a tier - except for `checkout`, `build` and `scan`, whose only job is outside the cluster and which default to `egress-https` instead ([the tier a stage gets by default](/guides/pipeline-reference#the-tier-a-stage-gets-by-default)). `egress-https` permits 53/UDP and 53/TCP to the cluster's own DNS - the `kube-system` namespace, plus `169.254.0.0/16` for a cluster running NodeLocal DNSCache - and 443/TCP to public addresses only: RFC1918, `169.254.0.0/16`, `100.64.0.0/10`, `fd00::/8`, `fe80::/10` and AWS's `fd00:ec2::254` are all excluded, so a step on it reaches neither your private networks nor any cloud metadata endpoint that hands out the node's identity. A `checkout`, `build` or `scan` stage that explicitly declares `network: "none"` is honoured rather than widened back - the tier is clamped only on a restricted fork run (below) - but the validator warns what that stage loses, because a step with no way to reach its source, its base images or its vulnerability database is not one broken step, it is a pipeline that cannot run at all. `services` is deny-all plus the run's own pods and that same DNS rule; the sidecars a step declares are containers in its own pod, reached over loopback by name. A private destination a pipeline legitimately needs is named in [`ci_egress_allowed_cidrs`](#organisation-ci-settings), which renders as an extra peer on the 443 rule of the `egress-https` tier alone, is re-validated when the step is rendered rather than trusted from the settings row (an entry that fails that check is dropped and the rest of the list is still honoured), and rides on the step's payload so the agent renders the same policy. The **agent** is what renders that policy in your cluster, so until the agent release of 5 September 2026 reaches it, a step still gets the previous tier - 443 to every address but `169.254.169.254`, and 53 to any pod. * **Image policy.** A `run` or `scan` stage's image is checked against `ci_allowed_image_prefixes` on the platform and again by the agent before anything runs; `checkout` and `build` always run Ankra's own pinned images regardless of the organisation's list. A named service's image goes through the same check, and a sidecar outside the policy refuses the whole step rather than running beside it. The **platform's** check is the one matched at a path boundary, and an entry ending in `/`, `:` or `@` admits exactly that boundary rather than matching nothing (above). The agent's own fallback check is still a plain textual prefix match, so a neighbouring registry path can satisfy it and a separator-terminated entry is read as plain text rather than as the boundary it names, until the agent release of 5 September 2026 reaches your cluster. * **Resources.** A stage's `cpu` and `memory` are the step's limits. The step reserves half of the CPU (never under 100m, or the whole figure when the stage declares 100m or less) and all of the memory on its node, so it can use CPU up to its ceiling when the node has it free and can never take memory another step was given. It may ask for at most 8 CPU cores, 32 GiB of memory and 4 GPU devices, and a GPU step must name a `runs_on.node_selector`, so it is refused here rather than left Pending until it times out. * **Reserved names.** A step may not set environment variables beginning `ANKRA_`, the loader and `PATH` variables, `HOME`, `TMPDIR`, `XDG_RUNTIME_DIR` and `XDG_CACHE_HOME`, or the image builder's own `BUILDKIT*`, `BUILDCTL`, `ROOTLESSKIT` and `DOCKER_CONFIG`. A service the step names may not set them either - those variables decide what a container actually executes, and a sidecar's process is no exception. * **Secrets and credentials.** A declared binding is revealed once, to a file under `/run/agent-secrets` named after the binding, by the agent calling back with its own session token - never written into the step's payload or a log line. A `checkout` step's own git credential is granted the same way and read by a credential helper that answers only for the host it was minted for, so a malicious submodule URL cannot walk off with it. * **Forks.** A pull request whose head lives in a fork - or whose provenance the delivery could not establish - runs with no secrets, no credentials and no identity beyond reading itself (`fork_policy: read_only`, the default). `checkout` and `build` both clone the fork's own head anonymously - never the pipeline's own repository - reading the head repository the trigger recorded on the run; `checkout` refuses outright, rather than guessing a remote, on the rare delivery whose fork head repository could not be established at all. `build` still runs (so a maintainer can see whether a contributor's change compiles) but renders with no push target, so nothing it produces can reach a registry; `scan` may still pull the image it built to report on, never a private one. Every stage but `checkout`, `build` and `scan` runs with no network at all, regardless of what the fork's own copy of the definition asks for - the tier is clamped, not merely left alone, so a fork-controlled `network: egress-https` on some other stage is not honoured just because these three need it. Beyond what such a run may *touch*, it is bounded in what it may *spend*: at most 8 matrix legs per stage, a 60-minute step timeout, 2 CPU and 4Gi per step, no sidecar services, no shared cache volumes, and 256 MiB of artifacts and logs for the whole run. The first four are imposed when the run is planned, so the plan a reviewer reads is the run that executes; caches are withheld at dispatch, and the artifact budget bounds what the run is granted from its declared sizes (the same ceiling on what actually arrived is in the artifact store, waiting on the settle lane that will call it). A `plan_fork_budget:` diagnostic names the ceilings, and one more diagnostic names each stage that hit one. * **Permissions.** `pipelines.read` covers listing and reading runs, logs, artifacts, the stored definition, schedules and validation; `pipelines.operate` covers dispatch, cancel and re-run; `pipelines.manage` covers connecting a repository, replacing the stored definition, creating, changing and deleting schedules, the pipeline policy and its approvals, and a dispatch with a `--spec-file` override. ## When something fails Every failed step carries an error class, and the class decides whose failure it is. * **Yours, never retried automatically.** `step_failed` (the script exited non-zero), `step_refused` (the definition asked for something the policy or the renderer refuses - an image outside the allow-list, a missing script, too much compute, or a `build` step whose push target cannot be resolved: no application link, a component with no resolvable registry, or a stage that belongs to no component of the application), and `timeout`. A step that ran out of time did run; running it again is a retry policy to declare. The `retry:` block is validated and carried to the agent but not applied yet, so today a non-zero exit is final. * **Ankra's, retried once.** `agent_infra` (the cluster could not create the namespace, bind the volume or pull the image), `worker_lost` (the agent claimed the step and stopped reporting), `result_malformed`, `agent_offline`, `agent_never_started` (no agent claimed the step inside its timeout - check that the cluster is connected and `ci_worker_count` is above zero), `build_runtime_confined` (below), `execution_vanished` and `stranded`. Each of these writes the next attempt instead of a conclusion, up to two attempts in total; the second failure concludes the step as `infra_error`, and the check run settles as `action_required` naming the class - Ankra failing never reads as your code failing. * **`build_runtime_confined`: your nodes confine the image builder, and no setting relaxes it.** A `build` stage runs rootless BuildKit in a pod that deliberately names **no** seccomp or AppArmor profile, because the Pod Security Baseline profile the `ankra-ci-build` namespace enforces forbids `Unconfined` - naming it would leave the pod refused by the very namespace admitting it. On a node whose runtime resolves "names none" to a *confining* profile - containerd on an Ubuntu host applying its default AppArmor profile, or a kubelet with seccomp defaulting applying `RuntimeDefault` - rootlesskit cannot make its mount namespace private, and the builder dies before BuildKit starts and before anything in your Dockerfile is read. The pod exits non-zero, so the agent reports it as your failure; it is not, and the settle loop re-classes it. **There is no cluster, organisation or pipeline setting that renders the build pod `Unconfined`**, and that is by design rather than an omission - nothing about a pod spec can defeat the node's own default from inside Baseline. The supported answer is [Ankra platform builders](#ankra-platform-builders), which takes two things: `ci_build_fallback: platform_builders`, which is the default, and the platform builds grant, which Ankra makes per organisation on request (that section says how to confirm it and how to ask). With both, the retry, and every later build on that cluster, is routed to Ankra platform builders and the step's `executor` records it. The verdict is remembered per cluster against the agent version and builder image it was observed under, so a cluster pays this failure once rather than on every run - and re-probes by itself when either moves. With the fallback set to `none` the build is refused instead, naming the containment and telling you to set the fallback or run the pipeline on a cluster whose runtime leaves the builder unconfined. With the fallback already `platform_builders` but the grant not yet made, the retry concludes `build_fallback_none` with a sentence saying Ankra's platform-operated build fallback is not enabled for this organisation yet: no setting of yours changes that, so ask Ankra for the grant, then re-run. Every other stage kind is unaffected: `checkout`, `test`, `scan` and `gate` run in-cluster on a confining node exactly as anywhere else. * **Ankra's, never retried.** `step_stranded` - a step still `pending` after its run has outlived the sum of every step's own `timeout` (capped at the same 6-hour ceiling one step is held to, plus a grace window). It concludes as `infra_error` with a sentence saying it never started and pointing at the cluster's agent and its `ci_worker_count`. A fresh attempt is not written, because it would face the identical exhausted budget; the value of the sweep is that the run concludes rather than sitting open forever. `report_unreadable` joins it: a `scan` step that concluded **successfully** but whose report Ankra could not download or parse is revised to `infra_error` carrying "Ankra could not read the *``* report this step produced, so its findings were not recorded." Ankra failing to read what a scanner said is not the same fact as the scanner having passed, so it is never recorded as clean. `report_unreadable` used to be what an organisation with no backup vault got on every scan step it ran; now that such an organisation's artifacts go to the [Ankra-managed default vault](#before-your-first-run), it means what it says - the object was never uploaded, or came back unparseable. The remaining causes are a run that had already spent its 2 GiB artifact budget before the report was due, and a vault whose bucket stopped answering. With no ready vault on either side, a `scan` step is refused before it runs instead, with error class `artifact_store_unavailable` and the sentence "This step's reports need a ready backup vault in the organisation, and there is none." A step that had already failed, timed out or hit `infra_error` keeps its own verdict - that is a more specific answer than this one. * **Timeouts.** A stage runs under its `timeout`, or `defaults.timeout`, or 30 minutes; the ceiling is 6 hours and a longer value is clamped and recorded. The agent stops a step at its deadline with a 60-second grace, and a reaper on the platform sweeps every 30 seconds for steps two watchdogs missed - a step five minutes past its deadline, an agent that has not checked in for ten. The run itself is heartbeated while any of this is going on, so a long pipeline is not failed by the platform's own stale-run reaper for taking longer than twenty minutes. * **Cancel.** `ankra pipeline cancel `, `POST …/pipeline-runs/{run_id}/cancel`, or the **Cancel** button on the check run while the run is live. A run that is cancelled concludes with outcome `cancelled`. * **Re-run.** `ankra pipeline rerun ` (add `--failed-only` to re-run only the steps that did not succeed, whatever depends on them, and the steps they build on), `POST …/pipeline-runs/{run_id}/rerun`, or GitHub's own **Re-run** button on the check. A re-run is a new run with `rerun_of_run_id` pointing at the old one; the old run's outcome stays the record of what happened. It re-plans from the **original run's own definition**, not from whatever the pipeline has become since, so re-running a month-old run reproduces it rather than quietly executing a different pipeline. * **Failed-only re-runs start from an empty workspace.** A re-run gets a workspace of its own - the original's is removed once it concludes - so `--failed-only` re-runs the checkout and every upstream step whose output a repeated step reads (through `needs`, and for a `gate` the stages in its `require_stages`) together with the failed steps and their dependents. Steps that succeeded and that nothing repeated needs, such as a publish after the failure, are not repeated. A failed-only re-run that could not run a step that failed - a stage limited by `when.events` to `push`, which a re-run's `rerun` trigger filters out - is refused with the reason for each such step, rather than queued to pass without it. ### Diagnosis codes A step that failed for a reason Ankra recognises, or that has waited more than two minutes for its pod to start, carries a `diagnosis`: a stable `code`, a `title`, a `next_step` and, where there is one, the cluster's or the provider's own words in `detail` (cleaned of hidden characters and anything that looks like a credential, and cut to 300 characters). The run carries the one a reader should act on first in its own `diagnosis`, and the run page shows it above the steps. A step that failed in its own script has no diagnosis, because its log is the answer. A running step whose cluster events could not be read says so in `diagnosis_unavailable` rather than showing nothing. Ankra reads the evidence it already has, so nothing extra runs on your cluster: the step's error class and the agent's message, the Kubernetes events on the step's pod and its workspace volume, the Cluster Autoscaler's events, and, for node groups Ankra scales, its own record of the node create the provider refused. It reads only events of clusters your organisation owns. **Why a step, or a CI cluster, is waiting** | `code` | What it means | What to do | | - | - | - | | `provider_quota_exceeded` | The cloud provider refused a new node for a project or account limit, such as Hetzner's `403 resource_limit_exceeded: shared core limit exceeded`, an AWS vCPU limit or a GCP or Azure quota | Raise the limit with the provider. On Hetzner, open the Cloud console, the account menu, **Limits**, **Request change**; the `next_step` field names the shared or dedicated vCPU or server limit. Or add another CI cluster to the pool with `ankra org ci-settings pool add ` | | `provider_volume_quota_exceeded` | The provider refused the step's workspace or cache volume for a volume limit | Delete detached volumes nothing uses, raise the volume limit, or add a pool member | | `scale_up_failed` | The autoscaler asked for a node and it was not added, for a reason that is not a known quota | Check the CI cluster's node groups; the autoscaler tries again after its back-off. Or add a pool member | | `node_group_at_max` | Every node group that could run the step is at its maximum size | Raise the node group's autoscaling maximum, or add a pool member | | `no_node_group_fits` | No node group's node type has enough CPU or memory for the step, so adding nodes would not help | Lower the stage's `resources`, or add a node group with a larger node type | | `insufficient_capacity` | No node has room for the step and nothing is adding one | Enable autoscaling on the CI node group, add nodes, or add a pool member | | `run_node_full` | The step shares the run's workspace volume, so it must run on the node that holds it, and that node is full | It starts when the run's other steps finish. Lower the stage's `resources` to fit more steps on one node | | `image_pull_auth` | The registry refused the step's image to the cluster's credentials | Check the image name, then add the registry's credential to the organisation, or use a public image | | `image_pull_failed` | The cluster could not pull the image for another reason | Check that the image name and tag exist and that the registry is reachable from the cluster | | `volume_unavailable` | The step's volume could not be provisioned, attached or mounted | Check the CI cluster's StorageClass and CSI driver. A volume still detaching from another node clears on its own | | `scale_up_in_progress` | The autoscaler is adding a node for the step | Nothing; a new node usually joins in one to three minutes | | `run_reservation_pending` | The run's first step is waiting for a node with room for the whole run's reservation (the run reservation is on for the cluster); the `next_step` names the room and says whether a node is being added | Nothing while a node is being added. If the node group is at its maximum or the provider refused a node, the same remedy as `node_group_at_max` or the quota codes. To stop holding room, `ankra cluster agent ci set --run-reservation off` | **Why a step or run failed** | `code` | What it means | What to do | | - | - | - | | `out_of_memory` | The kernel killed the step at its memory limit; the `next_step` field names the limit | Raise the stage's `resources.memory`, or make the step use less. When the run did not take the stage's resources because the definition is not approved, approve it instead | | `stage_exceeds_node_size` | The stage asks for more CPU or memory than any node of the CI cluster offers, so it can never start | Lower the stage's `resources`, or add a node group with a larger node type | | `build_runtime_confined` | The cluster's node runtime stops the rootless image builder (see [When something fails](#when-something-fails)) | Keep `--build-fallback platform_builders`, or build on a cluster whose runtime allows the builder | | `image_pull_auth`, `image_pull_failed` | As above, for a step that gave up on its image | As above | | `deadline_exceeded` | The step ran longer than its `timeout` | Raise the stage's `timeout`, or make the step faster; caches help most | | `deadline_exceeded_waiting` | The step's whole time budget went on waiting for its pod to start; when the agent's message quotes the cluster's reason, the `next_step` field names what held it | Fix that (see the waiting codes), then run the pipeline again | | `no_ci_cluster` | The organisation has no CI cluster | `ankra org ci-settings set --cluster `, then run again | | `definition_needs_approval` | An administrator has not approved the pipeline definition, so the run did not execute its protected sections as written | Approve it on the run page or with `ankra pipeline definitions approve `, then run again | **A notice on a passing step** | `code` | What it means | What to do | | - | - | - | | `cache_volume_fallback` | The step's caches ran without a persistent volume, so the next run starts cold. Its `category` is `notice`, not `failure` | Give the CI cluster a default StorageClass. Runs from forks and tags never share caches, by design | The codes are a fixed vocabulary that monitoring and scripts can key on; the sentences may be reworded. ### Status is the lifecycle, outcome is the verdict A run and each of its steps carry two fields, and only one of them says how the work ended. `status` is where the work is in its life; `outcome` is how it finished, and it is `null` until `status` is `concluded`. This is the same split as GitHub Actions' `status` / `conclusion`: a monitor that filters steps on `status == "failure"` matches nothing, because every finished step is `concluded` - read `outcome` for the verdict. | Field | On a run | On a step | | - | - | - | | `status` | `queued` (created, no step has started), `running` (work in flight), `concluded` (finished; always carries an outcome) | `blocked` (waiting on its dependencies), `pending` (dependencies satisfied, not yet claimed by a worker), `running` (claimed and started), `concluded` (finished; always carries an outcome) | | `outcome` | `null` until `concluded`, then one of the values below | `null` until `concluded`, then one of the same values | The outcome vocabulary is one set for runs and steps alike: | `outcome` | Meaning | | - | - | | `success` | The work did what was asked | | `failure` | Your own work failed - a non-zero exit, a refused definition, a gate that blocked. The error class says which | | `cancelled` | Stopped on purpose - `ankra pipeline cancel`, the API, or the check run's **Cancel** button | | `timed_out` | The work exceeded its `timeout` | | `skipped` | The work never ran: a dependency did not succeed, an `if:` excluded it, or - for a run - the trigger's branch, tag or path filter excluded every stage | | `infra_error` | Ankra failed, not your work - a retried `agent_infra` or `worker_lost` that failed twice, a `step_stranded`, a `report_unreadable` | A run's outcome folds its steps' outcomes: `success` when every step succeeded or was skipped, otherwise the worst of what its steps reported. `ankra pipeline get ` prints the outcome in place of the status once there is one, with a glyph per state - `✓ success`, `✗ failure` / `timed_out` / `infra_error`, `⊘ cancelled`, `○ skipped`, and `⟳` only for a run or step that has not concluded (`○` for a blocked step) - and `-o json` carries both fields verbatim. A run also records the protected authority it executed under, as `authority_state` (the `Authority:` line in `ankra pipeline get`): | `authority_state` | Meaning | | - | - | | `approved` | The run executes the authority its head declares: the default-branch definition either declares no protected section, or an administrator approved exactly what it declares, and the head changes nothing | | `unapproved` | A run of the default branch whose definition changed protected authority no administrator has approved yet. It executes under the last approved authority - or under none, when nothing was ever approved - until one does | | `changed_on_head` | A run of a head that is not the default branch (a pull request, another branch) whose definition declares different authority. It executes under the default branch's; the head's change is ignored and surfaced on the check run and the pull request comment | | `null` | The planner never resolved authority for this run: it is still `queued`, it concluded `skipped` because the trigger filter excluded every stage before planning ever ran, or it was planned before authority tracking existed. `null` means "not recorded", never "no authority" | `authority_hash` and `authority_definition_id` sit beside it and are `null` in the same cases. See [Logic is open, authority is closed](#logic-is-open-authority-is-closed) for what each state means for the run's protected sections. ## Limits and defaults | Limit | Value | | - | - | | Step timeout | 30 minutes when neither the stage nor `defaults` sets one; at most 6 hours, longer values are clamped | | Automatic retry | One, for Ankra's own failure classes only; two attempts in total | | User-declared retry | `retry.max_attempts` at most 5; not applied yet | | Matrix | At most 64 legs per stage after exclusions; a larger matrix is refused, not truncated | | Step compute | Default 500m CPU and 1Gi memory; at most 8 cores, 32 GiB and 4 GPUs. A step reserves half its CPU (at least 100m, or the whole figure when that is 100m or less) and all of its memory, and may use CPU up to the declared figure | | `shm_size` | 64Mi unless the stage sets it | | Workspace | One volume per run, shared by every step; `rwo` pins every step of the run to the node its volume is attached to. `rwx` is refused at dispatch today: the claim must name a StorageClass that serves it, and nothing sets one yet | | Caches | Each cache path is kept per repository and trust scope, restored before a step and saved after it succeeds; at most 4 GiB per saved path and 10 GiB per repository, least recently used first. On an agent without cache archives, one 5Gi volume per path, or nothing with `fallback: none`. A restricted fork run and a tag run get none. See [Caches](/guides/pipeline-reference#caches) | | Fork budgets | A restricted fork run: at most 8 matrix legs per stage, a 60-minute step timeout, 2 CPU and 4Gi per step, no sidecar services, no shared caches, and 256 MiB of artifacts and logs for the whole run | | Parallelism | 16 runs per organisation and 8 steps per run at once by default, up to 64 and 256; the CI cluster's agent runs at most `ci_worker_count` steps at once (0 to 128, or 0 to 32 on older agents); org-fair claiming; a step waiting for a CI slot does not use up its timeout | | Artifacts | 512 MiB per object, 2 GiB per run, 20 uploads per step; download links are valid for 5 minutes | | Retention | Artifacts and step logs 30 days by default (1 to 365), caches 14 days (1 to 365), concluded runs 90 days (7 to 365) | | Run budget | A `pending` step is reaped once its run outlives the sum of every step's `timeout`, capped at 6 hours plus a grace window; it concludes `infra_error` / `step_stranded` and is not retried | | Secret and credential grants | At most 32 bindings per step; each revealed once, alive for the step's own timeout plus 15 minutes, capped at 7 hours | | Step outputs | `$ANKRA_OUTPUT` takes `key=value` lines, at most 64 KiB, recorded on the step | | Live log | 1,024 lines are queued; when the reader falls behind the agent blocks for 250 ms and then drops, writing a `… N lines dropped` marker where the loss happened; the uploaded log keeps the last 8 MiB. The dropped count is recorded on the step and shown under its row on the check run and the status comment | | Log history | The live stream has no replay: a live connection shows output from the moment it connects; the complete log is the `step.log` artifact, which `ankra pipeline logs` reads automatically for a step that has already concluded, from the CLI's next release. `step.log` is an object in the organisation's ready backup vault, or in the Ankra-managed default vault when it has none, so history is kept either way; only with no ready vault on either side is there none - just what a live connection caught | | Expressions | A template of at most 8,192 bytes with at most 64 expressions, each at most 2,048 bytes and 24 levels deep | ## Bring your own CI (GitHub Actions) You do not have to delete your existing CI to try Ankra Pipelines - the [generated GitHub Actions workflow](/guides/cicd-pipeline) keeps building, scanning and publishing images to your registry, and a pipeline run on the same commit is a second, independent check, `Ankra pipeline` beside the workflow's own. But connecting the repository is not inert: the moment `.ankra/pipeline.yaml` is the definition of record and the organisation has a CI cluster, the application is read as pipeline-sourced and its pull-request **previews** switch to the pipeline's own digest from then on - not when you decide you trust it. A merge to your default branch is more forgiving, because your own CI's build observation always outranks the pipeline's for it, so the workflow stays what actually deploys production until the pipeline's `publish` catches up or you retire the workflow. Retirement is deliberate either way: Ankra will not delete a workflow for you; when it does propose one, it will be a reviewable pull request. `build` and `publish` now run in the cluster, so a pipeline can publish your image on its own once you trust it - review [what to check after conversion](/guides/migrate-from-github-actions#what-to-review-after-conversion) first, and keep the workflow live until you have watched the pipeline publish correctly a few times. [Migrate from GitHub Actions](/guides/migrate-from-github-actions) explains how a converted pipeline maps onto it and what to review; an application already connected to Ankra can skip the manual conversion entirely and [migrate with one call](/guides/migrate-from-github-actions#migrate-an-existing-application-with-one-call). ## Commands and API Every command addresses one pipeline with `--application ` or `--repository `. Run from inside a checkout, both are optional: the CLI resolves the application from the repository your `origin` remote points at, and says which one it used. It asks for the flag when the working directory answers nothing, or names the candidates when the repository carries more than one application. `ankra application pipeline …` is the same set of commands with a leading `` argument. Tables by default, `-o json` for the wire shape. | Command | What it does | | - | - | | `ankra pipeline run [--sha ] [--ref ] [--input k=v] [--reason ] [--wait]` | Dispatch a manual run at a commit the repository owns - the sha is resolved against the repository's own host and refused when it is neither a reference's tip nor a provable ancestor of one. `--spec-file` runs a definition of your own (`pipelines.manage`): it is stored on the run and both the planner and the dispatcher render from it, while the run's authority still comes from the default branch's approved definition | | `ankra pipeline list [--status queued\|running\|concluded] [--trigger …] [--branch …] [--head-sha …] [--limit N] [--cursor …]` | List runs, newest first, keyset paginated | | `ankra pipeline get ` | A run and its step table | | `ankra pipeline logs --step [--follow]` | A running step's live output, reconnecting through a dropped stream with `--follow` until it concludes; a concluded step's complete output from the CLI's next release, before that only what a live connection happened to catch | | `ankra pipeline cancel ` | Stop a run that has not concluded | | `ankra pipeline rerun [--failed-only] [--wait]` | Open a new run from a concluded one | | `ankra pipeline artifacts ` and `artifacts download [--out ]` | List a run's stored logs and artifacts, and fetch one through its presigned URL | | `ankra pipeline findings ` | List a run's persisted scan findings by tool and severity (CLI's next release) | | `ankra pipeline validate [file]` | Validate and dry-run a definition without writing anything | | `ankra pipeline definition get\|put ` | Read or replace the stored definition of record | | `ankra pipeline schedules list\|create\|update\|delete` | Manage cron schedules (`--cron`, `--timezone`, `--ref`, `--input`, `--enabled`) - stored, validated and now fired | | `ankra pipeline train list\|add \|remove \|enable [--max-cars N]\|disable` | The repository's [merge train](#the-merge-train-test-every-pull-request-as-it-will-merge): test each pull request as it will merge, then merge it | `ankra pipeline repositories connect|list|get|disconnect`, `ankra pipeline definitions get|approve`, `ankra pipeline findings`, the concluded-step behaviour of `logs` and artifact listings that carry a status have all merged and ship in v0.15.0, which is a pre-release today - enable the [beta channel](/integrations/ankra-cli#beta-pre-release-channel) to get them, or use the API and the portal until v0.15.0 is stable. `ankra cluster agent ci get|set`, which turns a cluster's pipeline-step workers on, lands in `v0.15.0-rc3`; until then that setting is `PUT /api/v1/org/clusters/{cluster_id}/agent/ci-settings` on the API. The same run and repository operations are on the API under `/api/v1/org/applications/{application_id}/pipeline-runs`, `/api/v1/org/pipeline-repositories/{repository_id}/pipeline-runs` and `/api/v1/org/pipelines/repositories` with a bearer [token](/reference/tokens): list and create runs, `…/{run_id}`, `…/{run_id}/rerun`, `…/{run_id}/cancel`, `…/{run_id}/steps/{step_id}/logs` (server-sent events, resumable with `from_seq`), `…/{run_id}/artifacts`, `…/artifacts/{artifact_id}/download` (a `302` to a presigned URL), and beside them `…/pipeline` (`GET`, `PUT`), `…/pipeline/validate`, `…/pipeline/schedules`, `…/merge-train` (`GET`), `…/merge-train/settings` (`PUT`), `…/merge-train/entries` (`POST`) and `…/merge-train/entries/{entry_id}` (`DELETE`), and `…/pipelines/definitions/{id}[/approve]`. Creating a run answers `202` with the run id and its number; connecting a repository answers `201` with the repository and what happened to its committed definition, or `409` naming the one that already exists. ## What comes next These are on the plan of record and not available yet. None of them is usable today, and this page will say so when that changes. * The runner for `verify` (HTTP probes, smoke scripts, k6 load tests, PromQL assertions, restore verification). * The per-run identity that `permissions` mints, so a step can call the `ankra` CLI with exactly the authority the default branch granted; `cloud` and `object_storage` credentials once it exists. * The built-in `ankra/*` step library behind `uses:`, organisation step templates, and `custom_tool/` steps. * The executors for the `preview` (inside the pipeline DAG), `agent`, `approval`, `webhook`, `automation`, `ankra` and `external` stage kinds; `on.webhook` inbound dispatch tokens. Each of those stages validates with a warning and is skipped today, so declaring one costs you the stage, not the run. * The user-declared `retry` block being applied; dynamic matrices that fan out over a previous step's output; a dedicated run page in the portal (the Pipelines section shows runs today; a standalone page with more depth is still to come). * Attestations and SBOM referrers carried onto the digest `publish` re-tags into your real repository, not only the one `build` pushes to staging. * Merge-when-green, automatic disposition re-evaluation, and planning from the committed file at a run's own commit, on GitLab and Bitbucket Cloud (GitHub only today). * The agent release that publishes live step output on the dedicated `pipeline_output` stream the platform already reads. (The narrower `egress-https` with its `ci_egress_allowed_cidrs` allow-list, and the image-prefix boundary check, were listed here and have since shipped on both the platform and the agent - see [What is in place today](#what-is-in-place-today).) * Environments hydrated from restore points, verification stages, promotions with approvals, and canary rollouts through Traefik weighted traffic. ## Next steps > [card] [pipeline.yaml reference](/guides/pipeline-reference) > > Every key, the expression language, the trigger table and the diagnostics the validator reports. > [card] [Migrate from GitHub Actions](/guides/migrate-from-github-actions) > > Convert a workflow, GitLab CI file, Bitbucket pipeline or CircleCI config and review what changed. > [card] [Application CI/CD](/guides/cicd-pipeline) > > The generated GitHub Actions workflow, registry authentication and image retention. > [card] [Backup vaults](/concepts/backup-vaults) > > Where pipeline logs, artifacts and scan reports are kept - a ready one is a precondition for a run. # Run tests and lint in Ankra Workspaces Source: https://docs.ankra.ai/guides/ankra-workspaces Send the heavy commands of your development loop - go test, golangci-lint, pnpm typecheck and test, Playwright - to a warm pod on your organisation's CI pool instead of your laptop, by hand with ankra exec or automatically for you and your coding agents with ankra dev install. An **Ankra Workspace** is your own long-lived pod on the organisation's CI pool, one per repository (and per kind, such as `node` or `e2e`). `ankra exec` runs a command for your current checkout in it: your uncommitted changes go with it, the output streams back live, and the exit code is the remote command's own. `ankra dev install` puts small shims in front of `go`, `golangci-lint`, `pnpm` and friends so that the heavy commands you, your editor, git hooks and coding agents already run go to the workspace without anyone typing anything different. The point is your laptop's CPU and memory: five coding agents each running `go test ./...` or a full typecheck stop fighting over one machine. ## Prerequisites * **The Ankra CLI**, logged in to the organisation that owns the repository: ```bash theme={null} brew install ankraio/tap/ankra # or: brew upgrade ankra ankra login ankra org current ``` * **The `workspaces.use` permission.** Organisation admins have it; ask an admin to grant it otherwise. A workspace you cannot reach answers "not found", like one that does not exist. * **A git checkout whose `origin` remote is a repository connected to Ankra Pipelines** in that organisation. If it is not connected yet, see [Connect a repository for Workspaces](#connect-a-repository-for-workspaces). ## Run your first command From anywhere inside the checkout: ```bash theme={null} ankra exec --check && echo "this checkout can use a workspace" ankra exec -- go test ./... ``` The first command in a repository brings its workspace up, which takes a few minutes while the pod is scheduled, the image is pulled and the profile's tools are installed. `ankra exec` says what it is waiting for (`still provisioning: Pulling image ...`). Later commands reuse the warm pod and start in a second or two. The command runs in the same relative directory as your shell, against your worktree exactly as it is: tracked files plus untracked files that git does not ignore. Your branch, index and working tree are never touched, and only what the workspace does not already hold is uploaded. ## Route commands automatically ```bash theme={null} ankra dev install ankra dev status ``` `ankra dev install` writes the shims to `~/.ankra/dev/shims` and links them into `~/.local/bin`, so every new shell, editor, git hook and coding agent session picks them up. If `~/.local/bin` is not on your `PATH`, or a real tool comes before it, the command prints the exact `PATH` line to add to your shell profile; it never edits a profile for you. Each shim decides per invocation: | Where it runs | Commands | | - | - | | In the workspace | `go test`, `go vet`, `go build` (without `-o`, `-c`, coverage or profile files), `golangci-lint run`, `gofmt -l` / `-d`, `pnpm` / `npm` `typecheck`, `test`, `test:ci`, `test:unit`, `lint`, `lint:*`, `format-check`, `knip`, `vitest run`, `tsc --noEmit` | | In the workspace, changes applied to your worktree | `go generate`, `go mod tidy`, `go get`, `go fmt`, `go tool ...`, `gofmt -w`, `golangci-lint run --fix` | | In the workspace, results copied back | Playwright runs (`pnpm test:e2e*`, `npx playwright test`): `test-results` and reports | | On your machine | Everything else: `go run`, `go install`, dev servers, builds with output, `pnpm install` / `add`, anything interactive (`--ui`, `--debug`, `--headed`) | A routed command behaves exactly like `ankra exec`: same output, same exit code. When a checkout cannot use a workspace - its repository is not connected, you are not logged in, you are offline - or the workspace could not run the command, the real tool runs on your machine instead, so nothing ever blocks on the platform. ```bash theme={null} ANKRA_EXEC_EXPLAIN=1 go test ./... # prints route or local, runs nothing ankra dev mode local # stop routing (ankra dev mode auto to resume) ANKRA_EXEC=local go test ./... # run one command locally ankra dev install --uninstall # remove the shims and their links ``` ## Commands that write files or produce results ```bash theme={null} ankra exec --apply -- gofmt -w ./cmd ankra exec --kind e2e --fetch test-results -- pnpm test:e2e ``` * `--apply` brings the files the command changed in the workspace back as a patch applied to your worktree. A patch that no longer applies (you edited the same lines meanwhile) is kept under `.git/ankra-exec-unapplied-.patch`. * `--fetch DIR` copies a directory back after the run, replacing your local one, whatever the exit code. Only an allowlisted environment is forwarded (`GOFLAGS`, `NODE_OPTIONS`, `TZ` and similar). Add your own variable names, never secrets, with `ANKRA_EXEC_FORWARD_ENV=MY_FLAG,OTHER_FLAG`. ## Manage your workspaces ```bash theme={null} ankra workspace status ankra workspace up --wait # bring it up ahead of the first command ankra workspace down --yes # tear it down; the next command brings a fresh one ankra workspace down --yes --kind e2e ``` A workspace that has not been used for 24 hours is torn down for you. Changing the repository's workspace profile replaces the workspace at the new size the next time it is brought up. ## Connect a repository for Workspaces A repository has to be connected to Ankra Pipelines in your organisation before anyone can use a workspace for it. If the repository's pipeline runs somewhere else - in another organisation or another CI - connect it for Workspaces only, so Ankra never reads its pipeline file, starts a run or posts a check on its pull requests: ```bash theme={null} ankra pipeline repositories connect --provider github --owner my-org --name my-repo \ --credential --workspaces-only ankra pipeline repositories set-mode workspaces-only # or: pipelines ``` Connecting needs the `pipelines.manage` permission. ## Give a repository a workspace profile Without a profile, the workspace uses the image of the repository's pipeline. A **workspace profile** says exactly what the pod needs: its image, CPU and memory, disk, service containers (a database the tests reach by name), pinned tools to install and setup commands to run once. A repository can have several kinds, such as `default`, `node` and `e2e`; `ankra dev` uses the `go` kind for Go tools, `node` for pnpm, npm and npx, and `e2e` (or `playwright`) for Playwright runs when the profile has them, and `default` otherwise. ```json theme={null} { "profiles": { "default": { "image": "golang:1.26.3-bookworm", "resources": { "cpu": "6", "memory": "16Gi" }, "volume_size": "40Gi", "services": [ { "name": "postgres", "image": "postgres:17", "env": { "POSTGRES_HOST_AUTH_METHOD": "trust", "PGDATA": "/tmp/pgdata" }, "ports": [5432], "ready": { "tcp": "5432" } } ], "tools": [ { "name": "golangci-lint", "url": "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz", "sha256": "8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553", "strip_components": 1 } ], "setup": ["golangci-lint version"] } } } ``` * **`tools`** are downloaded once, checked against their `sha256` and put first on the `PATH` of every command. A `.tar.gz` or `.tgz` URL is unpacked; anything else is the executable itself. * **`setup`** runs once per workspace, before the first command, in the checkout. * **Services and commands run as uid 65534.** An image that writes where only its own user may, such as postgres, needs pointing at a writable directory (`PGDATA` above). * **`resources.memory` is the limit for every command in the workspace together.** Heavy commands queue for a share of it, so several agents can run at once without the kernel killing the pod. If a command still runs it out of memory, `ankra exec` says so and names the limit to raise. * The image must match one of the organisation's allowed CI image prefixes. Store the profile with the API, which needs `workspaces.manage`: ```bash theme={null} curl -fsS -X PUT \ -H "Authorization: Bearer $ANKRA_API_TOKEN" -H "Content-Type: application/json" \ --data @workspace-profile.json \ "https://platform.ankra.app/api/v1/org/pipeline-repositories//workspace-profiles" ``` Find the repository id with `ankra pipeline repositories list`. ## Troubleshooting | What you see | What it means | Fix | | - | - | - | | `ankra exec` exits `196`, or a routed command quietly runs on your laptop | The checkout cannot use a workspace: not logged in, the `origin` repository is not connected in the selected organisation, or you lack `workspaces.use` | `ankra login`, `ankra org current`, `ankra exec --check`; connect the repository | | `still provisioning: Waiting for a node ...` | The CI pool is adding a node for your workspace | Wait; the first workspace on a fresh node takes a few minutes | | `[devbox] got a run slot after Ns` | Other heavy commands were using the workspace's memory; yours waited its turn | Nothing; raise `resources.memory` if waits are long and frequent | | `The command ran out of memory ...`, exit `137` | The command (or several together) exceeded the workspace's memory limit | Raise `resources.memory` in the profile | | A test passes locally but fails only in the workspace | The image or tools differ from your machine | Pin the same tool versions in the profile's `tools` | Every run appends one line to `~/.claude/logs/ankra-exec.log` (or `ANKRA_EXEC_LOG`) with the repository, exit code, duration and command, which is the first place to look when a routed command behaved unexpectedly. See also the CLI reference for [`ankra exec`](/reference/cli/exec), [`ankra workspace`](/reference/cli/workspace) and [`ankra dev`](/reference/cli/dev). # Environments and Branch Rules Source: https://docs.ankra.ai/guides/application-environments Deploy one application to production, staging and development, and choose which branches' pushes reach each environment. An application can run in several environments. Each environment is a cluster and a namespace with a name, such as `production`, `staging` or `development`. A **branch rule** on an environment lists the branches whose pushes deploy to it. With rules in place, a merge to `main` reaches production, a push to `develop` reaches development, and a `release/*` branch reaches staging, from one application and one pipeline. This page builds on [push to deploy](/guides/deliver-your-app#how-a-merge-redeploys). Push to deploy has to be on for an environment before its rule deploys anything. *** ## What a branch rule is A branch rule is a list of branch patterns. A push to a branch the list matches deploys to the environment. An environment with **no rule** follows the application's tracked branch, which is how every environment behaves until you give it a rule. Pull requests never deploy to an environment, whatever the rule says. They get [PR preview environments](/guides/pr-preview-environments) instead. ### Typical setups | Environment | Rule | Deploys pushes to | | - | - | - | | `production` | `main` | `main` only | | `staging` | `release/*` | Release branches such as `release/2.4` | | `development` | `develop` | `develop` only | | `development` | `**`, `!main` | Every branch except `main` | ### Pattern syntax Patterns use the same matching as a pipeline stage's [`when.branches`](/guides/pipeline-reference#fields-common-to-every-stage), so a pattern means the same thing in a pipeline and in an environment. | Pattern | Matches | Does not match | | - | - | - | | `main` | `main` | `main-v2`, `feature/main` | | `release/*` | `release/2.4` | `release/2.4/hotfix`, `release` | | `release/**` | `release/2.4`, `release/2.4/hotfix` | `releases/2.4` | | `**` | Every branch | Nothing | | `!main` | Every branch except `main` | `main` | * `*` matches within one path segment. It never crosses `/`. * `**` matches across `/`. * `!pattern` removes the branches the pattern matches. * Patterns are evaluated in order, and the last pattern that matches a branch decides. So `**`, `!main` is every branch except `main`, while `!main`, `**` is every branch. * A list whose first pattern is negated starts from every branch. `!main` on its own therefore means every branch except `main`. An invalid pattern is refused when you save the rule, with a sentence that names the problem. *** ## How a build reaches an environment 1. **A push builds.** The pipeline runs for the pushed branch. When its `publish` stage succeeds, Ankra records the image it published, `sha-<7>`, as that branch's newest build. 2. **Each environment picks its build.** An environment deploys the published build whose push came last, of any branch its rule matches. An environment with no rule deploys the newest build of the tracked branch, as before. 3. **Older builds never win.** A build of an older commit that finishes late never moves a branch back to that commit. Only a push run whose `publish` stage succeeded records a build. A run that fails, is skipped, or never reaches `publish` deploys nothing anywhere. > [!NOTE] > > The [generated pipeline](/pipelines/generated-pipelines#what-the-generated-pipeline-contains) triggers pushes on the tracked branch only. Before another branch can reach an environment, add it to `on.push.branches` in `.ankra/pipeline.yaml`, for example `branches: [main, develop, "release/*"]`, so its pushes run and publish. **Changing a rule is never a deploy.** Neither is turning push to deploy on. Ankra notes the newest build the rule already matches, and the environment moves on the next matching push. To deploy right away, deploy a tag by hand. **Deploying by hand still works.** A person can promote or roll back an environment to any tag with `ankra application deploy`, as before. The next matching push moves the environment on again. *** ## Set it up ### From the CLI List the application's environments. The `FOLLOWS` column shows each environment's rule, ` (tracked)` when it has none, or `manual` when pushes do not deploy to it: ```bash theme={null} ankra application environments my-app ``` Give an environment a rule, remove it, or turn push to deploy on or off for that environment alone. `--branches` takes a comma-separated list, and the environment is named by its name or its id: ```bash theme={null} ankra application environments set my-app production --branches main ankra application environments set my-app development --branches '**,!main' ankra application environments set my-app staging --branches 'release/*' ankra application environments set my-app staging --clear ankra application environments set my-app staging --enable ankra application environments set my-app production --disable ``` `--clear` removes the rule, so the environment follows the tracked branch again. Without `--enable` or `--disable`, the environment keeps its current push to deploy setting. Create a new environment that follows a branch from its first deploy: ```bash theme={null} ankra application deploy my-app \ --cluster my-cluster \ --namespace my-app-dev \ --environment development \ --follow-branches develop ``` ### In the portal In the new Ankra portal (`/next` on your portal's address), each environment card on the application's **Overview** shows what it follows, such as **Follows pushes to main** or **Follows pushes to every branch except main**. To change a rule, pick the environment in the application's environment menu and open **Settings** → **Deploys**. The **Deploy on every push** card there holds that environment's **Branches it follows** editor, with presets for the common setups, a field for your own patterns and a **Save branches** button. ### From the API Set a rule on one environment with `PUT /org/applications/{application_id}/auto-deploy`: ```json theme={null} { "enabled": true, "environment_id": "", "branches": ["develop"] } ``` * `enabled` is required, and it sets the environment's own push to deploy switch, so send its current value to change only the rule. * `branches` needs `environment_id`. * `"branches": []` removes the rule. * Leaving `branches` out leaves the rule as it is. * An invalid pattern returns `422` with a sentence that names the problem. `GET /org/applications/{application_id}/auto-deploy` lists every environment with its `branches`. `null` means the environment follows the tracked branch, unless `branches_mixed` is `true`: then the environment's deployments do not all carry the same rule, and setting or removing its rule makes them agree. `POST /org/applications/{application_id}/deploy` accepts `environment_name` and `follow_branches`, to create a named environment that follows those branches from the start. Changing a rule needs permission to deploy applications. Reading it does not. *** ## Add an environment on another cluster In the new Ankra portal, **Add** beside the environments on the application's **Overview** offers: * **On a new cluster…** opens **Add a cluster**, set to copy every stack of the cluster the environment in view runs on and to deploy the application there as a new environment. It needs `clusters.create` and `applications.deploy`. * **On a cluster you have…** opens the deploy form for a new environment. It needs `applications.deploy`. On a new cluster, **Review** shows the copy under **Application to deploy**, with the new environment's name and branches: * **Copied:** the source environment's image and deploy settings (replicas, resources, ports), and every stack of its cluster except the application's own, the backup stack and the provider-managed stacks. A stack that needs secrets the new cluster lacks stays a [draft](/guides/clone-cluster#stacks-that-need-your-input). * **Not copied:** hostnames (the new environment gets its own address), data and backup policy. Databases start empty. * **Secret overrides** come along with **Copy secret overrides**, which needs `applications.write` and starts on only when the new cluster's purpose matches the source environment's. Without it the environment uses the application's shared secrets. The application deploys once the cluster is online and its copied stacks have deployed; after an hour Ankra stops waiting and says so on the **Overview**. From the API, `POST /org/applications/{application_id}/environments/{environment_id}/clone` with `target_cluster_id` copies an environment onto a cluster you have: one the application does not run on gets a new environment, one it runs on is overwritten in place, the source's own is refused with `409`. `include_secrets: true` copies the overrides and needs `applications.write`; `environment_name` and `follow_branches` name the environment and its rule. A [cluster clone](/guides/clone-cluster#clone-a-whole-cluster) request takes the same copy as `application_environment` and checks it before creating anything. *** ## Adjust a pipeline to where a push is going A pipeline can ask which environments the current push deploys to. [`ankra.environments`](/guides/pipeline-reference#where-a-push-deploys) is the sorted list, by name, of the environments with push to deploy on whose rule matches the run's branch, or that have no rule when the run's branch is the tracked branch: ```yaml theme={null} stages: - name: smoke-production kind: run if: "'production' in ankra.environments" run: ./scripts/smoke.sh https://app.example.com - name: seed-demo-data kind: run if: "'development' in ankra.environments" run: ./scripts/seed.sh ``` Use `ankra.environments` for steps that depend on where the image is going. To change steps by branch, keep using `when.branches`. *** ## FAQ **I pushed to `develop` and development did not move.** Check the push's pipeline run. Development moves only when that run's `publish` stage succeeded and pushed a `sha-<7>` image. A failed or skipped `publish` records no build for `develop`, and neither does a pipeline whose `on.push.branches` leaves `develop` out, which is how the generated pipeline starts. Then check that push to deploy is on for development, and that its rule matches `develop`: `ankra application environments my-app` shows `manual` for an environment that pushes do not deploy to. **An environment has no rule. What does it deploy?** The newest build of the application's tracked branch, exactly as before branch rules existed. **I changed a rule and nothing deployed.** That is expected. Changing a rule is never a deploy. The next push that the rule matches deploys, or you can deploy a tag by hand. **Does a pull request deploy to development if its branch matches?** No. Pull requests never deploy to environments. They get a [PR preview](/guides/pr-preview-environments). **Two branches match one rule. Which build wins?** The published build whose push came last, of any matching branch, even if another build finished publishing after it. *** ## Related > [card] [Ship your app](/guides/deliver-your-app) > > Register an application and turn on push to deploy. > [card] [Pipeline reference](/guides/pipeline-reference#expressions) > > The expression context, including `ankra.environments`. # Audit Log Source: https://docs.ankra.ai/guides/audit-log Review the append-only record of every administrative change in your organisation - who did what, when, and from where. Every administrative change in Ankra is recorded in the organisation **Audit Log**. It is the platform's system of record for governance questions: who invited that member, who approved that deployment, who accepted that CVE as risk, and when. *** ## What is recorded Each entry captures: * **Who** - the actor (a member, or a token acting on their behalf) * **What** - the action and the resource it touched, with structured details of the change * **When** - the timestamp * **Where from** - the source IP Administrative changes across the platform write entries: invitations and role assignments, cluster-group edits, credential changes, cluster lifecycle operations, security disposition changes, and organisation settings edits. Changes to a cluster agent's settings are recorded with the value they replaced: turning automatic agent upgrades on or off for a cluster (`set_agent_auto_upgrade`), the agent's memory and CPU (`set_agent_resources`) and its pipeline settings (`set_agent_ci_settings`) each record every setting they moved as `changes: {: {from, to}}`, and the person's reason when they gave one. Turning automatic agent upgrades on or off for the whole organisation is recorded on the `update_organisation` entry, with the earlier value under `previous`. Kubernetes access is recorded too: cluster access grants and their changes, every kube token minted (including the one behind each generated kubeconfig), every [pod terminal](/platform/pod-terminal) session, whose entry links to a recording of the session, and every reveal of a Kubernetes Secret's values (action `reveal`, resource type `kubernetes_secret`, naming the cluster, namespace, Secret and key names, never the values). Other individual Kubernetes API calls are not in this log; your cluster's own audit policy records those. Cluster access grants are recorded as `cluster_access_grant_created`, `cluster_access_grant_extended` and `cluster_access_grant_deleted`, and each grant entry records the `authority`: the permission the grant was made under, `kube_access.manage` or, for a [break-glass elevation](/guides/cluster-access#break-glass-elevation) by a caller that holds only the break-glass permission, `kube_access.elevate`. Every change to the [organisation access policy](/guides/cluster-access#organisation-access-policy) is recorded as `cluster_access_policy_updated`. Entries are **append-only** - nobody in your organisation can edit or delete them, and only the [retention](#retention) job removes them - and they are written for every change regardless of who can view the log. *** ## Who can see it Viewing the audit log requires the `audit.read` permission, held by **owners** and **admins** by default. It can also be granted to [custom roles](/guides/roles-and-access#permission-matrix), so a compliance or security function can review the log without broader administrative access. *** ## Reading and filtering Open **Organisation → Audit log**. From there you can: * **Search** entries by keyword * **Filter** by user, action type, cluster, resource type, and date range * **Expand** an entry to see the structured details of the change Every entry names the **cluster** it belongs to, and the name links straight to that cluster's own Audit tab. Organisation-level events - sign-ins, invitations, role changes - show no cluster. A cluster you have since deleted keeps its name in the trail, marked deleted: the record outlives the cluster, which is exactly what makes the deletion itself auditable. The cluster filter is carried in the page URL, so a view narrowed to one cluster can be bookmarked or shared as a link. *** ## Per-cluster view Each cluster carries its own slice of the trail: the **Audit** tab in cluster **Settings** shows only the manual actions performed on that cluster - lifecycle operations, scaling, node restarts, and stack changes - with the same permission gate and entry format. See [Cluster Settings → Audit](/platform/cluster-settings#audit). The two views are connected in both directions. From the organisation log, a row's cluster takes you into that cluster's tab; from the tab, **View in organisation audit log** takes you back to the full trail with that cluster preselected as the filter - the same entries, alongside the organisation-level events the tab leaves out. *** ## Standard changes Entries for changes Ankra makes to a cluster also record which kind of change they were. A change that matches the [standard-change catalogue](/security/change-management#the-standard-change-catalogue) - an add-on upgrade that fixes a published security advisory, a Kubernetes patch upgrade, autoscaling within your limits, a scheduled power action, access expiry - is marked as a pre-approved standard change, with the catalogue entry and the reason it qualified. The cluster **Audit** tab shows it as **Pre-approved SC-01** (or the matching ID) beside the action; the full record is under `change_classification` in the entry's details. Entries without that record are normal changes, or events that are not changes at all. See [Change Management](/security/change-management) for the catalogue and for how to require approval for every change instead. *** ## Exporting Export the current filtered view to CSV or JSON from the portal. The download honours the filters on screen, including the cluster filter, and the export rows carry the cluster alongside the actor and action. A cluster's **Audit** tab exports through the same path, so a per-cluster download and the organisation view filtered to that cluster contain the same entries. A single export returns up to 100,000 rows and signals truncation when more rows match. See [Audit Export](/security/audit-export) for the full column list and how to get the trail into a SIEM. *** ## Retention Audit entries are retained for **365 days**, after which older rows are removed by a background retention job. Entries inside the window are immutable. If your framework requires a longer trail, [export](/security/audit-export) on a schedule into storage you control - the platform is not intended to be your long-term system of record. *** ## Relationship to Git history Ankra gives you two complementary trails: * The **audit log** records platform actions - who signed in, assigned a role, approved a plan, or changed a disposition. * **Git history** records desired-state changes as attributed commits - exactly what changed in your cluster definitions and Stacks, and when it was deployed. Together they answer both "who did what in the platform" and "what actually changed in the cluster". For how commits are produced and attributed, see [GitOps](/concepts/gitops). *** ## Related > [card] [Roles & Access](/guides/roles-and-access) > > Grant the `audit.read` permission via custom roles. > [card] [Change Management](/security/change-management) > > Pre-approved standard changes and how they are recorded. > [card] [GitOps](/concepts/gitops) > > The attributed commit trail for desired-state changes. > [card] [Security at Ankra](/security/overview) > > How the audit log fits the wider control set. > [card] [Cluster Security](/guides/cluster-security) > > Disposition changes that land in the audit log. # Use Avura domains with Ankra Source: https://docs.ankra.ai/guides/avura-domains Connect an Avura workspace to its Ankra organisation, choose which Avura domains Ankra may use, then publish cluster hostnames, manage records and root generated cluster domains on those domains. If your domains live in Avura, Ankra can use them directly: clusters publish Ingress hostnames into them, you edit their records from the Ankra dashboard, and one of them can become the root every generated cluster domain nests under. You connect once from Avura and choose, domain by domain, which ones Ankra may touch. > [!NOTE] > > This integration arrives with the Avura connection release of both products. If **Organisation** → **Settings** in Ankra has no **Avura** entry, or Avura's **Admin** → **General** page has no **Ankra Platform** card, it has not reached your organisation yet. ## Prerequisites * **One login for both.** You sign in to Avura with **Continue with Ankra**, using the same account you use for Ankra. If you already have an Avura password account, link Ankra to it from **Settings** → **Security** → **Link Ankra account**. The Ankra email has to match your Avura email, and both have to be verified. * **An Avura workspace linked to the Ankra organisation.** When an Ankra organisation owner or admin signs in to Avura with Ankra for the first time, Avura creates a workspace for each organisation they administer and links it. An existing workspace is linked by its Avura owner from **Admin** → **General** → **Ankra Platform organisation**, which lists the Ankra organisations you administer that are not linked anywhere else. One workspace links to one organisation. * **Admin rights on both sides.** Connecting needs an Avura owner or admin who is also an admin of the linked Ankra organisation. Managing records, custom DNS zones and the root domain in Ankra needs an Ankra organisation admin; any member can view them. ## Connect from Avura The connection is always made from Avura, never from Ankra. **1. Open the Ankra Platform card** In Avura, go to **Admin** → **General** → **Ankra Platform**. The card shows which Ankra organisation the workspace is linked to. **2. Connect** Click **Connect**. Avura creates a platform token for the workspace and hands it to the linked Ankra organisation, which checks it against Avura before saving it. If Ankra refuses, Avura discards the new token and shows why; a connection that was already working is left as it was. **3. Check it from Ankra** In Ankra, **Organisation** → **Settings** → **Avura** now shows the linked Avura organisation, the status **connected**, how many domains are shared and when the connection was last verified. **Open Avura** takes you back to Avura's admin page. **Rotate connection** on the same Avura card replaces the platform token. Clusters already publishing through the connection keep working through a rotation. ## Choose which domains Ankra can use Connecting shares nothing on its own. Every domain starts closed, and an Avura owner or admin opens each one: * In Avura's **Domains** list, turn on the **Ankra Platform** switch for the domain, or open the domain and use the **Use with Ankra Platform** card on its **Overview** tab. * Subdomains inherit the setting from the nearest parent that has it on, and show it as locked on. * Turning a domain off cuts Ankra off on its very next request, including the tokens clusters are already using to publish records in it. Anything in Ankra that relies on that domain stops updating until you turn it back on. * Moving a domain to another Avura organisation turns the setting off, so it never arrives already shared with that organisation's Ankra. ## What Ankra can and cannot do The platform token Avura issues belongs to the workspace, not to a person. It holds no Avura role, so it keeps working when the admin who connected leaves, and it never appears in anyone's token list. On the domains you opened, and their subdomains, Ankra can: * list the domains and read their records * create, edit and delete records, and ask Avura to re-sync a zone * create subzones, and delete any subzone inside a shared domain (but never the shared domain itself) * mint tokens pinned to a single shared domain, which is what a cluster's external-dns receives Ankra cannot see any domain you did not open, delete a shared domain itself, add root domains, change a domain's sharing, or reach grants, mail, DNSSEC or Avura's own settings. The platform token itself stays in Ankra's secret store and is never sent to a cluster. Clusters receive tokens pinned to one domain: a domain you serve from a single cluster gets a token for that cluster, and a domain declared for the whole organisation gets one token shared by every cluster that serves it. Revoking the connection revokes every token minted from it. ## Serve an Avura domain from your clusters A shared Avura domain works as a [custom DNS zone](/concepts/applications#serving-a-zone-in-your-own-dns-account-from-every-cluster): Ankra runs an external-dns pinned to that zone on each cluster, and an Ingress on a hostname under it is published by the cluster that serves it. The certificate follows once the name resolves. **1. Add the domain** Go to **Organisation** → **Settings** → **DNS records** → **Custom domains** and click **Add domain**. **2. Pick Avura** Choose **Avura** as the provider. The domains Avura shares with Ankra appear as a list; pick one. **3. Choose the scope** **All clusters** serves the zone from every cluster in the organisation, including clusters created later. **One cluster** serves it from the cluster you pick. The CLI does the same with the connection's credential, which is always named `avura`: ```bash theme={null} ankra org custom-dns-zones add --zone example.dev --credential avura ankra cluster custom-dns-zones add my-cluster --zone example.dev --credential avura ``` A zone that Avura does not share with Ankra is refused, and so is a zone that overlaps the organisation's Custom Ankra domain (use a domain either as a custom DNS zone or as the root below, not both). **Remove** on the zone card (or `ankra org custom-dns-zones remove --zone example.dev`) withdraws the zone and revokes the token its clusters used. The records already in the zone are yours and stay where they are. ## Manage records from Settings > Avura **Organisation** → **Settings** → **Avura** lists the shared domains. Select one to see its records; admins can add, edit and delete them. | Field | Rule | | - | - | | **Type** | A, AAAA, CAA, CNAME, MX, NS, SRV or TXT | | **Name** and **Value** | Required | | **TTL** | Blank for Avura's default, or a whole number of seconds from 60 to 604800 | A record's name and type are fixed once it exists, so editing changes its value and TTL only. If Avura refuses a change (a conflicting record, or Avura being briefly unavailable), the dialog stays open with the reason so you can fix it or retry. The changes are made in Avura, so Avura's own record list shows them straight away. ## Root generated cluster domains on an Avura domain Every hostname Ankra generates, including each cluster's domain and the preview URLs built from it, nests under the organisation's [Custom Ankra domain](/platform/ai-workspaces#custom-ankra-domain), which is `ankra.cc` until you set one. A domain Avura shares with Ankra can be that root. Go to **AI** → **Settings** → **Workspaces**. With Avura connected, the **Custom Ankra domain** field suggests the top-level domains your Avura workspace shares with Ankra; click one to fill it in, or type a domain yourself, then save. From the CLI: ```bash theme={null} ankra org domain set example.dev ``` Ankra first asks your Avura organisation whether it shares the domain. When it does, Ankra provisions the organisation's zone and every cluster's zone in your Avura account, with your connection, and skips the nameserver check a root hosted by Ankra needs: Avura's own listing is the proof you hold the domain. The saved field then carries a **Hosted in your Avura** badge and says no nameserver change is needed. When Avura does not share the domain, it is treated as an ordinary custom Ankra domain and has to be delegated to Ankra's nameservers. * Ankra adopts the domain as the zone apex and never creates or deletes it. It creates a subzone per cluster under it and deletes only those subzones when a cluster domain is removed. * If Avura cannot be asked at that moment (it is unreachable, or the connection token was rejected), the change is refused and nothing is saved. Try again once the connection shows **connected**. * Switching from one root to another follows the same checklist as any root change: cluster domains and DNS records under the old root have to be removed first. See [the preview URL](/concepts/applications#the-preview-url) for the procedure. Zones under the old root are torn down in the account that created them. ## Disconnect You can disconnect from either side. In Avura, **Disconnect** on the **Ankra Platform** card asks Ankra to drop the connection and then revokes the platform token and every token minted from it. In Ankra, admins can use **Disconnect** on **Organisation** → **Settings** → **Avura**. Ankra refuses to disconnect while something still depends on the connection: * a custom DNS zone still uses the `avura` credential, or * the Custom Ankra domain is an Avura domain from this connection. While generated cluster domains are rooted on an Avura domain, **Disconnect** on **Settings** → **Avura** is disabled and says why; move the Custom Ankra domain off Avura to enable it. When a custom DNS zone is what blocks it, the refusal points you to the DNS records page. Remove what depends on the connection first, then disconnect again. The same rule refuses reconnecting the organisation to a *different* Avura organisation while either is true; rotating the token within the same Avura organisation is always allowed. > [!WARNING] > > When Ankra refuses or cannot be reached, Avura keeps the connection working and offers **Revoke anyway**. That revokes every platform token in Avura immediately, whatever Ankra says. Clusters stop publishing to your Avura domains and generated cluster domains on an Avura root stop updating until you connect again. Use it when you need to cut Ankra off now, not as the normal way out. Unlinking the workspace from its Ankra organisation also revokes the platform tokens first. > [!NOTE] > > Deleting the `avura` credential on the **Credentials** page, with `ankra credentials delete` or through Ankra AI is the same as **Disconnect**: it runs the checks above and is refused with the same message while something still depends on the connection. ## Troubleshooting **Avura says your Ankra session expired.** Avura connects and disconnects with the Ankra sign-in stored from your last **Continue with Ankra**, and that sign-in expires. Sign out of Avura, sign in again with **Continue with Ankra**, and retry. **Avura refuses with a permission error, or the Ankra organisation is not offered.** You have to be an admin (or owner) of the linked Ankra organisation, and the membership has to be active. Ankra also refuses when your organisation requires multi-factor authentication and your account has not enrolled. Check the organisation on the **Ankra Platform organisation** card is the one you administer; linking an existing workspace lists only organisations where you are owner or admin and that are not linked to another workspace. **Link Ankra account fails.** The Ankra email and the Avura email must be the same address, and both must be verified. If signing in with Ankra sends you back with "an account already exists", sign in with your Avura password and link Ankra from **Settings** → **Security**. **Settings > Avura shows token rejected.** The platform token was revoked in Avura, or the workspace was unlinked. Connect again from Avura's **Ankra Platform** card. **Settings > Avura shows unavailable, or the shared domain count is Unknown.** Ankra could not reach Avura. Nothing is changed while it is unreachable: record changes and root domain changes are refused rather than half applied. When a record change reports that Avura's DNS server is unavailable, Ankra has already asked for a zone re-sync; refresh the page before retrying. **Avura says the Ankra Platform is unavailable.** Avura could not reach Ankra within its time limit. The previous connection state is kept, so retry later. **A domain is missing from the Avura list in Ankra.** Only domains with **Use with Ankra Platform** turned on are shared. Turn it on in Avura and refresh. # AWS Clusters Source: https://docs.ankra.ai/guides/aws-clusters Create a self-managed kubeadm or k3s cluster on EC2 instances in your AWS account, in a VPC Ankra creates or one you already own, with Ankra installing and operating Kubernetes. Choose self-managed EC2 over [Amazon EKS](/guides/eks-clusters) when you want the control plane on instances you own, with no EKS control-plane fee, or when the cluster has to join a VPC you already run. Ankra creates the network (or adopts yours), a bastion, the control plane and worker instances, installs Kubernetes with the AWS cloud controller manager and EBS CSI driver, and then operates the cluster - this is an **Ankra Managed** cluster. The [comparison table](/guides/managed-kubernetes#cloud-managed-or-ankra-managed) sets the two side by side. ## Before you start * An **AWS credential** whose role carries the **Provisioning** scope (the one write role for EKS and self-managed clusters) or the EC2-only **Self-managed** scope; access keys with the equivalent policy also work. A provisioning stack launched before the self-managed statements were added must be updated. See [AWS Credentials](/platform/credentials/aws#provisioning). * An **SSH key credential** - your own public key, or one Ankra generates. See [SSH Key Credentials](/platform/credentials/ssh-key). * A decision on the **network**. Nothing needs to exist in the account beforehand unless you adopt a VPC. ### Created or adopted VPC * **Created VPC** (the default; leave out `vpc_id`). Ankra creates a VPC from `network_ip_range` (default `10.0.0.0/16`, any `/16` to `/20`) with, per availability zone, a private `/20` for the nodes and a public `/24` for the bastion and NAT gateway, an internet gateway and route tables. It uses one zone when the control plane has fewer than three nodes and three zones otherwise, or the `availability_zones` you name; more than one zone needs at least three control planes, so etcd keeps quorum when a zone is lost. Teardown removes all of it. * **Adopted VPC** (`vpc_id`). Ankra builds inside a VPC you already run: private `node_subnet_ids` for the control plane and workers (spread them across zones for a multi-AZ cluster) and a public `bastion_subnet_id`. Ankra never creates, modifies or deletes your VPC, subnets, internet gateway, NAT gateways, DHCP options or existing route tables. `network_ip_range`, `availability_zones` and `nat_gateway_single_zone` are refused alongside `vpc_id`, and the subnet fields without it. Nodes never get a public IP, so they need an egress path (`egress_mode`): | Mode | Where | What it is | Cost | | - | - | - | - | | `nat_gateway` | Created VPC (the default there) | An Ankra-created NAT gateway per zone, so egress survives a zone outage; `nat_gateway_single_zone: true` shares one | About USD 32 a month per gateway plus per-GB processing ([VPC pricing](https://aws.amazon.com/vpc/pricing/)) | | `bastion_nat` | Either | The bastion forwards node traffic. Egress is capped by the bastion's network performance, and stops while it restarts or resizes | The bastion and its Elastic IP only | | `existing` | Adopted VPC | Your NAT gateway, NAT instance or transit path already on the node subnets' route table; Ankra changes nothing | What you already pay | For an adopted VPC, preflight picks the mode when you leave it out: subnets that already have egress get `existing`, empty subnets without it get `bastion_nat`. `bastion_nat` is always refused when a node subnet holds an instance Ankra did not create, because re-pointing that subnet's default route would take over its egress; create a NAT gateway and use `existing` instead. [Egress modes](/reference/aws#egress-modes) has the full rules. ## Create the cluster > [tab] Dashboard > > **1. Open the create dialog** > > Go to **Clusters**, click **Create cluster** and pick **AWS** under **Ankra Managed** (the **AWS** card under **Cloud Managed** creates [EKS](/guides/eks-clusters)). > > **2. Basics** > > Name the cluster and pick the AWS credential, the SSH key and the region. A read-only (cost) credential, or a provisioning role whose stack predates the self-managed statements, is listed but refused at preflight with the missing permission named. > > **3. Network** > > Keep **Create a new VPC (recommended)** - optionally with your own network range and availability zones - or switch to **Use an existing VPC** and pick the VPC, the node subnets and a public bastion subnet; the wizard shows each subnet's zone and route. Choose the egress. **Bastion allowed IPs** starts as `0.0.0.0/0`: SSH is key-only, password login is off and sshd is rate-limited on the host, but replace it with your own CIDRs to close the port to everyone else. Ankra's own addresses stay admitted. > > **4. Compute** > > Pick the control plane count (1, 3 or 5) and instance type, one or more worker node groups, the bastion instance type (a burstable type such as `t3.small` is enough, unless it is the NAT), the root volume size (an encrypted gp3 volume, 40 GiB by default), the Ubuntu series (24.04 by default) and what happens to EBS volumes on teardown. Instance types load live with prices; only `amd64` types are offered. > > **5. Kubernetes** > > Keep **kubeadm** (the default, Cilium only) or pick **k3s**, the version, and the etcd topology or CNI. The CNI defaults to Cilium; a k3s cluster can pick Calico or flannel, but only Cilium and Calico enforce the [IMDS guard](/reference/aws#imds-guard), so flannel gets a preflight warning. See [Choices fixed at create time](/guides/operate-ankra-managed-clusters#choices-fixed-at-create-time). > > **6. GitOps** > > Optionally connect a Git repository. Two checkboxes are on by default: > > * **Include Networking Stack** - Traefik, cert-manager and a Let's Encrypt ClusterIssuer, with Traefik behind a Network Load Balancer the AWS cloud controller creates. > * **Include DNS Integration** - a delegated subdomain on `ankra.cc` with external-dns wired, so Ingress hostnames publish their DNS records automatically. > > **7. Review** > > Click **Run preflight**. It reports the network ownership, the zones it resolved, the egress mode, the AMI, and the vCPU, VPC, NAT gateway, Elastic IP and security-group quotas. Once nothing is an error, click **Create cluster** and follow the progress view. > [tab] CLI > > ```bash theme={null} > # Provisioning role: print the external ID and CloudFormation link, launch the stack, then store the role > ankra credentials aws onboarding --scope provisioning # or --scope self_managed for the EC2-only role > ankra credentials aws create-role --name my-aws --role-arn --external-id \ > --region eu-north-1 --scope provisioning > # Or access keys (the secret is asked for on a masked prompt) > ankra credentials aws create-keys --name my-aws-keys --access-key-id --region eu-north-1 > > ankra credentials hetzner ssh-key create --name my-ssh-key --generate # any SSH key credential works > ``` > > The external ID must be the one `onboarding` printed and the stack was launched with; a role stored at the `cost` scope is refused at create. Look up what the credential can reach with `ankra cluster aws regions`, `availability-zones`, `instance-types`, `images` and `pricing` (all take `--credential-id` and `--region`), and `vpcs` and `subnets --vpc-id ` when you adopt a VPC. > > `preflight` takes the same flags as `create` and creates nothing. A three-zone cluster in a created VPC: > > ```bash theme={null} > ankra cluster aws preflight \ > --name my-cluster \ > --credential-id \ > --ssh-key-credential-id \ > --region eu-north-1 \ > --bastion-allowed-ips 203.0.113.10/32 \ > --control-plane-count 3 \ > --control-plane-type t3.medium \ > --worker-count 3 \ > --worker-type t3.large > # When it reports no errors, run the same command with create instead of preflight > ``` > > `--network-ip-range`, `--availability-zones`, `--egress-mode nat_gateway|bastion_nat` and `--nat-gateway-single-zone` shape a created network. To adopt a VPC instead: > > ```bash theme={null} > ankra cluster aws create \ > --name my-cluster \ > --credential-id \ > --ssh-key-credential-id \ > --region eu-north-1 \ > --vpc-id \ > --node-subnet-ids ,, \ > --bastion-subnet-id \ > --egress-mode existing \ > --bastion-allowed-ips 203.0.113.10/32 \ > --control-plane-count 3 \ > --worker-count 3 > ``` > > `--distribution k3s`, `--cni`, `--cni-features`, `--retention-policy retain|delete`, `--environment`, `--criticality` and the `--gitops-*` flags cover the other choices. Every flag is in the [CLI reference](/reference/cli/cluster). > [tab] API > > Preflight takes the same body as create and answers with findings - `can_proceed`, `network_ownership`, `resolved_availability_zones` and `resolved_egress_mode` - instead of a cluster: > > ```bash theme={null} > curl -X POST https://platform.ankra.app/api/v1/clusters/aws/preflight \ > -H "Authorization: Bearer $ANKRA_API_TOKEN" \ > -H "Content-Type: application/json" \ > -d '{ > "name": "my-cluster", > "credential_id": "", > "ssh_key_credential_id": "", > "region": "eu-north-1", > "availability_zones": ["eu-north-1a", "eu-north-1b", "eu-north-1c"], > "egress_mode": "nat_gateway", > "bastion_allowed_ips": ["203.0.113.10/32"], > "control_plane_count": 3, > "control_plane_type": "t3.medium", > "node_groups": [ > {"name": "default", "instance_type": "t3.large", "count": 3} > ], > "retention_policy": "retain" > }' > ``` > > Send the same body to `POST /api/v1/clusters/aws` to create the cluster. It answers `cluster_id`, `kind: "aws"`, `state: "creating"` and an `operation_id` that tracks provisioning. To adopt a VPC, replace the network fields with `"vpc_id"`, `"node_subnet_ids"`, `"bastion_subnet_id"` and `"egress_mode": "existing"` or `"bastion_nat"`; the created-network fields are then refused with `422`. Every field and default is in the [AWS Reference](/reference/aws). Ankra's AI can create the cluster in chat too, running the preflight for you. ## Verify 1. The cluster moves from **Provisioning** to **Online** in the clusters list once the Ankra Agent has connected. 2. Point `kubectl` at it through Ankra (this needs a [Cluster Access](/guides/cluster-access) grant) and check the nodes: ```bash theme={null} ankra cluster kubeconfig add my-cluster --use kubectl get nodes ``` Every control plane and worker should be `Ready`. See [Accessing Clusters with kubectl](/guides/kubeconfig). ## What Ankra created in your account * **The network**, in a created VPC: the VPC, subnets, internet gateway, route tables and, in `nat_gateway` mode, NAT gateways with their Elastic IPs. In an adopted VPC, at most one route table (`bastion_nat` mode). * **Two security groups** (nodes and bastion), an imported **key pair**, one **Elastic IP** for the bastion, and the **bastion**, control plane and worker instances with encrypted gp3 root volumes and no public IPs on the nodes. * **Two IAM roles with instance profiles**, `ankra-k3s--cp` and `ankra-k3s--node`, that the cloud controller manager and EBS CSI driver use. The credential you gave Ankra is never copied into the cluster. * **The AWS cloud-provider stack**: the cloud controller manager (load balancers for `LoadBalancer` Services), the EBS CSI driver (encrypted gp3 volumes by default) and the IMDS guard. Everything carries `ankra.cloud/cluster-id=`, `ankra.cloud/managed=true` and `kubernetes.io/cluster/=owned`, and the credential's write permissions are scoped to those tags. [What Ankra creates](/reference/aws#what-ankra-creates) has the full list and the network diagram. ## AWS specifics * **Node groups follow the node subnets.** New nodes are balanced across the cluster's zones - each takes the zone with the fewest instances - unless the group is pinned with `--availability-zone` (API `availability_zone`). Pin a group that runs zonal storage: an EBS volume cannot attach from another zone. Every node carries `topology.kubernetes.io/zone` and `topology.kubernetes.io/region`, so `topologySpreadConstraints` work as they are. * **An instance type change replaces nodes.** EC2 cannot resize a running instance, so each node of the group is drained, terminated and recreated on the new type, one at a time. * **Control plane.** A cluster whose nodes span more than one zone needs three control planes and cannot go below three. `ankra cluster aws control-plane get|set-count|set-instance-type` manage it. * **Restart** is an EC2 reboot; if the instance does not respond it falls back to a stop and start, which keeps the private IP. `ankra cluster aws nodes cloud-init-log` reads a node's cloud-init status over the bastion. OS-level diagnosis and repair over SSH run through Ankra's AI (`ssh_diagnose_node`, `ssh_repair_node`). * **Bastion.** Resize it from **Nodes → Bastion & VMs** or with `PUT /api/v1/clusters/aws/{cluster_id}/bastion/instance-type`; there is no CLI verb on AWS. The instance is stopped, changed and started, keeping its Elastic IP. SSH as `ubuntu` through it (`ankra cluster aws access-info`); only `bastion_allowed_ips` and Ankra's addresses reach it. * **Upgrades** move the AWS cloud controller manager with the nodes: its minor version is pinned to the cluster's Kubernetes minor. > [!WARNING] > > In `bastion_nat` mode the bastion is every node's default route, so a bastion resize or restart stops image pulls, the Ankra Agent connection and the cloud controller's AWS calls until it is back. ### Stopping and starting A stop stops every node and the bastion rather than terminating them, so the instances keep their root volumes and private IPs, and the bastion keeps its Elastic IP. Start brings the same instances back and etcd rejoins on the same addresses, so the cluster returns with its state, not as a rebuild. Ankra records each instance's private IP at create and checks it after every start; a difference is reported as drift. While stopped, compute is not billed, but EBS storage, the Elastic IP, load balancers and - in a created VPC - the NAT gateways are, so a cluster parked for weeks on `nat_gateway` egress is cheaper to terminate. A stop never deletes the volumes the CSI driver provisioned, whatever the `retention_policy`. ### Terminating A terminate terminates the instances, releases the bastion's Elastic IP, and deletes the security groups, key pair and IAM roles. In a created VPC it then removes the network in reverse order - NAT gateways with their Elastic IPs, route tables, subnets, internet gateway, VPC - by the tags Ankra set. In an adopted VPC it restores each node subnet's previous route table association before deleting the one route table it created, and leaves the rest of your network alone. EBS volumes the CSI driver provisioned, and load balancers the cloud controller created, follow the cluster's `retention_policy`, chosen at create: * **`retain`** (the default) keeps them in your account, where they keep billing. The teardown result lists their IDs, and they carry the cluster ID tag. `--force` still deletes the load balancers, never the volumes, and no volume question is asked. * **`delete`** removes those tagged `ankra.cloud/managed=true` for this cluster - the EBS volumes only once you accept it. With a created VPC, load balancers are always deleted, because they cannot outlive the VPC; volumes still follow the policy. ```bash theme={null} ankra cluster aws deprovision # names the EBS volumes a delete cluster loses, and asks ankra cluster aws deprovision --accept-volume-data-loss # for scripts ``` ## Operate it Day-2 tasks work the same way on every Ankra Managed provider, with the CLI name `aws`: * [Node groups](/guides/operate-ankra-managed-clusters#node-groups) and [legacy worker scaling](/guides/operate-ankra-managed-clusters#legacy-worker-scaling) * [Control plane](/guides/operate-ankra-managed-clusters#control-plane) * [Restart a node](/guides/operate-ankra-managed-clusters#restart-a-node) and [the bastion](/guides/operate-ankra-managed-clusters#bastion) * [SSH access and keys](/guides/operate-ankra-managed-clusters#ssh-access-and-keys) * [Upgrade Kubernetes](/guides/operate-ankra-managed-clusters#upgrade-kubernetes) * [Stop and start](/guides/operate-ankra-managed-clusters#stop-and-start) * [Terminate a cluster](/guides/operate-ankra-managed-clusters#terminate-a-cluster) ## Troubleshooting | Issue | Solution | | - | - | | Preflight refuses the credential | It is read-only (cost scope), or its provisioning stack predates the self-managed statements. Add a [provisioning credential](/platform/credentials/aws#provisioning) or update the stack to the published template | | The preflight `cidr` item is an error | `network_ip_range` does not hold a private `/20` and a public `/24` per zone - a `/20` VPC fits one zone. Widen the range (up to `/16`) or use fewer zones | | Preflight refuses the zones | A named zone does not exist or is not available, or several zones were given with fewer than three control planes. Check `ankra cluster aws availability-zones` | | Preflight reports the VPC or NAT gateway quota | The account is at its VPCs-per-region (`L-F678F1CE`) or NAT-gateways-per-zone (`L-FE5A380F`) limit. Request an increase, remove unused clusters, or use `bastion_nat` or `nat_gateway_single_zone` | | Preflight refuses `bastion_nat` (adopted VPC) | A node subnet holds an instance Ankra did not create. Pick an empty subnet, or create a NAT gateway and use `existing` | | Preflight refuses `existing` (adopted VPC) | The node subnets have no `0.0.0.0/0` route to a NAT gateway, NAT instance or transit path. Add one, or use empty subnets with `bastion_nat` | | `node_subnet_ids` or `network_ip_range` refused with `422` | Subnet fields belong to an adopted VPC and network fields to a created one - send `vpc_id` with subnets, or neither | | `bastion_allowed_ips` refused | An entry is not an IPv4 CIDR. `0.0.0.0/0` and `::/0` are accepted, and an empty list means `0.0.0.0/0` | | Cluster stuck in provisioning | Check the vCPU quota for the instance family, the Elastic IP and NAT gateway quotas, and - in an adopted VPC - that the bastion subnet routes to the internet gateway | | Nodes stay `NotReady` with the `node.cloudprovider.kubernetes.io/uninitialized` taint | The cloud controller cannot match the node to its instance - see below | | `LoadBalancer` Service stuck in `Pending` | Check the cloud controller pods in `kube-system`. On a fresh account the first load balancer also creates the Elastic Load Balancing service-linked role, which the credential allows | | PVC stuck in `Pending` | Check the EBS CSI controller pods; a pod pinned to a zone with no volume there needs a node group in that zone | | Cannot SSH to the bastion | Your address is not in `bastion_allowed_ips`, or the security group was edited outside Ankra | | Nodes lost egress after a bastion resize | Expected in `bastion_nat` mode while the bastion restarts | | Create result reports `imds_guard: unavailable` | The cluster runs flannel, which cannot enforce the IMDS guard - see below | **Cloud controller cluster ID and node names.** The cloud controller finds a cluster's instances by the `kubernetes.io/cluster/=owned` tag and matches each node to an instance by name. Ankra sets both: the tag on every instance, and each node name to the instance's EC2 private DNS name as `DescribeInstances` reports it (k3s gets `--node-name`), so a VPC whose DHCP options set carries a custom `domain-name` does not break the match, and changing the DHCP options later affects no node. Removing or editing the tag in the console makes the controller treat the instance as foreign until the tag is back. If a node was renamed or re-registered by hand, delete its Kubernetes node object and restart the node from **Nodes**; it registers again under the right name. **Instance metadata (IMDS).** Every instance requires IMDSv2, so tools that use IMDSv1 fail on the nodes. Workers and the bastion have a hop limit of 1, so pods cannot reach the instance role. Control planes have a hop limit of 3 so the EBS CSI controller can use the control-plane role; the IMDS guard, a network policy rendered for Cilium and Calico, blocks every other pod from `169.254.169.254`. On flannel there is no policy engine, so a pod scheduled onto a control plane can read that role's credentials: prefer Cilium or Calico, or keep the control planes tainted. Give a workload that needs AWS access its own credentials; do not raise the hop limits or re-enable IMDSv1 - Ankra restores the hardening on reconciliation. **AWS service quotas.** Preflight checks the quotas that most often block a create, but a quota can be used up between preflight and create, and the per-cluster quota report answers `404` for AWS. If provisioning fails with a quota error, check the [Service Quotas console](https://console.aws.amazon.com/servicequotas/) for the region - On-Demand vCPUs for the instance family, VPCs per region, NAT gateways per zone, Elastic IPs (one for the bastion plus one per NAT gateway), security groups per interface and rules per group, and Network Load Balancers - then create again. A failed create leaves only tagged objects, which Ankra removes on the next attempt. # Backups and Restore Points Source: https://docs.ankra.ai/guides/backups Register an S3-compatible bucket as a backup vault, protect a stack on a schedule, take and inspect restore points, restore a stack in place, and follow the runs that move the data. > [!NOTE] > > A **backup vault** is an object-storage bucket your organisation owns that Ankra writes cluster backups to, and a **restore point** is one immutable copy of a stack's data inside it. Register a vault once, protect a stack, and every backup, restore and clone is a verb over the restore points that land there. ## Why a vault first Ankra's answer to "copy this database to another cluster" is the industry one: take a backup, then restore it on the target. That keeps data off the platform entirely - the source cluster writes to the bucket, the target reads from it over HTTPS - and it means a disaster-recovery copy exists as a side effect of every clone. That only works with somewhere to write to, which is what a vault is. Registering one is a one-time, organisation-level step. > [!NOTE] > > **Pipelines are the exception, and they do not wait for this.** A pipeline step's log, a `scan` stage's reports and every declared artifact are kept in an Ankra-managed default vault for an organisation that has no ready vault of its own, under a key prefix of that organisation, so a first [Ankra Pipelines](/guides/ankra-pipelines#before-your-first-run) run works before anybody registers a bucket. Backups, restore points, cloning a stack with its data and `ankra migrate` data never use it - those are organisation-scoped and are what a vault of your own is for. Register one and it takes precedence for pipeline artifacts too, from the next step onward; objects already written stay where they are. ## What a vault records | Field | What it is | | - | - | | Name | How the vault is referred to in policies and the CLI | | Provider | The object-storage service, or `other` for any S3-compatible endpoint | | Endpoint | The S3 endpoint URL | | Bucket | The bucket backups are written to | | Region | Only needed when the endpoint does not imply it | | Path style | `https://endpoint/bucket` addressing - required by most self-hosted S3 services | | Status | `ready`, `error` (with the failure excerpt), or `provisioning` while Ankra is still creating the bucket or checking the keys | | Kind | `ankra_provisioned` when Ankra created the bucket from one of your provider credentials, `customer_s3` when you registered a bucket you already had | The access key ID and secret you provide are written to Ankra's secret store and never returned by the API, the dashboard, or the CLI. Rotating keys means creating a new vault or re-verifying after updating the bucket policy. ## Let Ankra create the bucket The quickest path: pick one of your organisation's provider credentials and a region, and Ankra creates the bucket, mints or stores the access keys, verifies the bucket and registers the vault. The vault shows **Provisioning** until that has finished, then **Ready** - or **Failed** with the excerpt of what the provider refused. | Credential | What Ankra does with it | What you still supply | | - | - | - | | UpCloud | Creates a Managed Object Storage service in the region, a user with the `ECSS3FullAccess` policy and an access key, then the bucket | Nothing - the region is checked against your account's list | | DigitalOcean | Mints a Spaces access key with the API token and creates the Space | Nothing | | Scaleway | Uses the credential's own IAM key pair (Scaleway API keys are S3 keys) and creates the bucket in the credential's project | Nothing | | Hetzner | Creates the bucket at the chosen location with the Object Storage key pair you provide | The key pair: Hetzner issues it in the Cloud Console (**Object Storage > Manage credentials**), and its Cloud API cannot mint one - so neither can Ankra | Other credential kinds (OVH, AWS, GCP, Azure, Proxmox, Morpheus) cannot provision a bucket yet - register a bucket you own instead, below. > [tab] Dashboard > > **1. Open Backups** > > Go to your organisation's **Settings** and choose **Backups**, then **Add vault** and **Let Ankra create the bucket**. > > **2. Press the button** > > The dialog opens ready: a credential Ankra can complete on its own is already selected, the vault is named for you, and the region is that provider's usual one. It says what it is about to do - "Ankra will create a bucket in europe-1 on UpCloud Object Storage and register it as 'backups'." - so pressing **Create bucket and vault** is the whole flow. > > **3. Hetzner: paste the Object Storage keys** > > A Hetzner credential is the one that cannot be a single click, and the picker labels it **(needs keys)**: the form asks for the access key and secret key from the Cloud Console. They are stored in Ankra's secret store and never shown again. > > **4. Only if you want to change something** > > **Advanced** opens the name, region and bucket for editing. Everything there already has a value; you never have to touch it. > > **5. Watch it come up** > > The row shows **Provisioning** with "Creating the bucket on ..."; the page refreshes on its own until it reads **Ready**. A **Failed** vault shows what the provider answered - a region your account cannot use, a bucket name someone else holds, a token without the right scope - and can be deleted and re-added once that is fixed. > [tab] CLI > > ```bash theme={null} > ankra backup vaults provision > ``` > > That is the whole command. The vault is named `backups` (then `backups-2` and so on), the credential is the only one Ankra can provision from, and the region is that provider's usual one - and the command prints all three before it creates anything. If the organisation has several usable credentials it asks which, naming them. > > Every default is an override, not a prerequisite: > > ```bash theme={null} > ankra backup vaults provision offsite --credential upcloud-main --region europe-1 --bucket my-backups --wait > ``` > > `--credential` takes a credential name or id. `--wait` blocks until the vault is `ready` (or exits non-zero with the failure excerpt); without it the command returns as soon as the platform has accepted the request, and `ankra backup vaults get offsite` shows progress. > > For a Hetzner credential the command prompts for the Object Storage access key and (hidden) secret key when they are not passed with `--access-key-id` / `--secret-access-key`. > [!NOTE] > > Ankra records the provider-side handle it created (the UpCloud service, the DigitalOcean key) on the vault, so it can clean them up later if you ask it to - see [Deleting a vault](#deleting-a-vault). ## Bring your own bucket > [tab] Dashboard > > **1. Open Backups** > > Go to your organisation's **Settings** and choose **Backups**. > > **2. Add a vault** > > Click **Add vault** and pick the provider first: the dialog fills in the endpoint and region shapes that provider uses, sets **Path-style addressing** to what it expects (off for Amazon S3, on everywhere else), and says where its access keys are issued. Then enter the endpoint, bucket, region (if needed) and the keys. > > **3. Watch the verification** > > Ankra immediately checks the credentials against the bucket. A vault that passes shows **Ready**; one that does not shows **Failed** with the excerpt of what the bucket answered and a **Verify again** action - see [When verification fails](#when-verification-fails). > [tab] CLI > > ```bash theme={null} > ankra backup vaults create production-backups \ > --provider upcloud \ > --endpoint https://fi-hel2.upcloudobjects.com \ > --bucket ankra-prod-backups \ > --region fi-hel2 > ``` > > The access key ID and secret are prompted for when not passed as flags - the secret hidden - so they stay out of your shell history. Prefer the prompt over `--access-key-id` / `--secret-access-key`. > > A vault that fails its first verification exits non-zero with the failure excerpt instead of a green create. ## Provider notes | Provider | Endpoint shape | Notes | | - | - | - | | UpCloud Object Storage | `https://.upcloudobjects.com` | Region is the endpoint prefix, for example `fi-hel2`. Note that *provisioning* takes a Managed Object Storage region instead - `europe-1`, `us-1` - which Ankra checks against your account | | DigitalOcean Spaces | `https://.digitaloceanspaces.com` | Spaces keys are separate from API tokens | | Scaleway Object Storage | `https://s3..scw.cloud` | Uses the same key pair as the API | | Hetzner Object Storage | `https://.your-objectstorage.com` | Keys are issued in the console, not the cloud API | | Amazon S3 | `https://s3..amazonaws.com` | Turn **path style** off | | Other | Anything S3-compatible | MinIO, Garage, Ceph RGW; keep path style on | ## When provisioning fails or stalls A vault Ankra provisions shows **Provisioning** until the bucket exists and its keys verify. Two things can go wrong, and both are recoverable the same way. **It reads Failed with an excerpt.** The excerpt is what the provider answered - a region your account cannot use, a bucket name someone else holds, a token without the right scope. There is nothing to re-verify: the run never minted keys, so **Verify** is not offered and the API refuses it. Delete the vault - tick **Also destroy the bucket Ankra created**, or pass `--destroy-provider-resources`, so anything the run got as far as creating goes with it - and provision again with the excerpt's cause fixed. **It stays on Provisioning.** Nothing retries a provisioning run, so a row that has not moved for quarter of an hour is stuck rather than slow, and the dashboard says so. Delete it the same way and try again. ## When verification fails A **Failed** vault is still registered - nothing has to be redone from scratch - but the keys you gave it are never read back, so they cannot be edited in place. The recovery depends on what the excerpt says: | Excerpt | Cause | Fix | | - | - | - | | `403 Forbidden` / `AccessDenied` | The key is valid but cannot reach this bucket | Fix the bucket policy or the key's permissions, then **Verify again** (`ankra backup vaults verify `) | | `InvalidAccessKeyId` / `SignatureDoesNotMatch` | The key pair is wrong | Delete the vault and add it again with the corrected keys | | `NoSuchBucket` | The bucket name or region is wrong | Delete and re-add with the right bucket; on providers whose region is part of the endpoint, check the endpoint too | | Connection or DNS errors | The endpoint is wrong or unreachable from Ankra | Check the endpoint URL (it must start with `https://`) and re-add, or fix the network path and **Verify again** | **Verify again** re-runs the same check the create ran; the row updates in place with the new status and the time it last verified. ## Manage vaults ```bash theme={null} ankra backup vaults list # every vault with status and last verification ankra backup vaults get production-backups # details, including a failure excerpt ankra backup vaults verify production-backups # re-run the credential check ankra backup vaults delete production-backups # confirms first; --yes to skip ``` `get`, `verify` and `delete` accept a vault name or id, and `list` / `get` support `-o json|yaml`. `verify` on a vault that is still provisioning answers that it is still being provisioned rather than recording a failed check. ## Deleting a vault Deleting a vault removes Ankra's record of it and the access keys it stored. **By default the bucket is left exactly as it is** - with every restore point in it - along with anything Ankra created for it, so a deletion can never lose data on its own. > [!WARNING] > > Nothing stops you deleting a vault that is still in use: Ankra does not yet refuse a delete on the grounds that something references the vault. Check before deleting one. One exception, and it is deliberate: deleting a vault that is **still provisioning** always tears down the provider side, whether or not you ask for it. Such a vault has no restore points to lose, but the run may already have created a bucket or a provider resource - and once the record is gone nothing else can find them. For a vault **Ankra provisioned**, you can ask for the cloud side to be cleaned up in the same step. Ankra then empties and deletes the bucket and removes what it minted for the vault - the UpCloud Managed Object Storage service, the DigitalOcean Spaces key. Scaleway and Hetzner vaults use your own credential's keys, which are never touched. > [!WARNING] > > **This destroys data.** Every restore point in the bucket is gone, and nothing restores from that vault afterwards. Ankra refuses it for a bucket you registered yourself: that one is yours, and only you should delete it. > [tab] Dashboard > > In the delete dialog, tick **Also destroy the bucket Ankra created**. The box is unchecked every time the dialog opens, names the bucket and its provider, and the confirm button becomes **Delete and destroy bucket**. A bring-your-own vault shows no such box. > [tab] CLI > > ```bash theme={null} > # Forget the vault; the bucket stays in your cloud account > ankra backup vaults delete offsite > > # Forget the vault and destroy the bucket Ankra created for it > ankra backup vaults delete offsite --destroy-provider-resources > ``` > > The confirmation prompt names the bucket before anything is destroyed. `--yes` skips it, so use that only in scripts that already know what they are deleting. The teardown runs as a background job: the vault disappears from the list immediately and the bucket is removed shortly after. Each of its steps is written to be repeatable - a bucket already gone, a service already deleted and keys already removed all count as done - but nothing retries it automatically today. If the teardown fails, the provider resources stay and have to be removed in the provider console. ## Protect a stack A vault is where restore points land; a stack's backup policy is what puts them there. Protection is a property of the stack, not a separate object: it is stored as the stack's own `backup` block, and the platform converges on it by installing the [backup data plane](/concepts/backup-vaults#the-backup-data-plane) on the cluster. Look at what a backup would have to carry first: ```bash theme={null} ankra cluster stacks data list shop ``` That lists the stack's data assets - standalone persistent volume claims, and the databases an operator runs - under the names the selection uses. > [tab] CLI > > ```bash theme={null} > ankra cluster stacks protect shop \ > --vault production-backups \ > --schedule daily \ > --retention daily=7,weekly=4,monthly=6 \ > --include-pvc shop/data \ > --backup-now --wait > ``` > > The command prints the policy it stored and then `Backup stack: installing` until the data plane is actually on the cluster, so nothing is called protected while the Velero that would do the protecting is still arriving. A capture started before then waits rather than failing. > > A policy that cannot work is refused with every field-level reason at once, so a bad schedule and a bad retention are fixed in one edit: > > ``` > The backup policy is not valid. > - backup.schedule: 'evry day' is neither a five-field cron expression nor one of hourly, daily, weekly. > ``` > [tab] Cluster file > > The same policy in your cluster file, applied with `ankra cluster apply -f cluster.yaml`: > > ```yaml theme={null} > stacks: > - name: shop > backup: > enabled: true > vault: production-backups > schedule: daily > retention: > hourly: 0 > daily: 7 > weekly: 4 > monthly: 6 > yearly: 0 > minimum_count: 3 > minimum_age: 24h > selection: > databases: true > persistent_volume_claims: > - shop/data > confirm_exclude_databases: false > ``` > > Every field, with its default, is in the [`backup` block reference](/reference/importcluster#spec-stacks-backup). What `protect` takes: | Flag | Meaning | | - | - | | `--vault` | **Required.** The vault restore points are written to, by name or id. A protected stack with nowhere to write to is not protected | | `--schedule` | `hourly`, `daily`, `weekly`, or a five-field cron expression. Default `daily` | | `--retention` | `key=value` pairs from `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `minimum-count`, `minimum-age` | | `--include-pvc` | Repeatable, `namespace/name`. Volumes are captured only where named | | `--exclude-databases` | Leave the stack's databases out. Needs `--confirm-exclude-databases` beside it | | `--backup-now` | Take the first restore point immediately instead of waiting for the schedule | | `--wait` | Follow the first capture's run until the restore point is sealed | Ankra takes the scheduled restore points itself, on the schedule the block names, through the same capture as **Back up now** - so every scheduled backup is a restore point you can list, restore and let retention expire. Nothing is scheduled inside the cluster for any engine: no Velero `Schedule`, no CloudNativePG `ScheduledBackup`, no schedule entry inside a Percona cluster. A Percona cluster is captured into the backup storage its own spec already declares. For a CloudNativePG cluster, protecting the stack writes two things into it, because CloudNativePG writes a backup wherever the `Cluster` itself says: the barman-cloud archiver goes into the member that declares the `Cluster` (the manifest body, or `cluster.plugins` in the `cloudnative-pg/cluster` chart's values), and a stack member named `ankra-backup-policy-` holds the object store it archives into. Both are Ankra's: `unprotect` removes them, and a clone or a profile capture leaves them out of the copy while the source stack keeps them. An archiver you configured yourself - your own object store, an in-tree `spec.backup.barmanObjectStore`, the chart's own `backups` section - is left exactly as you wrote it, and Ankra reports that it cannot take restore points of that cluster into the vault: a restore point you take yourself is refused with the reason, and a scheduled backup is skipped with `archiver_unavailable` (as opposed to `archiver_not_wired` below, which means Ankra's own archiver is missing and a protect or manual restore point puts it back). Four behaviours worth knowing before you rely on it: * **Databases are in by default; volumes are not.** Protecting a stack does not start copying every claim in its namespaces. Name the volumes you want with `--include-pvc` or `selection.persistent_volume_claims`. Excluding databases from a stack that runs them needs an explicit acknowledgement, and leaves the stack carrying a standing warning. * **An omitted block preserves protection.** A cluster file that does not mention `backup` leaves the stored policy alone, so an older CLI or a partial file cannot unprotect a stack. Removing protection is an explicit `enabled: false` or `ankra cluster stacks unprotect`, and the write warns about what stops being covered. * **Protecting a CloudNativePG cluster restarts its instances once.** Ankra adds the barman-cloud archiver to the `Cluster`, and CloudNativePG reconciles that by rolling the instances one at a time - a brief primary failover. Only `protect`, `unprotect` and a restore point you take yourself do this; a scheduled backup never changes a database. If a stack's clusters lost the archiver (an `ankra cluster apply` that replaced the stack from a file without it, say), scheduled backups are skipped with `archiver_not_wired` until you protect the stack again or take a restore point by hand, and the restore point's answer says the instances restart. * **A CloudNativePG restore point needs the stack protected, and lands in the stack's own vault.** The cluster archives into the object store its policy wired, so a restore point of it cannot be written anywhere else: `restore-points create` on an unprotected stack, or into another vault, is refused with the reason, and nothing is created. A restore point taken in the same minute as the first protect can fail while the instances restart; the next one succeeds. * **Retention counts the schedule's restore points, not the ones you take by hand.** `daily=7` keeps the newest scheduled restore point of each of the last seven days. A restore point you take yourself - **Back up now**, or `restore-points create` - is kept for the policy's longest period (seven days under `daily=7`), and the safety restore point Ankra takes before an in-place restore is kept for seven days, so a restore stays undoable for a week. Neither one replaces that day's scheduled restore point, and taking another does not delete the one before it. Per-add-on overrides ride the add-on's settings, for the case where one add-on in a protected stack should be captured differently (or not at all): ```bash theme={null} ankra cluster addons settings get cnpg-cluster ankra cluster addons settings set cnpg-cluster --backup-enabled=false ``` ## Take a restore point now ```bash theme={null} ankra cluster stacks restore-points create shop --note "before the 3.2 upgrade" --wait ``` The capture is dispatched by the platform, so without `--wait` the command answers with the restore point in `creating` and the run that will seal it. `--wait` follows that run and prints the sealed restore point. The vault is resolved in order: `--vault`, then the stack's own policy, then the organisation's single ready vault. With more than one ready vault and nothing naming which, the request is refused rather than chosen for. The selection resolves the same way - the request's own `--include-pvc` / `--exclude-databases`, then the stack's stored selection, then the default of databases in and volumes only where named. A request that carried no selection never widens to everything. ## See what a restore point holds ```bash theme={null} ankra cluster stacks restore-points list shop # newest first ankra cluster stacks restore-points list shop --status complete ankra cluster stacks restore-points list shop --trigger scheduled --limit 10 ankra cluster stacks restore-points get shop 0b2f # an unambiguous id prefix is enough ankra backup restore-points list --cluster production # across every cluster ``` `get` adds the manifest, every asset in it, and the run that produced it. `list` pages with `--cursor`; `--vault` narrows to one vault, and `ankra backup restore-points list` takes `--cluster` and `--stack`. > [!WARNING] > > **Read the `Not carried` list before you rely on a restore point.** Every listing and every detail read prints it, next to what the restore point does contain - a volume outside the selection, a data engine no adapter covers, an external service that lives outside the cluster - each with the reason and the remedy. An omission nobody read is the same as an omission nobody was told about. An id prefix is resolved against the stack's 200 most recent restore points, so it is checked for ambiguity only within that window. The full id is what addresses an older restore point with certainty. ## Restore a stack in place > [!WARNING] > > **This destroys before it replaces.** The restore removes the stack's current volumes before it writes the restore point's. Confirmation is typing the stack's own name, not `y/N`. ```bash theme={null} ankra cluster stacks restore-points restore shop 0b2f --wait ``` The platform accepts the restore and answers with the sequence it will follow, which the command prints before any of it runs: 1. Scale down the workloads in the stack that own the data being restored. 2. Remove the volumes the restore point replaces, so the restored data is not written beside the old data. 3. Restore the restore point's assets onto the cluster through the backup data plane. 4. Scale the workloads back up over the restored data. `--yes` skips the typed confirmation for scripts. `--force` is needed when the stack's data has changed since the restore point was taken: the refusal compares the restore point's assets against the stack's current inventory, and **an inventory whose live database scan did not run counts as drift**, because "we could not read it" is not "it is unchanged". > [!WARNING] > > **CloudNativePG and Percona databases cannot be restored in place yet.** A restore point carrying one is refused with `Restoring this restore point in place is not supported yet: it carries a database this platform cannot yet recreate in place.` The ordinary restore path would report success over unchanged data, which is worse than refusing - see [Restoring in place](/concepts/backup-vaults#restoring-in-place). Volumes captured by Velero and databases captured as logical dumps, including every `ankra migrate` import, restore in place today. A restore is also refused against a cluster that is offline or stopped - the sequence destroys before it replaces, so it must not start against a cluster nobody can reach - and against a restore point that is not `complete`. ## Unprotect a stack ```bash theme={null} ankra cluster stacks unprotect shop ``` Confirmation is the typed stack name again; `--yes` skips it. Turning protection off stops future backups and removes the rendered schedules on the next render. **Every restore point already taken is kept and stays restorable** - removing protection is a decision about the future, not about the past. The `ankra-backup` system stack is not removed either, because those restore points are restored through it. ## Follow a run Every capture and every restore is a run, and a run has a step for each thing it does. Runs are organisation-scoped, so one listing covers every cluster. ```bash theme={null} ankra runs list --kind backup --cluster production ankra runs list --status failed ankra runs get # the plan, and every attempt of every step ankra runs cancel ankra runs retry # opens a NEW run from the step that failed ``` `retry` answers with the new run, not the old one: the failed row will never move again. `list` takes `--stack`, `--cursor` and `--limit`, and every command supports `-o json|yaml`. ## Backup posture Posture is the answer to "is this data protected, and if not, why", rendered wherever someone could lose data: a **Protected** / **Unprotected** / **Protection unknown** pill on stack headers, stack cards and the Stack Builder, a backup posture card on the cluster overview, a fleet widget on the organisation dashboard, and a **Protect your stateful stacks** milestone on the setup checklist once the organisation runs something stateful. | State | What it means | What to do | | - | - | - | | **Protected** | An enabled policy naming a vault that is ready | Nothing. Check the restore points are actually landing | | **Unprotected** | No policy; or the vault it names is missing, provisioning or in error; or databases are excluded from a stack that runs them; or the stack holds no data at all | The reason says which. A stack with no data assets is unprotected but not a finding | | **Unknown** | The `backup` block did not decode, the vault lookup could not be answered, or the cluster's inventory has not landed | Check the cluster is online and reporting; an unknown is never reported as unprotected | An organisation with no vault at all gets a call to action rather than a report that everything is exposed, and a posture that has not loaded renders nothing rather than flashing a verdict it does not have. ## Troubleshooting | What you see | Cause | Remedy | | - | - | - | | `Backups are not enabled for this organisation.` | The **backups** feature is switched off for the organisation the CLI is acting on | Check `ankra org current` first, then [contact support](/platform/support). See [Not enabled yet?](#not-enabled-yet) | | `This backup vault has not verified; re-verify it before taking a restore point.` | The vault's last credential check failed or never ran | `ankra backup vaults verify `, and [When verification fails](#when-verification-fails) for what the excerpt means | | `This organisation has more than one ready backup vault; name the one to write to.` | The request named no vault, the stack has no policy, and there is more than one candidate | Pass `--vault `, or protect the stack so its policy names one | | `This organisation has no ready backup vault to write a restore point to.` | No vault is `ready` | Register or provision one, above | | `This selection covers none of the stack's data, so the restore point would carry nothing.` | The selection excluded everything the stack holds | `ankra cluster stacks data list ` to see the asset names, then widen the selection | | `This stack's data has changed since the restore point was taken` | Drift between the restore point and the live inventory, **or** an inventory whose database scan did not run | Take a fresh restore point, or restore with `--force` if the drift is expected | | `Restoring this restore point in place is not supported yet` | The restore point carries a CloudNativePG or Percona database | Not yet available. The capture is still good and the databases are still in the vault | | `Only a complete restore point can be restored.` | The restore point is `creating` or `failed` | `ankra runs get ` on the run that was sealing it | | `This cluster is not online, so its data cannot be restored right now.` | The target cluster is stopped or its agent is not connected | Bring the cluster back, then retry | | `A backup, restore or clone is using this restore point` | A delete raced a run that is reading the restore point | Wait for the run to finish, or `ankra runs cancel ` | | `Backup stack: installing` never becomes `ready` | The cluster's agent is too old to run the backup jobs, or a resource name the backup stack needs is already taken by a user-authored resource | Upgrade the [cluster agent](/concepts/cluster-agent); rename the colliding resource. `ankra runs list --cluster ` shows steps that never got claimed | | `Its objects were left in the bucket: ...` after a delete | No sweep could be dispatched - the source cluster is gone, the vault is gone, or the agent does not carry the object-delete job | The restore point row is removed either way. Remove the objects from the bucket yourself so you stop paying for them, or upgrade the agent and let the retention sweep take them | > [!NOTE] > > Diagnose all of this with the Ankra CLI, not `kubectl`. The backup data plane is a stack Ankra owns and reconciles, so a change made against the cluster directly is reverted on the next render. ## Permissions | Permission | Allows | | - | - | | `backups.read` | Seeing vaults, policies, restore points, runs and posture | | `backups.operate` | Verifying a vault; taking restore points and running restores | | `backups.manage` | Creating and deleting vaults; protecting and unprotecting stacks; deleting restore points | Organisation admins hold all three. A member with only `backups.read` sees the vault list read-only; the page says so and names the permission an admin has to grant for adding, verifying or deleting a vault. Both gates - the organisation's **backups** feature and the permission - are checked in that order, so an organisation without the feature answers the same way whatever the caller may do. ## Not enabled yet? Backups are on for every organisation unless the **backups** feature has been switched off for yours. While it is off: * The **Backups** settings page renders an explanation instead of the vault list, and no protection pill, posture card or fleet widget appears anywhere. * `ankra backup …`, `ankra cluster stacks protect|unprotect|restore-points|data …` exit non-zero with `Backups are not enabled for this organisation.` followed by what to do about it. A real permission refusal on the same commands keeps its own wording, so the two are never confused. * The API answers `403` with that same text on every backups route, browser and token alike - a personal access token cannot reach the lane in an organisation where it is dark. * A `backup` block in a cluster file is rejected on apply with the same sentence. Two things to check before asking for it to be enabled: 1. **The right organisation is selected.** The CLI acts on the current organisation - `ankra org current` shows which, and `ankra org switch ` changes it. 2. **The account is in the organisation that was enabled.** Enablement is per organisation, not per user. Then [contact support](/platform/support) to have the **backups** feature enabled for the organisation. ## Command reference The generated CLI reference carries every flag and default of these families: | Family | Reference | | - | - | | `ankra backup vaults`, `ankra backup restore-points` | [ankra backup](/reference/cli/backup) | | `ankra cluster stacks data`, `protect`, `unprotect`, `restore-points` | [ankra cluster](/reference/cli/cluster) | | `ankra runs` | [CLI command reference](/reference/cli/index) | > [!NOTE] > > The reference is regenerated from the CLI source on each release, so the restore-point families appear there from **v0.17.0**. Until then `ankra --help` is the authority on a flag. ## Next steps > [card] [Backup vault concepts](/concepts/backup-vaults) > > How vaults, policies, restore points, runs and posture fit together. > [card] [The backup block](/reference/importcluster#spec-stacks-backup) > > Every field and default of a stack's backup policy, in the cluster file. > [card] [Clone stacks between clusters](/guides/clone-stack) > > Clone a stack's configuration today - data follows via restore points. > [card] [Move a Docker deployment in](/guides/migrate-from-docker) > > `ankra migrate` carries databases through the same vault. # Build a Bazel Repository with Ankra Pipelines Source: https://docs.ankra.ai/guides/bazel-pipelines Run Bazel builds and tests on Ankra Pipelines - Bazelisk and the Bazel output tree on the shared workspace, a disk and repository cache that survives between runs, JUnit test results from bazel-testlogs, an optional remote cache, and how a Bazel-built binary gets into an image. Bazel runs on Ankra Pipelines as an ordinary `run` stage. Ankra brings what Bazel itself leaves to the CI system: a cluster to run on, caches kept between runs, test results in the run's check, and an image build that is scanned and gated before it is published. Bazel brings hermetic builds and an action cache, so a run with a warm cache only rebuilds what changed. This guide assumes the repository is already connected to Ankra Pipelines. If it is not, start with [Get started with Ankra CI](/get-started/ankra-ci). ## What Bazel needs from a pipeline step A pipeline step differs from a laptop or a hosted CI runner in a few ways, and each one matters for Bazel. The [three rules](/guides/ankra-ci-starter-pipelines#three-rules-every-step-follows) apply to every step; this is what they mean here: * **The Bazel output tree goes under `/workspace`.** `HOME` is `/tmp`, which is 256 MiB and held in memory. Bazel's default output root is under `HOME` and outgrows that within one build, so pass `--output_user_root=/workspace/.ankra-bazel/root` on every Bazel command. * **Bazel comes from Bazelisk.** The root filesystem is read-only, so nothing can be installed into the image. The step installs Bazelisk into the workspace, and Bazelisk downloads the Bazel version pinned in `.bazelversion`. * **Downloads need network.** A `run` stage has no network by default. `defaults.network: "egress-https"` lets Bazel fetch toolchains and external dependencies from public hosts. * **Bazel needs more than the default size.** A step gets 500m of CPU and 1Gi of memory unless it asks for more. Give the Bazel stage a few cores and several gigabytes, and a longer timeout for the first cold build. ## Add a CI config to `.bazelrc` Keep the CI flags in a named config, so the pipeline only says `--config=ci` and you can run the same build locally: ```bash theme={null} # .bazelrc build:ci --disk_cache=/workspace/.ankra-bazel/disk-cache build:ci --repository_cache=/workspace/.ankra-bazel/repository-cache build:ci --color=no build:ci --curses=no build:ci --show_progress_rate_limit=10 test:ci --test_output=errors ``` `test` inherits every `build` flag, so the caches apply to tests too. On Bazel 7.4 or later, add `build:ci --experimental_disk_cache_gc_max_size=20G` to keep the disk cache from growing without limit. The caches, Bazel's output root and the Go tools all sit under `/workspace`, which is also the root of the checkout, so `bazel test //...` would walk into them. List them in a `.bazelignore` at the repository root: ```text theme={null} .ankra-bazel .ankra-go ``` ## The pipeline This `.ankra/pipeline.yaml` tests every target on each pull request and push to `main`, and reports each test in the run: ```yaml theme={null} apiVersion: ankra.io/v1 kind: Pipeline metadata: name: "my-repo" on: push: branches: ["main"] pull_request: branches: ["main"] manual: {} workspace: size: "30Gi" defaults: network: "egress-https" stages: - name: "checkout" kind: "checkout" - name: "test" kind: "run" image: "golang:1.26" needs: ["checkout"] resources: cpu: "4" memory: "8Gi" timeout: "90m" env: GOPATH: "/workspace/.ankra-go" GOCACHE: "/workspace/.ankra-go/cache" GOFLAGS: "-buildvcs=false" BAZELISK_HOME: "/workspace/.ankra-bazel/bazelisk" cache: - key: "bazel-${{ hashFiles('MODULE.bazel.lock', '.bazelversion') }}" paths: - ".ankra-bazel/disk-cache" - ".ankra-bazel/repository-cache" - ".ankra-bazel/bazelisk" - ".ankra-go/bin" restore_keys: ["bazel-"] test_results: - format: "junit" path: "test-results" run: | go install github.com/bazelbuild/bazelisk@v1.25.0 bazel() { /workspace/.ankra-go/bin/bazelisk --output_user_root=/workspace/.ankra-bazel/root "$@"; } status=0 bazel test --config=ci //... || status=$? mkdir -p /workspace/test-results cd "$(bazel info --config=ci bazel-testlogs)" find . -name test.xml | while read -r report; do cp "$report" "/workspace/test-results/$(echo "${report#./}" | tr / _)" done exit "$status" ``` What each part does: * **`golang:1.26`** is the image because it has the C toolchain Bazel's default rules expect and lets `go install` fetch Bazelisk. Any image your organisation's image policy allows works the same way, as long as it has a shell and a compiler. * **The cache** keeps Bazel's disk cache, repository cache and downloaded Bazel binary between runs, keyed on the module lock file and the Bazel version. A repository still on `WORKSPACE` keys on the files that pin its external dependencies instead. Bazel's own cache decides what to rebuild, so a run where little changed finishes in a fraction of the cold time. * **`test_results`** reads the JUnit `test.xml` Bazel writes for every test target. The script copies them out of `bazel-testlogs` into one directory before the step ends, flattening each path into a file name so two targets never overwrite each other's report. * **`status`** holds Bazel's exit code while the reports are copied, so a failing test still reports its results and still fails the step. > [!NOTE] > > `cache`, `resources` and `timeout` are protected settings. They take effect once an administrator approves the pipeline on the default branch, so the first run before that approval is cold and uses the default size. A large repository can run out of memory or time on that first run - approve the definition and run it again. ## Use a remote cache Bazel's remote cache shares results between runs, branches and developers' machines. Ankra does not host one, but a step can reach any cache that speaks Bazel's remote cache protocol over HTTPS on port 443. Store the cache's API key as an organisation variable, which the CLI reads from standard input: ```bash theme={null} ankra org variables set BAZEL_REMOTE_CACHE_KEY - ``` Declare it in the pipeline and name it on the stage. The value arrives as a file under `/run/agent-secrets`: ```yaml theme={null} secrets: - name: "bazel_remote_cache_key" from: "org_variable" key: "BAZEL_REMOTE_CACHE_KEY" stages: - name: "test" kind: "run" secrets: ["bazel_remote_cache_key"] run: | bazel() { /workspace/.ankra-go/bin/bazelisk --output_user_root=/workspace/.ankra-bazel/root "$@"; } bazel test --config=ci \ --remote_cache=grpcs:// \ --remote_header="=$(cat /run/agent-secrets/bazel_remote_cache_key)" \ //... ``` The header name depends on the cache you use. `secrets` is a protected section too, so the binding reaches the step once the definition that declares it is approved on the default branch. ## Build an image from a Bazel target A `kind: "build"` stage cannot use a binary that an earlier `run` stage built. It does not read the workspace the other stages share: it runs in its own namespace and fetches the commit itself, so it sees only what is in the repository at that commit. See [where a build's paths are resolved from](/guides/pipeline-reference#where-a-builds-paths-are-resolved-from). To ship a Bazel-built binary as an image, build the binary inside the image build: a multi-stage Dockerfile whose first stage installs Bazelisk and runs `bazel build`, and whose final stage copies the binary out of the first one. The build stage then needs nothing from earlier stages. A `build` stage has `egress-https` by default, so Bazel can fetch its dependencies there, but it does not see the caches under `/workspace`. To ship the binary to machines outside Kubernetes instead, a `build` stage with `build.bundle` packs a directory an earlier stage left in the workspace into a release bundle rather than building an image. The `deploy` row of [kind-specific blocks](/guides/pipeline-reference#kind-specific-blocks) describes it. ## Make it faster * **Let Bazel skip unchanged work.** With the cache warm, `bazel test //...` reruns only the tests whose inputs changed. That is usually faster than narrowing the target list by hand. * **Skip runs that touch nothing Bazel builds.** `affected:` reads Go and pnpm import graphs, not Bazel's, so use path filters instead: `on.pull_request.paths: ["!docs/**", "!*.md"]` records a docs-only change as skipped. See [Fast CI](/pipelines/fast-ci). * **Split slow suites onto a schedule.** Tag long tests `slow`, leave them out with `--test_tag_filters=-slow` on pull requests and pushes, and run them from an `on.schedule` stage at night. ## Troubleshooting | Symptom | Cause | Fix | | - | - | - | | `No space left on device` early in the build | Bazel's output root is still under `HOME` (`/tmp`) | Pass `--output_user_root=/workspace/.ankra-bazel/root` before the command, on every Bazel call | | The step fails with the diagnosis `out_of_memory` | Bazel and its workers outgrew the step's memory | Raise `resources.memory`, or run fewer actions at once with `build:ci --jobs=4` | | `fetching ... failed` or a download times out | The stage has no network, or the host is private | Set `defaults.network: "egress-https"`; a private mirror needs an administrator to allow its address range | | Every run is cold | The pipeline definition is not approved yet, or the cache key changes on every run | Approve the definition on the default branch; check the step's **CACHE** column in `ankra pipeline get` | | A sandbox error such as `linux-sandbox failed` | The step's container does not allow the namespaces Bazel's Linux sandbox uses | Add `build:ci --spawn_strategy=processwrapper-sandbox,local` to `.bazelrc` | | Tests pass but the run shows no test results | The reports were not copied before the step ended | Keep the copy loop after `bazel test`, and point `test_results.path` at the directory it writes | [Caches](/pipelines/caching) explains keys, trust scopes and retention, and the [pipeline reference](/guides/pipeline-reference) lists every field used here. # Work the AI Board with Your Own Agent Source: https://docs.ankra.ai/guides/board-own-agent Connect Claude Code, Cursor, Codex or your own agent to Ankra over MCP and have it pick up board tickets, investigate, draft a plan for human approval, carry it out, and close the ticket with evidence. The [AI board](/platform/ai-board) is not only for Ankra's agents. Any MCP client you connect - Claude Code, Cursor, Codex, or an agent you wrote - can read the board, file tickets, investigate, draft a plan, and carry it out once a person approves it. The ticket keeps one timeline whoever does the work, so your team sees the same history, Slack or Teams thread and [Jira or Linear issue](/guides/ticket-sync) either way. There are four ways to combine Ankra's AI with your own. This guide covers the first. | You want | Where it is covered | | - | - | | Your own agent works board tickets | This guide | | Ankra's agents run on your own model (Anthropic, OpenRouter, an OpenAI-compatible endpoint) | [Models and Cost](/platform/ai-provider) | | Ankra's agents call your own tools | [AI Connections - MCP Tool Servers](/platform/ai-connections#mcp-tool-servers) | | Board tickets show up in your own tracker or automation | [Sync board tickets to Jira, Linear, or a webhook](/guides/ticket-sync) | ## How your agent fits the board Your agent acts **as you**. Every comment, status change and plan it writes is recorded on the timeline under the user who owns the token or the OAuth connection, stamped as written by a model. The same gates apply to it as to Ankra's own agents, and one applies more strictly: * **It can** list and read tickets, file new ones, comment, move a ticket through its statuses, draft and revise the plan, ask for review, request changes on a plan, answer a blocked ticket's question, and close a ticket with a resolution. * **It cannot approve a plan.** Plan approval is a human action in the product or a Slack or Teams thread, and an MCP token cannot reach it by any route. Until a person approves, the ticket cannot move to **Executing**. * **It cannot be the assignee.** `ticket_assign` hands a ticket to one of Ankra's agents, which then starts working it. Your agent owns a ticket by working it, and you show the board it is yours by assigning the ticket to yourself. * **Its comments do not wake Ankra's agents.** Only a person's comment re-admits a parked ticket to Ankra's dispatcher, so a model cannot keep an Ankra agent spinning. ```mermaid theme={null} flowchart LR T[Triage] --> I[Investigating] I --> P[Planning] P --> R[Awaiting review] P --> A[Awaiting approval] R --> A A -->|a person approves| E[Executing] E --> V[Verifying] V --> D[Done] ``` **Awaiting review** applies only when a reviewer agent is configured in the ticket desk settings; otherwise a plan goes straight to **Awaiting approval**. ## Prerequisites * The AI board is available in your organisation (**AI** → **Board** in the sidebar). The ticket tools do not appear over MCP while it is not. * Your role includes the `ai.execute` permission - the built-in **owner**, **admin**, **operator** and **member** roles do, **viewer** does not. See [Roles and Access](/guides/roles-and-access). * An MCP client connected to Ankra with **write** access. Reading the board needs `mcp:read`; every ticket change needs `mcp:write`. See [MCP Server](/platform/mcp-server). * Someone who can approve the plans your agent drafts. ## Step 1: Connect your agent Follow [MCP Server](/platform/mcp-server) for your client. Claude connects over OAuth and asks you to approve `mcp:read` or `mcp:write` access; Cursor and other clients take a token: ```bash theme={null} ankra tokens create my-board-agent --scopes mcp:read,mcp:write ``` To confirm the board is reachable, ask the agent to list the open tickets. It should call `ticket_list` and return them with their numbers, such as `T-412`. > [!TIP] > > When the agent runs unattended, create the token for a dedicated member account rather than your own. Its comments and status changes are then attributed to that account rather than to a person. ## Step 2: Pick up a ticket Find work with `ticket_list`, which filters by `status`, `kind`, `priority`, `cluster_id`, `resolution` and free-text `search`. Then read one in full with `ticket_get`, which takes a `ticket_number` (`412` for `T-412`) or a `ticket_id` and returns its body, plan, review state, links and newest timeline events. To file a ticket instead, call `file_ticket` with a `title` and a `body`, and optionally: | Parameter | Values | Default | | - | - | - | | `kind` | `incident`, `insight`, `gap`, `automation`, `request`, `chore` | `request` | | `priority` | `urgent`, `high`, `medium`, `low` | `medium` | | `cluster_id` | The cluster the work targets | none | Filing the same `condition` again is folded into the open ticket rather than creating a second one. **Move the ticket out of Triage as soon as your agent takes it.** Ankra's dispatcher assigns unowned **Triage** tickets to its own board workers, so a ticket left there can be claimed by an Ankra agent while yours is reading it. Ask your agent to call `ticket_update_status` with `status: investigating`; the dispatcher does not claim tickets outside **Triage**. Then assign the ticket to yourself from its assign menu (`a` on the board), so the board shows a person owns it rather than escalating it as unstaffed. Example prompt: ```text theme={null} Take T-412 off the Ankra board: move it to investigating and comment that I'm working it from Claude Code. ``` ## Step 3: Investigate and record findings Your agent investigates with Ankra's read tools - `get_pods`, `get_pod_logs`, `get_events`, `query_prometheus`, `get_stack_details` and the rest in the [MCP Tool Reference](/platform/mcp-tools) - and with whatever else it can reach, such as your repository checkout. Have it post what it finds with `ticket_comment`. The timeline is what the approver reads, and it is relayed as comments to any [sync connection](/guides/ticket-sync), so findings written there reach Jira, Linear and the ticket's Slack or Teams thread too. If the ticket names no cluster, or the wrong one, `ticket_set_cluster` binds it. ## Step 4: Draft the plan and send it for approval **1. Move to Planning** `ticket_update_status` with `status: planning`. **2. Write the plan** `ticket_draft_plan` stores the plan as markdown on the ticket. Write it for the person who approves it: what will change, on which cluster and stack, how to verify it, and how to roll it back. Calling it again revises the plan. **3. Ask for review** `ticket_request_review` moves the ticket to **Awaiting review** when your organisation has a reviewer agent, and straight to **Awaiting approval** when it does not. A reviewer that requests changes sends the ticket back to **Planning** with its rationale on the timeline. At **Awaiting approval** Ankra raises **Plan awaits your approval** for the people who decide, and the ticket appears in the **Needs a human** view. ## Step 5: A person approves Approve the plan on the ticket's page - the decision panel at the top offers **Approve** and **Reject** - or with **Approve plan** on the escalation card in the ticket's Slack or Teams thread. Approving moves the ticket to **Executing**. Rejecting asks for a reason and sends it back to **Planning**, where your agent reads the reason and revises the plan. Your agent can wait by polling `ticket_get` until `plan_status` reads `human_approved` and `status` reads `executing`. A rejection shows as `status: planning` with your reason as the newest timeline event. > [!NOTE] > > Approval is a gate on the ticket, not a grant of access. Your agent carries out the plan with its own token, so what it may change is whatever that token and your role allow - an `mcp:write` client runs a write as soon as it calls the tool, with no second confirmation from Ankra. Grant write access only to an agent you trust to stay inside the approved plan. ## Step 6: Carry out the plan, verify, and close **1. Execute** The agent carries out the approved plan with Ankra's write tools. Writes that start platform work answer at once with an `operation_id`; `wait_for_execution` blocks until it settles. Have it comment each step it runs, so the timeline shows what changed: the gated writes it makes over MCP are recorded on a separate `chat-fix` receipt that stays off the board, not under this ticket's **Changes**. **2. Verify** `ticket_update_status` with `status: verifying`, then check the fix held against live state - the pod is ready, the alert stopped firing, the error rate dropped. **3. Close** `ticket_update_status` with `status: done` and a `resolution`: `fixed`, `no_action`, `duplicate`, `wont_fix` or `escalated`. `fixed` needs evidence: an `evidence_kind` and an `evidence_reference` together. From your own agent that is normally `observation` (the tool it called and the values it read, quoted so a reader can re-check them) or `link` (a URL to a dashboard, a CI run or a pull request). Example prompt: ```text theme={null} The fix for T-412 is applied. Verify the payments-api pods are ready and have not restarted in 10 minutes, then close T-412 as fixed with what you observed as evidence. ``` ## When a ticket is blocked on a choice When an Ankra agent parks a ticket at **Blocked** with a question and options, `ticket_get` shows it under `decision_request`, and `ticket_decide` records the answer - an `option_key`, an `answer` in your own words, or both - and resumes the agent. > [!WARNING] > > Ankra records a decision made through `ticket_decide` as relayed from the token's user, and does not check that a person chose it. Have your agent relay only decisions you made, never pick an option on its own. ## Start your agent from new tickets To have new tickets reach your agent without anyone asking, add a **webhook** [sync connection](/guides/ticket-sync) that points at a receiver you run. Each matching ticket arrives first as a signed `ticket.created` request carrying the full ticket - its number, kind, priority, cluster and body. Your receiver verifies the signature and starts your agent on that ticket number; from there the agent works it through the steps above. Narrow the connection's **Kinds** and **Minimum priority** filters to the tickets you want your agent on. A new connection picks up the tickets that are live when you add it and everything that changes afterwards. It never writes back to the board: everything your agent does goes through MCP. ## Limits * Each token may make 120 read calls and 20 write calls per minute. A rate-limited call says so; wait a minute and retry. * Every tool call has a 55-second deadline. A write that timed out may still have run, so read the ticket before repeating it. * A human gate that times out closes the ticket as `expired`. Nothing is executed and nothing is recorded as fixed. ## Troubleshooting | What your agent sees | What it means | | - | - | | `Plan approval is a human action: ask the user to approve the plan on the AI board ...` | An MCP client tried to approve its own plan. A person approves it on the ticket page or in its thread. | | `A human must approve the ticket's plan on the AI board before execution begins ...` | The agent tried to move a ticket to **Executing** before approval. Wait for the approval to move it there. | | `The ticket has no plan to approve. Draft one with ticket_draft_plan ...` | **Awaiting approval** needs a plan. Draft one first. | | `This desk requires a peer review before the human gate ...` | Your organisation requires the reviewer agent's verdict. Call `ticket_request_review` and wait for it. | | `Cannot move a ticket from 'triage' to 'verifying'.` | Statuses move one hop at a time along the board's lifecycle. Go through the statuses in between. | | `This ticket is not waiting on a decision.` | `ticket_decide` only answers a ticket at **Blocked** with a pending question. | | `No ticket with that ticket_number exists in this organisation.` | The number is wrong, or the client is connected to another organisation. MCP access is bound to the organisation chosen when the token or connection was created. | | No ticket tools in the client's tool list | The AI board is not available in the organisation, or the token has no MCP scope. Ticket changes also need `mcp:write`. | | A tool reports that it needs `mcp:write` | The client holds read-only access. Reconnect with write access. | | An Ankra agent started working the same ticket | The ticket was still in **Triage** when the dispatcher swept it, or someone assigned it to that agent. Move tickets out of **Triage** as soon as your agent takes them. | ## Related pages * [AI Board](/platform/ai-board) - statuses, plans, review and approval * [MCP Server](/platform/mcp-server) - connecting a client, scopes and how calls behave * [MCP Tool Reference](/platform/mcp-tools) - every ticket tool, under **Tickets & Collaboration**, and the scope it needs * [`ankra tickets`](/reference/cli/tickets) - work the board from a terminal * [Sync board tickets to Jira, Linear, or a webhook](/guides/ticket-sync) - mirror tickets out to your tracker # Branch Demos Source: https://docs.ankra.ai/guides/branch-demos Launch a throwaway demo of any branch build on your staging cluster - with environment variables, an optional Postgres, and an automatic teardown timer > [!NOTE] > > A branch demo runs a branch's already-built container images in their own isolated namespace on your organisation's staging cluster, and stops itself when the timer runs out. Use it to show work in progress, reproduce a bug against a specific build, or hand a reviewer a live URL instead of a screenshot. Pull requests get [previews automatically](/guides/pr-preview-environments). This guide covers the manual path: the **Launch a branch demo** dialog in an application's **Previews** section, and its CLI and MCP equivalents. *** ## Prerequisites **1. Connect the repository as an application** The branch must belong to a [connected application](/concepts/applications). Application setup adds the CI definition that builds its images - `.ankra/pipeline.yaml` for a new application, or the generated GitHub Actions workflow. **2. Set the organisation's staging cluster** An admin sets it under **AI** → **Settings** → **Workspaces**. Every demo lands there. Without it, there is nowhere to launch. **3. Build the branch at least once** A demo deploys an image, it does not build one, so the branch needs a tag in the application's registry. The generated pipeline and the generated workflow publish only the tracked branch: for any other branch, see [Which image a demo finds](#which-image-a-demo-finds). If nothing was ever published, the dialog offers [Fix the build](#no-built-image-for-this-branch). **4. Optional: a demo base domain for HTTPS** Without one, demos are served over the staging cluster's own Ankra zone (`*.ankra.cc` by default, or the [Ankra domain your organisation selected](/concepts/applications#the-preview-url)) on plain HTTP, or stay in-cluster-only. *** ## Launch a demo Open the application, open the **Previews** section, and choose **Launch preview**. ### Staging cluster capacity The panel at the top of the dialog reports the staging cluster's live headroom - nodes ready, CPU and memory in use, pod count, and how many demos are already running - and highlights a figure in amber once it passes 90% of capacity. It is informational: a busy cluster does not block the launch, but it tells you whether your pods will actually get scheduled. Each demo namespace carries its own hard `ResourceQuota`, so one demo can never exhaust the cluster: | Demo type | Pods | CPU limit | Memory limit | | - | - | - | - | | Single service | 5 | 2 | 2 Gi | | Full stack (stack profile attached) | 20 | 8 | 16 Gi | ### Branch Pick the branch to demo. The list marks the application's configured **build branch** and the repository **default** branch. As soon as you pick one, Ankra checks the registry for a matching image and reports the verdict inline: | Verdict | Meaning | | - | - | | ✅ **Build found** | The resolved image tag is shown and will be deployed. | | ⚠️ **No built image was found** | Nothing was published for this branch yet. See [below](#no-built-image-for-this-branch). | | ❌ An error message | The registry or the repository could not be read - the message names the cause. | ### Which image a demo finds Ankra looks in the application's registry repository for these tags, in this order, and deploys the first one it finds: 1. `sha-<7>` - the first seven characters of the branch's head commit, the only tag that is provably this head's build, 2. for each open pull request from the branch, `sha-<7>` of its merge commit, then `pr-`, 3. the branch name, with every character other than letters, digits, `.`, `_` and `-` replaced by `-` (`feature/login` becomes `feature-login`). A pull request tag is built from merging the branch into its base, not from the branch head, and the dialog says so when it picks one. What publishes these tags depends on what builds the application (see [Ship Your App](/guides/deliver-your-app#where-the-image-is-built)): | Builder | Tags it publishes | | - | - | | Ankra Pipelines, as generated | `sha-<7>` for commits on the tracked branch only: the `publish` stage runs on a push to that branch, not on a pull request | | Generated GitHub Actions workflow | `sha-<7>` and the branch name for commits on the tracked branch only. A pull request is built and scanned but never pushed | | Your own workflow | Whatever it pushes | | Ankra platform builders | `sha-<7>` of any commit you ask for, where Ankra has enabled platform builders for your organisation | So a demo of any branch other than the tracked one needs an image from somewhere else. Build the branch's head on Ankra platform builders: ```bash theme={null} ankra application build start --commit --ref feature/login --reason demo --wait ``` Or push a `sha-<7>` tag from your own CI, or pick an existing tag with the [image tag override](#advanced-options). ### Components (monorepos only) A single-app repository has nothing to choose. A monorepo shows every deployable component with its own build status and tag, and **all of them deploy by default** - each as its own pod in the one namespace, so a frontend and its API come up wired together instead of half an application answering where the other half belonged. * **Untick a component** to leave it out of this launch. At least one must stay ticked. * **The web entry** owns the demo's URL root. Ankra marks the component its heuristic picked; **set as entry** moves the badge to another one. * **Path badges** on the remaining components show where they are published under the same host, so in-cluster callers and the browser reach the same demo. The container port prefill follows the web entry, and the launch is blocked until every ticked component has an image (or you supply an [image tag override](#advanced-options), which applies to all of them). ### Stop automatically after (hours) The teardown timer. It is pre-filled with the organisation's default demo TTL and capped by your role's limit, which admins set per role (admin, operator, member) under **AI** → **Settings** → **Workspaces**. The field states your own ceiling. When the timer expires the namespace is deleted; nothing survives it. ### Environment and database Expand **Environment & database** to set variables for this launch only. **Add variable** adds a row - a name, a value, and a **Secret** checkbox. Values marked secret are stored as Ankra secret slots in the platform secret store and mounted as a Kubernetes Secret at deploy time; the plaintext never persists in the application's configuration. Toggle **Attach a throwaway Postgres** and an ephemeral pgvector-capable Postgres is provisioned inside the demo namespace with a per-demo random password, then destroyed with it. Nothing is injected automatically, because every codebase names its configuration differently - reference the placeholders in your own variable *values* instead: | Placeholder | Resolves to | | - | - | | `${{ ankra.demo_database.url }}` | Full `postgres://` connection string | | `${{ ankra.demo_database.host }}` | In-namespace service host | | `${{ ankra.demo_database.port }}` | `5432` | | `${{ ankra.demo_database.name }}` | Database name | | `${{ ankra.demo_database.user }}` | Database user | | `${{ ankra.demo_database.password }}` | The per-demo random password | With the toggle on, the editor offers quick-add buttons for the common names (`DATABASE_URL` and friends) already wired to the right placeholder. > [!NOTE] > > Everything in this section applies to **this launch only**. To give every demo of the application the same configuration - manual launches, CLI and MCP deploys, and the automatic PR previews - set it once as the application's demo defaults in the environment template under **Previews** (*Preview settings*). Per-launch values win by name. Full reference: [environment variables and a throwaway database](/concepts/applications#environment-variables-and-a-throwaway-database). ### Advanced options | Field | Use it when | | - | - | | **Image tag override** | You want a specific tag rather than the one resolved from the branch build - an older build, or a tag pushed outside the standard workflow. It applies to every included component, so it also rescues a partially built branch. Leave empty to use the resolved tag. | | **Container port** | Your application listens on something other than the detected port. It is pre-filled from the web entry's build metadata and stops following it once you edit the field. | ### Full-stack demos When the application designates a **stack profile** for demos, the dialog says so before you launch. That launch also instantiates the profile as a demo-scoped Stack in the same namespace - dependencies, add-ons, and manifests included - and tears the whole thing down with the demo. The per-demo quota rises to the full-stack row in the table above. Configure the designation in the environment template under **Previews**. The **Previews** section also names that profile and lists the profile's own live demos, each one linking straight to its details. A profile can be demoed on its own - the same stack, without an application in front of it - which is usually a cheaper way to look at it than launching a whole application demo. See [Stack Profile Demos](/guides/stack-profile-demos). *** ## Reach the running demo Launching drops you back in the **Previews** section, which shows one card per active preview with its status, preview URL, and remaining TTL. Each demo has a detail page carrying live provisioning progress read from the staging cluster, the namespace's bill of materials, Kubernetes events, and pod logs. How you reach it depends on DNS: 1. **An organisation demo base domain**, if configured, wins outright: `.`, with your ingress class and TLS secret. 2. **Otherwise the staging cluster's delegated Ankra DNS zone**, when active: `...` over HTTP (`ankra.cc` by default, or the Ankra domain your organisation selected). 3. **Otherwise in-cluster only** - the demo still runs, and the detail page gives you the service DNS name and a ready-made `kubectl port-forward` command. The full resolution order is documented under [the preview URL](/concepts/applications#the-preview-url). *** ## From the CLI and the AI Assistant > [tab] CLI > > ```bash theme={null} > # Deploy a branch demo > ankra application demo deploy --branch feature/login > > # Override the teardown timer > ankra application demo deploy --branch feature/login --ttl-hours 8 > > # List active demos, then stop one early > ankra application demo list > ankra application demo stop > ``` > > The deploy response includes `preview_url` when a public host was resolved. > [tab] AI / MCP > > Ask the AI Assistant to demo a branch, or call the `deploy_pr_demo` tool directly - it is allowed in **Ask** mode, because a demo is isolated and self-expiring. Pass `env` to set per-launch variables. Tear down early with `demo_stop`, and inspect a failed one with `get_demo_diagnostics`. *** ## No built image for this branch When the registry holds nothing for the branch, the dialog does not dead-end. It offers, in order of effort: * **Recent image tags** from the same repository as one-click chips - handy for demoing the last good build while a new one is still running. * **Fix the build**, which diagnoses why no image exists and applies the safe fixes it can: restarting a failed application setup, repairing publish readiness, or re-running the latest workflow so it can push to the registry. When the deterministic fixes cannot unblock it, Ankra dispatches an agent mission and links you to its run so you can watch the work. * **Open CI/CD**, which jumps straight to the application's pipeline runs. *** ## Stopping a demo A demo stops itself when its TTL expires - namespace deleted, quota released, database gone. Stop one early with **Stop preview** in the **Previews** section, with `ankra application demo stop`, or with the `demo_stop` tool. There is nothing to clean up afterwards. > [!WARNING] > > Demo namespaces are ephemeral by design. The throwaway database starts empty on every deploy and is wiped with the namespace - run your migrations on boot and never point a demo at data you care about. *** ## Troubleshooting | Symptom | What to check | | - | - | | **Launch demo** is disabled | The dialog names the reason on hover: no branch picked, an unresolved image tag, a TTL beyond your role's cap, an invalid container port, or an incomplete environment variable. | | **No built image was found** for a branch other than the tracked one | Expected with the generated pipeline or workflow, which publish only the tracked branch. See [Which image a demo finds](#which-image-a-demo-finds). | | No branches are listed | The application's Git credential can no longer read the repository. Re-check it on the application's settings, then reopen the dialog. | | Deployed, but the pod crash-loops | The demo runs *your* image, so a boot crash is application-side. Read the pod logs on the demo's detail page. If the container refuses to start without configuration, Ankra dispatches a [pre-setup agent](/concepts/applications#automatic-pre-setup-when-a-demo-crashes) that works out the missing variables, saves them as the application's demo defaults, and redeploys. | | Pods stay `Pending` | The namespace quota or the cluster is full. Check the capacity panel and the demo's Kubernetes events, and stop demos you no longer need. | | The preview URL does not resolve | The staging cluster needs an active Ankra DNS zone, or the organisation needs a demo base domain. Without either, use the port-forward command on the detail page. | | An environment variable did not reach the container | Per-launch values win by name over the application defaults, and an empty value is still a value. Confirm the final set on the demo's detail page bill of materials. | *** ## Related * [Preview demos](/concepts/applications#preview-demos) - the concept, the namespace model, and the isolation guarantees * [PR preview environments](/guides/pr-preview-environments) - the automatic per-pull-request flow and its status comment * [Environment variables and a throwaway database](/concepts/applications#environment-variables-and-a-throwaway-database) - the defaults layer shared by every demo surface * [Ship Your App](/guides/deliver-your-app) - the routes from a repository to a running application, and which builder publishes which tags # Application CI/CD Source: https://docs.ankra.ai/guides/cicd-pipeline Two ways to build and publish an application's image - Ankra Pipelines in your own cluster, or the generated GitHub Actions workflow that publishes to your private Ankra registry. Ankra Applications get their container images built, scanned and published in one of two ways. [Ankra Pipelines](/guides/ankra-pipelines) runs the pipeline as hardened Jobs inside a cluster you already operate through Ankra, from one committed file, `.ankra/pipeline.yaml`; it is the primary path, a new application's setup pull request commits `.ankra/pipeline.yaml` by default, and the guide says exactly which pieces are in place today. The generated GitHub Actions workflow below is for a repository that must keep building on GitHub, and for applications set up before the pipeline became the default. To pick between these and your own CI, see [Ship Your App](/guides/deliver-your-app). > [card] [Ankra Pipelines](/guides/ankra-pipelines) > > CI in your cluster: triggers, a Job per stage, check runs, artifacts, and what a pull request can and cannot change. > [card] [Migrate from GitHub Actions](/guides/migrate-from-github-actions) > > Convert a workflow, GitLab CI file, Bitbucket pipeline or CircleCI config and review the notes. ## Bring your own CI (GitHub Actions) Ankra Applications generates a GitHub Actions pipeline that builds your source repository, scans the image, and publishes it to your organisation's private registry. The generated Kubernetes manifests already reference that image and the matching pull secret. Nothing about it changes when you add an Ankra pipeline to the same repository: the workflow's checks keep their names, the pipeline's is `Ankra pipeline`, and Ankra keeps reading the workflow's results for publish readiness, push-to-deploy and previews. > [!NOTE] > > The Ankra registry is private. Ankra provisions its project, GitHub Actions credentials, and Kubernetes pull credentials automatically. ### How it works ```mermaid theme={null} flowchart LR Repo[Application repository] -->|connect| Ankra Ankra -->|open setup PR| PR[Setup pull request] PR -->|merge| Actions[GitHub Actions] Actions -->|build and scan| Image[Container image] Image -->|push| Registry[Private Ankra registry] Registry -->|pull| Cluster[Kubernetes cluster] ``` **1. Connect the repository** Create an Application from a GitHub repository. Ankra analyses the source and prepares the missing Dockerfile, Kubernetes manifests, and workflow. **2. Review the setup pull request** The pull request adds `.github/workflows/build-and-publish.yml` and manifests under `.ankra/manifests/`. Monorepos get one scoped workflow per application. **3. Merge and build** Merging the pull request runs the workflow. It tests the source, builds the image, scans it with Trivy, and only then pushes it to your organisation's project on `registry.ankra.cloud`. **4. Pull requests build but do not publish** The workflow also runs on `pull_request`, where it tests, scans the source, builds the image and scans it with Trivy, but skips the push - so an unmerged branch never puts artifacts in your registry. Because nothing is pushed, a pull request gets no image a [PR preview](/guides/pr-preview-environments#applications-built-by-github-actions) could deploy. **5. Deploy** Deploy the Application to a cluster. Ankra checks that an image was published before starting the deployment. ## Registry authentication Ankra installs these encrypted GitHub Actions secrets on the application repository: | Secret | Purpose | | - | - | | `ANKRA_REGISTRY_USERNAME` | Username of the application's own push robot on the organisation registry project (`robot$+app-`) | | `ANKRA_REGISTRY_PASSWORD` | That robot's secret | The robot is minted per application when the application is set up, so no two applications share a registry login. Rotate or revoke it from the application's **Settings** → **Image registry**, or with `ankra application registry robot rotate|revoke` - see [Robot accounts](/concepts/applications#robot-accounts). The generated workflow uses the secrets with `docker/login-action`. It does not use `GITHUB_TOKEN` package permissions and does not publish a public package. For Kubernetes pulls, Ankra creates an `ankra-registry-pull` Docker registry secret in each managed namespace. Generated Deployments include: ```yaml theme={null} spec: template: spec: imagePullSecrets: - name: ankra-registry-pull ``` The same pull credential is injected into short-lived Application preview environments. ### Your own registry An Application that [declares its own registry](/concepts/applications#using-your-own-registry) publishes there instead. The workflow logs in with the secret names the declaration gives (defaulting to the `ANKRA_REGISTRY_*` pair), the generated Deployments reference the pull Secret it names, and Ankra creates that Secret in the deploy namespace from the registry credential the declaration names. Ankra does not write the login secrets for a registry you operate unless you hand it robot management there: the robots are yours to administer, and overwriting a push robot with the credential Ankra holds would deny every later build. Publish readiness reports whether the named secrets exist. Name an `admin_credential_name` on the declaration to have Ankra mint a push robot for the application on your registry and write that into the secrets, or opt in with `manage_actions_secrets: true` to have it write the declared credential. ## Image retention The Ankra registry does not keep every image forever. Each organisation project carries a retention policy that Harbor runs once a day: an artifact is kept while it was pushed within the last **30 days**, and the **three most recently pushed tagged versions** of every repository are kept whatever their age - so a component nobody has built in months still holds its last three releases. Everything else goes: older tagged images beyond the last three, and the untagged leftovers of overwritten tags once they are 30 days old. Harbor's garbage collection reclaims the storage afterwards. Two consequences worth planning for: * A rollback can only reach a version that is still in the registry - one of the last three, or one pushed in the last 30 days. * A workload still running an image older than that keeps running, because the image is cached on its node, but it cannot be rescheduled onto a fresh node once the image is gone. Redeploy long-lived workloads from a recent build. A registry you operate yourself is never touched: retention there is yours to configure. ## Existing applications Reconcile an Application after the registry cutover. Ankra updates its image URL, replaces a legacy build workflow or manifest with the private registry version, and restores missing repository secrets. You can inspect readiness in the application's **Pipelines** section or from the CLI: ```bash theme={null} ankra application publish-readiness ``` Readiness checks the organisation registry project, both repository secrets, the workflow login target, and whether an image has been published. ## Converting an existing workflow Ankra converts a GitHub Actions workflow, a GitLab CI file, a Bitbucket Pipelines file or a CircleCI config into `.ankra/pipeline.yaml`. Ask Ankra AI in the portal chat or through the MCP server: `cicd_convert_workflow_to_pipeline` answers with the pipeline, a note for everything it could not map one to one, and a validation verdict; `cicd_validate_pipeline_yaml` checks a definition you wrote or edited. Steps that only installed cloud credentials or a registry login are dropped with a note, because the pipeline gets those from Ankra, and a third-party step the mapping table does not know becomes a stage that fails until you replace it. For an application already connected to Ankra, `GET`/`POST .../pipeline` and `.../pipeline/migrate` do this in one call instead: read what builds it today, convert, and open the pull request - see [Migrate an existing application with one call](/guides/migrate-from-github-actions#migrate-an-existing-application-with-one-call). The disposition itself only switches once that pull request merges, and a later push that adds or removes `.ankra/pipeline.yaml` or a workflow file on the default branch re-evaluates it automatically on GitHub. The [migration guide](/guides/migrate-from-github-actions) carries the full mapping table, what each source format loses, what to review afterwards, and how to run both side by side until you trust the pipeline. The [pipeline.yaml reference](/guides/pipeline-reference) documents every key. ## Troubleshooting > [accordion] The workflow cannot log in > > Open the application's **Pipelines** section. If Ankra cannot restore the repository secrets, approve the updated Ankra GitHub App **Secrets** permission and retry the fix. > [accordion] The registry project is not ready > > Registry projects are provisioned automatically. The background reconciler retries missed provisioning. If readiness remains blocked after a few minutes, contact support. > [accordion] The image push is denied > > Confirm that the workflow logs in to the host shown in the Application image URL and uses `ANKRA_REGISTRY_USERNAME` and `ANKRA_REGISTRY_PASSWORD`. Reconcile the Application to replace a legacy workflow. > [accordion] Pods report ImagePullBackOff > > Reconcile the Application and check the target namespace for `ankra-registry-pull`. Ankra rotates and reapplies pull credentials automatically. > [accordion] Deploy is blocked > > Ankra blocks deployment until it can verify a published image in the organisation registry. Check the latest GitHub Actions run, then retry after the image push succeeds. ## Next steps > [card] [Ankra Pipelines](/guides/ankra-pipelines) > > Run the same lifecycle inside your own cluster. > [card] [Applications](/concepts/applications) > > Connect a repository and follow the Application lifecycle. > [card] [GitHub integration](/integrations/github) > > Review the GitHub App permissions used by Ankra. > [card] [Pipeline Agents](/guides/pipeline-agents) > > Add read-only deployment and health reporting to your CI workflows. > [card] [SOPS Encryption](/guides/sops) > > Encrypt other secrets stored in GitOps. # From Claude Design to a live application Source: https://docs.ankra.ai/guides/claude-design-to-ankra Export a canvas from Claude Design, import it into Ankra, and get a repository, a setup pull request, a build and a public URL without writing a Dockerfile. A design you made in [Claude Design](https://claude.ai/design) can be a running application on one of your clusters in a few minutes. Ankra takes the export, turns the artboards into a static site, creates a GitHub repository for it, commits the site with a Dockerfile, and registers it as an [application](/concepts/applications) - from there it is the same setup pull request, build and deploy flow every application goes through. > [!NOTE] > > Applications connect to **GitHub** through a [GitHub credential](/integrations/github). The import creates the repository under that credential's account (or an owner you name), so the credential needs the Ankra GitHub App installed there. ## What the import does ```mermaid theme={null} flowchart LR Export[Claude Design export] -->|import| Ankra[Ankra] Ankra -->|create + commit| Repo[GitHub repository] Repo -->|register| App[Application] App -->|"analyze + generate (PR)"| PR[Setup pull request] PR -->|merge| Build[Build] Build -->|deploy| Cluster[Live on a cluster] ``` Every artboard (`.dc.html`) becomes a page under `site/` in the repository: `Main.dc.html` is `index.html`, the rest take their name (`Pricing.dc.html` becomes `pricing.html`), and a canvas with several artboards gets a `canvas.html` front door listing them by their canvas titles and pages. Images travel along under `site/assets/`. The Dockerfile serves the site with busybox `httpd` on port 8080 as a non-root user, which is what the generated Kubernetes manifests expect. A `.ankra/design-import.json` file records what was imported, so running the same import again registers the same repository without a second commit. ## 1. Export the design In Claude Design, export the canvas. Any of these shapes work: * a folder holding the artboards (`Main.dc.html`, `Pricing.dc.html`, …), `canvas.json` and any images; * a zip of that folder; * a single artboard file; * a saved canvas page (`.html`) - the page Claude Design publishes, which carries the artboards inside it. Files over 2 MiB and exports over 16 MiB are refused; those are the editor's own limits, so an export that came out of it fits. ## 2. Import it > [tab] CLI > > ```bash theme={null} > ankra application import claude-design ./my-design-export --name my-site > ``` > > With one GitHub credential in the organisation the command picks it and creates the repository under that credential's account. Name them yourself when there is a choice: > > ```bash theme={null} > ankra application import claude-design my-design-export.zip \ > --credential my-github --owner my-org --repository my-site \ > --source-url https://claude.ai/design/p/ --wait > ``` > > `--wait` follows the analysis and prints the setup pull request when it opens. `-o json` returns the application id, the repository, the pages and any warnings as one document. > [tab] Portal > > Open **Applications → Add application** and choose **Claude Design** as the source. Drop the export files (or the zip, or the saved page) onto the form, name the application, pick the GitHub credential and the owner, and import. The application page opens with the repository link and the setup pull request once analysis has run. > [tab] API > > ```bash theme={null} > curl -X POST "$ANKRA_API/api/v1/org/applications/imports/claude-design" \ > -H "Authorization: Bearer $ANKRA_API_TOKEN" \ > -H "Content-Type: application/json" \ > -d '{ > "name": "my-site", > "app_repo_credential_name": "my-github", > "app_repo_owner": "my-org", > "files": [ > {"path": "Main.dc.html", "content_base64": "..."}, > {"path": "canvas.json", "content_base64": "..."} > ] > }' > ``` > > The answer has the create route's `id` and `errors`, plus `repository`, `pages` and `warnings`. A refusal you can act on - a repository name that is taken, a file the importer does not recognise - comes back in `errors` with a 200; see the [API reference](/api-reference/applications/import-a-claude-design-application). ## 3. Merge, build, deploy From here the application is an ordinary one. Ankra opens the setup pull request with the manifests, chart and build workflow (it keeps the committed Dockerfile), the merge builds and publishes the image, and you deploy it to a cluster: ```bash theme={null} ankra application get ankra application deploy --cluster ``` The [applications concept](/concepts/applications) walks through the setup pull request, secrets and deployment in full. ## Artboards that use template logic An artboard is served as it was authored. Artboards that only use markup and styles look exactly as they did on the canvas. Artboards that depend on the Claude Design runtime - `{{ }}` holes, `sc-for` or `sc-if` blocks, `dc-import` components, or a `data-dc-script` logic block - are imported too, but the import reports each of them as a warning: without the runtime they may not render as they did in the editor. Replace the logic with plain markup in Claude Design and import again, or edit the page under `site/` in the repository; every push builds and deploys. ## Importing again The repository keeps `.ankra/design-import.json`, a record of the files the import was built from. Running the import again with the same export finds that record and registers without committing; running it with a changed export is refused for now - import into a new repository name, or edit the pages in the repository directly. ## Related * [Applications](/concepts/applications) * [Ankra Pipelines](/guides/ankra-pipelines) * [Branch demos](/guides/branch-demos) # Clone a Cluster Source: https://docs.ankra.ai/guides/clone-cluster Duplicate a cluster with the CLI or the portal, onto an import cluster or a different cloud provider. > [!NOTE] > > Clone stack configurations from existing clusters or remote repositories. Perfect for replicating production setups to staging or dev environments. *** ## Prerequisites * [Ankra CLI](/integrations/ankra-cli) installed and configured * A cluster configuration file (YAML) *** ## Clone from a Local File Copy stacks from an existing cluster configuration: ```bash theme={null} ankra cluster clone existing-cluster.yaml new-cluster.yaml ``` *** ## Clone from GitHub Pull configurations directly from a repository: ```bash theme={null} ankra cluster clone https://github.com/user/repo/raw/main/cluster.yaml my-cluster.yaml ``` *** ## Clone Options | Flag | Description | | - | - | | `--clean` | Replace all stacks in target (clean copy) | | `--force` | Override conflicts during merge | | `--copy-missing` | Copy missing files even from skipped stacks | | `--stack` | Clone only specific stacks by name (repeatable) | **Examples:** ```bash theme={null} # Clean copy - replace all stacks in target ankra cluster clone source.yaml target.yaml --clean # Force merge - override conflicts ankra cluster clone source.yaml target.yaml --force # Clone only specific stacks ankra cluster clone source.yaml target.yaml --stack "monitoring" --stack "networking" # Complete replacement with all files ankra cluster clone source.yaml target.yaml --clean --force --copy-missing ``` *** ## Example Workflow **1. Clone from GitHub** ```bash theme={null} ankra cluster clone https://github.com/ankraio/ankra-gitops-examples/raw/main/clusters/monitoring-stack/cluster.yaml ./my-cluster.yaml ``` **2. Apply the Configuration** ```bash theme={null} ankra cluster apply -f my-cluster.yaml ``` **3. Customize Values** Edit the YAML file or use the UI to adjust values for your environment (resource limits, domains, replicas). *** ## Clone via UI You can clone stacks directly in the Ankra UI: **Clone a single stack:** 1. Navigate to **Stacks** in your cluster 2. Click the menu on any stack card 3. Select **Clone to Cluster** 4. Choose a target cluster and click **Clone Stack** **Clone multiple stacks at once:** 1. Navigate to **Stacks** in your cluster 2. Click the **Clone Stacks** button next to the search bar (or click a stack card's avatar to start selecting) 3. Select the stacks you want to clone - a floating action bar shows the count 4. Click **Clone to Cluster**, choose a target cluster, and confirm 5. All selected stacks are cloned as drafts to the target cluster > [!TIP] > > For a full guide on multi-stack cloning, see [Clone Stacks Between Clusters](/guides/clone-stack#clone-multiple-stacks-at-once). ### Clone a whole cluster **Clone Cluster** in a cluster's menu copies every stack of that cluster onto a new one and, with **Deploy after cloning** left on, deploys them there straight away. The dialog asks where the clone should run: * **Import an existing cluster** - Ankra creates an import cluster and prints the command that connects it. Pick this when the clone should land on Kubernetes you already run. * **Build on a cloud provider** - Ankra provisions a new cluster on Hetzner, OVHcloud, UpCloud, DigitalOcean, Scaleway, Proxmox VE or HPE Morpheus and copies the stacks onto it. Choose the provider and click **Continue**; the provider's create page opens with a *Cloning from* notice, where you pick the credential, region, node sizes and the usual create options. **Create Cluster** there builds the cluster and clones the stacks in one request. This is the path for moving a workload between clouds. A cloud-provider clone skips the stacks the provider's own provisioning installs - the cloud controller (`hcloud`, `openstack`, `upcloud-cloud-provider`, `digitalocean-cloud-provider`, `scaleway-cloud-provider`), `networking`, `external-dns` and, on kubeadm targets, `cilium`. The new cluster gets the target provider's versions of those; the result lists them under `stacks_skipped` with a warning naming each one. Stacks whose values pin the old provider (a storage class, a load-balancer annotation, a node selector) still clone, so review each draft before deploying. > [!TIP] > > The same request is available to automation: `POST /org/clusters/imported/{cluster_id}/clone` with `target_provider` and a `target` object carrying that provider's create body (the fields of `POST /org/clusters/{provider}`), and `deploy_after_clone: true` to deploy the clones. The response carries `target_provider`, `stacks_skipped`, `stacks_deployed` and `stacks_held` alongside the cloned and failed stacks; each cloned stack reports its `needs_input`. ### Stacks that need your input A clone never carries secrets: every value a member's `encrypted_paths` seal is replaced by the `ENCRYPTED_VALUE_REMOVED` placeholder on the target. A stack that still holds that placeholder cannot come up healthy, so the clone does not deploy it. It is kept as a draft and marked **Needs input** in the stack list, with the members and values to re-enter listed on the chip and in the clone result. Open the draft: the builder shows the same list as a banner, each entry opens the add-on or manifest that holds the field, and once every placeholder is replaced the banner disappears and **Create Stack** deploys it. Everything else deploys on its own; addons that had no stored configuration deploy with chart defaults and are named in the warnings. ### What a cluster clone carries Beyond the stacks, a cluster clone also copies the source cluster's **cluster variables**, so stack content keeps resolving its `${{ ankra.* }}` tokens. Everything else that is cluster-scoped cannot be cloned and is named in the result's warnings instead: a deployed **SOPS key**, **registry pull secrets**, the **Git repository connection**, and **Prometheus or log source** connections. Reconnect those on the new cluster before deploying stacks that need them. *** ## Next Steps > [card] [Ankra CLI Reference](/integrations/ankra-cli) > > Full CLI command reference. > [card] [GitOps Examples](https://github.com/ankraio/ankra-gitops-examples) > > Example cluster configurations to clone. # Clone Stacks Between Clusters & Organisations Source: https://docs.ankra.ai/guides/clone-stack Learn how to clone one or multiple stacks from one cluster to another - including across organisations - for consistent deployments across environments > [!NOTE] > > Cloning stacks allows you to replicate your infrastructure configurations across multiple clusters - even across different organisations. You can clone a single stack or select multiple stacks at once. With **Deploy on the target after cloning** left on, a cloned stack deploys on the target straight away; turn it off to keep the clone as a draft for review first. A stack whose encrypted values had to be removed is always held as a draft, marked **Needs input**, until you re-enter them.