mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-07 17:34:23 +00:00
a1804c8fbe
* docs: comprehensive review and refresh of all documentation pages Reviewed every doc page against the current app state after the v0.38 dashboard redesign. Updated content, fixed inaccuracies, and refreshed all screenshots at 1920x1080. Pages updated: - introduction: expanded feature list to 25 items across 6 subsections - quickstart: fixed docker run command (Docker Hub, auto JWT, COMPOSE_DIR) - configuration: replaced personal paths with generic /home/user/docker - sso-quickstart: fixed Settings navigation reference - sso: added SSO_LDAP_DISPLAY_NAME env var - overview: added 8 missing feature sections (labels, API tokens, schedules, etc.) - dashboard: complete rewrite for new health bar, gauges, stack health table - stack-management: updated for UP/DN indicators, rollback button, split actions - editor: rewritten for two-column layout, inline stats, embedded terminal - resources: updated Quick Clean docs, added network topology and inspect - app-store: updated categories, deploy sheet details, permission gate, settings - openapi.yaml: fixed YAML parsing error on line 1831 Screenshots refreshed: 14 images across 6 feature areas. * docs: review and update observability, console, multi-node, and compatibility pages - Global Observability: fix log format fields, add download button docs, split display limits into memory buffer vs rendered rows, correct settings labels - Host Console: remove internal implementation details per security docs policy, add stack directory behavior, expand header bar docs, remove unverified scrollback claim - Multi-Node: add Compose Directory field, document connection test details panel, fix edit/delete node behavior, simplify token security section, remove internal details - Node Compatibility: add missing self-update capability, remove internal endpoint paths and cache TTL, move from Features to Reference group in navigation - Refresh all screenshots for the redesigned UI (7 images) * docs: review and refresh fleet, remote updates, labels, alerts, routing, and webhooks pages - Fleet View: added node updates modal, container detail, version/update/critical badges, Tags filter - Remote Updates: removed internal details, added capability cross-link, fast polling - Stack Labels: three creation methods, two assignment methods, 10 colors, bulk actions screenshot - Alerts & Notifications: fixed metric labels, added notification popover detail, status banner - Notification Routing: HTTPS requirement, rule card layout, channel terminology fix - Webhooks: corrected license tier to Admiral, matched action labels to UI, removed internal security details, added local-only note - Troubleshooting: centralized entries from remote-updates, stack-labels, notification-routing - Refreshed all screenshots at 1920x1080, removed 11 orphaned images * docs: review and refresh RBAC, user management, and atomic deployments pages - RBAC: added missing Auditor role (5th role), updated permission matrix, fixed license tier references, documented username/password validation rules, self-deletion protection - Atomic Deployments: added "Which operations are protected" section covering webhooks/schedules/app store, removed internal backup path, fixed license tier to Skipper/Admiral - Screenshots: cropped to dialog element per updated strategic cropping guideline, removed 2 orphaned images * docs: review and refresh fleet-wide backups and audit log pages Update fleet-backups page to reflect current inline create form, add scheduled snapshots section, document the detail view and restore dialog, expand RBAC table to all five roles. Update audit log page to document expanded row detail fields, pagination, refresh button, and data retention screenshot. Replace all screenshots with fresh captures at 1920x720. * docs: review and refresh API tokens and private registries pages - API Tokens: clarify Full Admin scope, add Managing tokens section with card details, document revocation confirmation dialog, add usage tracking to security model, refresh screenshot - Private Registries: add Managing registries section with card details and action buttons, document edit behavior, fix URL auto-fill description, remove encryption algorithm name per security policy, fix grammar, refresh both screenshots * docs: review and refresh auto-update policies, scheduled operations, and SSO pages - Auto-Update Policies: document all 8 table columns, expand action buttons, add "All Stacks" wildcard option, fix field labels, add CSV export and pagination details, refresh screenshots - Scheduled Operations: fix System Prune target description, add Task List table columns, restructure create dialog fields with action-specific annotations, rewrite execution history with column table, refresh screenshots - SSO: remove encryption algorithm name per security policy, add LDAP and OIDC configuration field tables, document provider card controls (Save, Test Connection, Remove, Active badge), refresh screenshots - Move SSO troubleshooting entries to centralized troubleshooting page * docs: review and refresh licensing & billing page Update upgrade card feature lists to match actual tier gating (Skipper: fleet view, webhooks, labels, atomic deployments, backups, auto-update policies; Admiral: scoped RBAC, SSO, audit log, host console, API tokens, private registries, scheduled operations). Add flex layout to align upgrade card buttons at the bottom. Replace stale screenshot with fresh community and active license captures. Add feature breakdown subsection and profile menu billing shortcut to docs. * docs: review and refresh settings reference and security advisories pages Settings Reference: add 5 missing sections (SSO, API Tokens, Registries, Labels, Routing), expand Users from 2 to 5 roles, fix System Limits and Developer field labels to match UI, restructure Developer into Streaming and Data Retention sub-tables, update App Store and Support sections, refresh overview screenshot. Security Advisories: restructure into versioned sections (v0.25.x hardening and v0.19-v0.24 CVE remediation), expand from 3 bullet points to 10 specific improvements, fix GitHub URL from SaelixCode to AnsoCode, redact internal details per security docs policy. Remove "Sencho Pro" product name from all three pages, replaced with tier names (Community, Skipper, Admiral). * docs: review and refresh troubleshooting page, remove architecture and development guides - Rewrote forgotten password section to remove exposed SQL and table names - Updated all Settings navigation paths to Profile > Settings > X - Fixed network topology from "tab" to "view mode", added Pro license note - Updated Prune Networks to current "Prune Dead Networks" label - Corrected update check cooldown from vague to 2 minutes - Consolidated two network creation error sections into one - Removed hardcoded version reference (v0.34.0) - Replaced em dashes throughout - Deleted architecture.mdx (exposes internal implementation details) - Deleted development.mdx (contributor guide belongs in repo, not public docs) - Removed both pages from docs.json navigation * docs: review and refresh operations pages (backup, upgrade, self-hosting, troubleshooting) Backup & Restore: - Added missing encryption.key to all backup/restore procedures - Added Warning about restoring db without matching encryption key - Added cross-reference to Fleet-Wide Backups for paid tiers - Removed false claim about no built-in backup scheduler - Updated cron example to include encryption key copy Upgrading Sencho: - Removed internal migration details (table names, column specs, encryption algorithm) - Replaced with high-level migration summary per security docs policy - Added encryption.key to pre-upgrade backup command - Updated version pinning example from 0.25.3 to 0.38.0 - Added Remote Updates cross-reference for Skipper/Admiral users Self-Hosting Best Practices: - Removed JWT_SECRET from env var table (auto-generated, not an env var) - Removed PORT from env var table (hardcoded to 3000, not configurable) - Added API_RATE_LIMIT to env var table (actually exists in code) - Fixed listen port description from "configurable" to "fixed" - Updated resource recommendations based on measured footprint audit - Removed su-exec reference (internal implementation detail) - Upgraded data directory Note to Warning with file names Troubleshooting: - Fixed "Pro features" heading to "Paid features" with correct tier names
2783 lines
88 KiB
YAML
2783 lines
88 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Sencho API
|
|
version: 0.25.3
|
|
description: |
|
|
REST API for Sencho, a self-hosted Docker Compose management dashboard.
|
|
|
|
Sencho exposes a public API for automating stack deployments, managing webhooks,
|
|
monitoring fleet health, and integrating with CI/CD pipelines.
|
|
|
|
## Authentication
|
|
|
|
All authenticated endpoints accept a Bearer token in the `Authorization` header.
|
|
Generate API tokens from **Settings > API Tokens** in the Sencho dashboard (requires Admiral license).
|
|
|
|
```
|
|
Authorization: Bearer YOUR_API_TOKEN
|
|
```
|
|
|
|
## Node Routing
|
|
|
|
Sencho manages multiple nodes (local and remote). To target a specific node,
|
|
include the `x-node-id` header or `?nodeId=` query parameter. If omitted,
|
|
the request targets the default node.
|
|
|
|
## License Tiers
|
|
|
|
Some endpoints require a Pro or Admiral license. Requests to gated endpoints
|
|
on Community Edition return `403` with `code: "PRO_REQUIRED"` or `code: "ADMIRAL_REQUIRED"`.
|
|
contact:
|
|
name: Sencho
|
|
url: https://sencho.io
|
|
license:
|
|
name: BSL 1.1
|
|
url: https://github.com/AnsoCode/Sencho/blob/main/LICENSE
|
|
|
|
servers:
|
|
- url: "{protocol}://{host}:{port}"
|
|
description: Your Sencho instance
|
|
variables:
|
|
protocol:
|
|
default: https
|
|
enum: [http, https]
|
|
host:
|
|
default: localhost
|
|
port:
|
|
default: "3000"
|
|
|
|
security:
|
|
- bearerAuth: []
|
|
|
|
tags:
|
|
- name: Health
|
|
description: Instance health check
|
|
- name: Stacks
|
|
description: Create, read, update, delete, and operate Docker Compose stacks
|
|
- name: Containers
|
|
description: List and manage running containers
|
|
- name: API Tokens
|
|
description: Manage scoped API tokens (Admiral license required)
|
|
- name: Webhooks
|
|
description: Configure and trigger deployment webhooks (Pro license required)
|
|
- name: Nodes
|
|
description: Manage local and remote Sencho nodes
|
|
- name: Fleet
|
|
description: Multi-node fleet overview and snapshots (Pro license required)
|
|
- name: Scheduled Tasks
|
|
description: Configure recurring automated operations (Admiral license required)
|
|
- name: Registries
|
|
description: Manage private container registry credentials (Admiral license required). These endpoints are only accessible via browser sessions — API tokens receive `SCOPE_DENIED`.
|
|
- name: Image Updates
|
|
description: Check for available container image updates across stacks
|
|
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
description: API token generated from Settings > API Tokens. Scopes — `read-only`, `deploy-only`, or `full-admin`.
|
|
webhookSignature:
|
|
type: apiKey
|
|
in: header
|
|
name: X-Webhook-Signature
|
|
description: HMAC-SHA256 signature for webhook trigger requests. Compute as `sha256=HMAC(request_body, webhook_secret)`.
|
|
|
|
parameters:
|
|
stackName:
|
|
name: stackName
|
|
in: path
|
|
required: true
|
|
description: >
|
|
Stack directory name. Must match `^[a-zA-Z0-9_-]+$` (alphanumeric characters,
|
|
hyphens, and underscores only). Returns `400 Invalid stack name` if the name
|
|
contains path separators, dots, spaces, or other special characters.
|
|
schema:
|
|
type: string
|
|
pattern: '^[a-zA-Z0-9_-]+$'
|
|
example: my-stack
|
|
nodeId:
|
|
name: x-node-id
|
|
in: header
|
|
required: false
|
|
description: Target a specific node by ID. If omitted, the default node is used. Can also be passed as `?nodeId=` query parameter.
|
|
schema:
|
|
type: integer
|
|
example: 1
|
|
nodeIdQuery:
|
|
name: nodeId
|
|
in: query
|
|
required: false
|
|
description: Target a specific node by ID (alternative to `x-node-id` header).
|
|
schema:
|
|
type: integer
|
|
example: 1
|
|
idPath:
|
|
name: id
|
|
in: path
|
|
required: true
|
|
description: Numeric resource ID.
|
|
schema:
|
|
type: integer
|
|
|
|
schemas:
|
|
Error:
|
|
type: object
|
|
required: [error]
|
|
properties:
|
|
error:
|
|
type: string
|
|
description: Human-readable error message.
|
|
code:
|
|
type: string
|
|
description: Machine-readable error code (e.g., `PRO_REQUIRED`, `SCOPE_DENIED`).
|
|
enum: [PRO_REQUIRED, ADMIRAL_REQUIRED, SCOPE_DENIED]
|
|
|
|
SuccessMessage:
|
|
type: object
|
|
required: [message]
|
|
properties:
|
|
message:
|
|
type: string
|
|
|
|
SuccessBoolean:
|
|
type: object
|
|
required: [success]
|
|
properties:
|
|
success:
|
|
type: boolean
|
|
example: true
|
|
|
|
Container:
|
|
type: object
|
|
properties:
|
|
Id:
|
|
type: string
|
|
description: Docker container ID.
|
|
Names:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: Container names (prefixed with `/`).
|
|
Service:
|
|
type: string
|
|
description: Compose service name (if part of a stack).
|
|
State:
|
|
type: string
|
|
description: Container state.
|
|
enum: [running, exited, paused, restarting, dead, created]
|
|
Status:
|
|
type: string
|
|
description: Human-readable status string (e.g., "Up 2 hours").
|
|
Ports:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
PrivatePort:
|
|
type: integer
|
|
PublicPort:
|
|
type: integer
|
|
|
|
Node:
|
|
type: object
|
|
required: [id, name, type, compose_dir, is_default, created_at]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
type:
|
|
type: string
|
|
enum: [local, remote]
|
|
compose_dir:
|
|
type: string
|
|
description: Base directory for Docker Compose stacks on this node.
|
|
is_default:
|
|
type: integer
|
|
enum: [0, 1]
|
|
description: Whether this is the default node (1 = true).
|
|
api_url:
|
|
type: string
|
|
description: Remote node API URL (only for remote nodes).
|
|
api_token:
|
|
type: string
|
|
description: Redacted API token for remote node authentication.
|
|
created_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
|
|
Webhook:
|
|
type: object
|
|
required: [id, name, stack_name, action, secret, enabled, created_at]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
stack_name:
|
|
type: string
|
|
action:
|
|
type: string
|
|
enum: [deploy, restart, stop, start, pull]
|
|
secret:
|
|
type: string
|
|
description: Masked secret (e.g., `****...****`). Full secret is only returned on creation.
|
|
enabled:
|
|
type: integer
|
|
enum: [0, 1]
|
|
created_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
|
|
WebhookExecution:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
webhook_id:
|
|
type: integer
|
|
triggered_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
status:
|
|
type: string
|
|
response:
|
|
type: string
|
|
error:
|
|
type: ["string", "null"]
|
|
|
|
ApiToken:
|
|
type: object
|
|
required: [id, name, scope, user_id, created_at]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
scope:
|
|
type: string
|
|
enum: [read-only, deploy-only, full-admin]
|
|
user_id:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
expires_at:
|
|
type: ["integer", "null"]
|
|
description: Unix timestamp (seconds) or null for no expiry.
|
|
revoked_at:
|
|
type: ["integer", "null"]
|
|
last_used_at:
|
|
type: ["integer", "null"]
|
|
|
|
FleetNodeOverview:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
type:
|
|
type: string
|
|
enum: [local, remote]
|
|
status:
|
|
type: string
|
|
enum: [online, offline]
|
|
stats:
|
|
type: ["object", "null"]
|
|
description: Container statistics for the node.
|
|
systemStats:
|
|
type: ["object", "null"]
|
|
description: Host system resource statistics.
|
|
stacks:
|
|
type: ["integer", "null"]
|
|
description: Number of stacks on this node.
|
|
|
|
FleetSnapshot:
|
|
type: object
|
|
required: [id, description, created_by, created_at, node_count, stack_count]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
description:
|
|
type: string
|
|
created_by:
|
|
type: string
|
|
created_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
node_count:
|
|
type: integer
|
|
stack_count:
|
|
type: integer
|
|
skipped_nodes:
|
|
type: string
|
|
description: JSON-stringified array of skipped node names.
|
|
|
|
FleetSnapshotDetail:
|
|
allOf:
|
|
- $ref: "#/components/schemas/FleetSnapshot"
|
|
- type: object
|
|
properties:
|
|
nodes:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
nodeId:
|
|
type: integer
|
|
nodeName:
|
|
type: string
|
|
stacks:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
stackName:
|
|
type: string
|
|
files:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
filename:
|
|
type: string
|
|
content:
|
|
type: string
|
|
|
|
ScheduledTask:
|
|
type: object
|
|
required: [id, name, target_type, action, cron_expression, enabled, created_by, created_at, updated_at]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
target_type:
|
|
type: string
|
|
enum: [stack, fleet, system]
|
|
target_id:
|
|
type: ["string", "null"]
|
|
description: Stack name (when target_type is `stack`).
|
|
node_id:
|
|
type: ["integer", "null"]
|
|
description: Target node ID (when target_type is `stack`).
|
|
action:
|
|
type: string
|
|
enum: [restart, snapshot, prune]
|
|
cron_expression:
|
|
type: string
|
|
description: Standard cron expression (5 fields).
|
|
example: "0 3 * * *"
|
|
enabled:
|
|
type: integer
|
|
enum: [0, 1]
|
|
created_by:
|
|
type: string
|
|
created_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
updated_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
last_run_at:
|
|
type: ["integer", "null"]
|
|
next_run_at:
|
|
type: ["integer", "null"]
|
|
last_status:
|
|
type: ["string", "null"]
|
|
last_error:
|
|
type: ["string", "null"]
|
|
prune_targets:
|
|
type: ["string", "null"]
|
|
description: JSON-stringified array of prune targets (containers, images, networks, volumes).
|
|
target_services:
|
|
type: ["string", "null"]
|
|
description: JSON-stringified array of service names to restart.
|
|
prune_label_filter:
|
|
type: ["string", "null"]
|
|
description: Docker label filter for prune operations.
|
|
|
|
ScheduledTaskRun:
|
|
type: object
|
|
required: [id, task_id, started_at, status, triggered_by]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
task_id:
|
|
type: integer
|
|
started_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
completed_at:
|
|
type: ["integer", "null"]
|
|
triggered_by:
|
|
type: string
|
|
enum: [scheduled, manual]
|
|
status:
|
|
type: string
|
|
enum: [pending, running, success, failed]
|
|
output:
|
|
type: ["string", "null"]
|
|
error:
|
|
type: ["string", "null"]
|
|
|
|
Registry:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
example: GitHub Container Registry
|
|
url:
|
|
type: string
|
|
example: ghcr.io
|
|
type:
|
|
type: string
|
|
enum: [dockerhub, ghcr, ecr, custom]
|
|
username:
|
|
type: string
|
|
aws_region:
|
|
type: ["string", "null"]
|
|
description: AWS region (only present for ECR registries).
|
|
created_at:
|
|
type: integer
|
|
description: Unix timestamp (seconds).
|
|
|
|
RegistryCreate:
|
|
type: object
|
|
required: [name, url, type, username, secret]
|
|
properties:
|
|
name:
|
|
type: string
|
|
maxLength: 100
|
|
example: GitHub Container Registry
|
|
url:
|
|
type: string
|
|
maxLength: 500
|
|
example: ghcr.io
|
|
type:
|
|
type: string
|
|
enum: [dockerhub, ghcr, ecr, custom]
|
|
username:
|
|
type: string
|
|
example: my-user
|
|
secret:
|
|
type: string
|
|
description: Password, token, or access key. Write-only — never returned in GET responses.
|
|
aws_region:
|
|
type: string
|
|
description: Required when `type` is `ecr`.
|
|
example: us-east-1
|
|
|
|
ImageUpdateStatus:
|
|
type: object
|
|
properties:
|
|
stack_name:
|
|
type: string
|
|
description: Name of the stack.
|
|
has_updates:
|
|
type: integer
|
|
enum: [0, 1]
|
|
description: Whether any images in this stack have newer versions available.
|
|
last_checked:
|
|
type: ["integer", "null"]
|
|
description: Unix timestamp of the last check.
|
|
|
|
responses:
|
|
Unauthorized:
|
|
description: Authentication required. Provide a valid Bearer token.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
example:
|
|
error: "Authentication required"
|
|
Forbidden:
|
|
description: Insufficient permissions or license tier.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
example:
|
|
error: "This feature requires Sencho Pro."
|
|
code: "PRO_REQUIRED"
|
|
NotFound:
|
|
description: Resource not found.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
InternalError:
|
|
description: Internal server error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
paths:
|
|
# ── Health ──────────────────────────────────────────────
|
|
/api/health:
|
|
get:
|
|
operationId: getHealth
|
|
tags: [Health]
|
|
summary: Health check
|
|
description: Returns instance health status and uptime. No authentication required. Useful for uptime monitors and Docker HEALTHCHECK.
|
|
security: []
|
|
responses:
|
|
"200":
|
|
description: Instance is healthy.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [status, uptime]
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: ok
|
|
uptime:
|
|
type: number
|
|
description: Process uptime in seconds.
|
|
|
|
/api/meta:
|
|
get:
|
|
operationId: getMeta
|
|
tags: [Health]
|
|
summary: Instance metadata
|
|
description: Returns this Sencho instance's version and list of supported capabilities. No authentication required. Used by nodes to negotiate feature compatibility.
|
|
security: []
|
|
responses:
|
|
"200":
|
|
description: Instance metadata.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [version, capabilities]
|
|
properties:
|
|
version:
|
|
type: string
|
|
description: Sencho version (semver).
|
|
example: "0.32.0"
|
|
capabilities:
|
|
type: array
|
|
description: List of feature capabilities this instance supports.
|
|
items:
|
|
type: string
|
|
example: ["stacks", "containers", "fleet", "auto-updates", "host-console"]
|
|
|
|
/api/system/update:
|
|
post:
|
|
operationId: triggerSelfUpdate
|
|
tags: [Health]
|
|
summary: Trigger self-update
|
|
description: |
|
|
Instructs this Sencho instance to pull the latest Docker image and recreate its own container.
|
|
Returns 202 immediately; the actual update happens asynchronously after the response is sent.
|
|
Requires the instance to be deployed via Docker Compose with the Docker socket mounted.
|
|
responses:
|
|
"202":
|
|
description: Update initiated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
example: "Update initiated. The server will restart shortly."
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"503":
|
|
description: Self-update not available (not running in Docker Compose).
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
# ── Stacks ──────────────────────────────────────────────
|
|
/api/stacks:
|
|
get:
|
|
operationId: listStacks
|
|
tags: [Stacks]
|
|
summary: List all stacks
|
|
description: Returns an array of stack directory names on the target node.
|
|
parameters:
|
|
- $ref: "#/components/parameters/nodeId"
|
|
- $ref: "#/components/parameters/nodeIdQuery"
|
|
responses:
|
|
"200":
|
|
description: Array of stack names.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
example: ["traefik", "portainer", "monitoring"]
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
post:
|
|
operationId: createStack
|
|
tags: [Stacks]
|
|
summary: Create a new stack
|
|
description: Creates a new stack directory with a default `compose.yaml` file.
|
|
parameters:
|
|
- $ref: "#/components/parameters/nodeId"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [stackName]
|
|
properties:
|
|
stackName:
|
|
type: string
|
|
description: Stack name. Alphanumeric characters, hyphens, and underscores only.
|
|
pattern: "^[a-zA-Z0-9_-]+$"
|
|
example: my-new-stack
|
|
responses:
|
|
"201":
|
|
description: Stack created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [message, name]
|
|
properties:
|
|
message:
|
|
type: string
|
|
example: Stack created successfully
|
|
name:
|
|
type: string
|
|
example: my-new-stack
|
|
"400":
|
|
description: Invalid stack name.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"409":
|
|
description: Stack already exists.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
example:
|
|
error: Stack already exists
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}:
|
|
get:
|
|
operationId: getStack
|
|
tags: [Stacks]
|
|
summary: Get stack compose file
|
|
description: Returns the raw `compose.yaml` content for the specified stack.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Raw compose.yaml content.
|
|
content:
|
|
text/plain:
|
|
schema:
|
|
type: string
|
|
"400":
|
|
description: Invalid stack name.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
put:
|
|
operationId: updateStack
|
|
tags: [Stacks]
|
|
summary: Update stack compose file
|
|
description: Overwrites the `compose.yaml` content for the specified stack. Requires `stack:edit` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [content]
|
|
properties:
|
|
content:
|
|
type: string
|
|
description: Full compose.yaml content.
|
|
responses:
|
|
"200":
|
|
description: Stack saved.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Stack saved successfully
|
|
"400":
|
|
description: Invalid stack name or content.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
delete:
|
|
operationId: deleteStack
|
|
tags: [Stacks]
|
|
summary: Delete stack
|
|
description: Permanently deletes the stack directory and all its files. Requires `stack:delete` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Stack deleted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Stack deleted successfully
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/envs:
|
|
get:
|
|
operationId: getStackEnvFiles
|
|
tags: [Stacks]
|
|
summary: List env file paths
|
|
description: Returns the list of environment file paths referenced in the stack's compose file (`env_file` directives).
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: List of env file paths.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [envFiles]
|
|
properties:
|
|
envFiles:
|
|
type: array
|
|
items:
|
|
type: string
|
|
example: [".env", "config/database.env"]
|
|
"400":
|
|
description: Invalid stack name.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/env:
|
|
get:
|
|
operationId: getStackEnv
|
|
tags: [Stacks]
|
|
summary: Get env file content
|
|
description: Returns the content of an environment file. Defaults to `.env` if no `file` query parameter is specified.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
- name: file
|
|
in: query
|
|
required: false
|
|
description: Path to a specific env file (must be in the resolved env files list).
|
|
schema:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: Raw env file content (KEY=VALUE format).
|
|
content:
|
|
text/plain:
|
|
schema:
|
|
type: string
|
|
"400":
|
|
description: Invalid stack name or disallowed file path.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"404":
|
|
description: Env file not found.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
example:
|
|
error: Env file not found
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
put:
|
|
operationId: updateStackEnv
|
|
tags: [Stacks]
|
|
summary: Update env file
|
|
description: Overwrites an environment file's content. Requires `stack:edit` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
- name: file
|
|
in: query
|
|
required: false
|
|
description: Path to a specific env file to update.
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [content]
|
|
properties:
|
|
content:
|
|
type: string
|
|
description: Full env file content (KEY=VALUE format).
|
|
responses:
|
|
"200":
|
|
description: Env file saved.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Env file saved successfully
|
|
"400":
|
|
description: Invalid input.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/containers:
|
|
get:
|
|
operationId: getStackContainers
|
|
tags: [Stacks]
|
|
summary: List stack containers
|
|
description: Returns all containers belonging to the specified stack.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Array of container objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Container"
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/services:
|
|
get:
|
|
operationId: getStackServices
|
|
tags: [Stacks]
|
|
summary: List stack services
|
|
description: Returns the service names defined in the stack's compose file.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Array of service names.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
example: ["web", "db", "redis"]
|
|
"400":
|
|
description: Invalid stack name.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/deploy:
|
|
post:
|
|
operationId: deployStack
|
|
tags: [Stacks]
|
|
summary: Deploy stack
|
|
description: |
|
|
Runs `docker compose up -d` for the stack. On Pro tier, uses atomic deployment
|
|
with automatic rollback on failure. Requires `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Stack deployed successfully.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Deployed successfully
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
description: Deployment failed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [error]
|
|
properties:
|
|
error:
|
|
type: string
|
|
rolledBack:
|
|
type: boolean
|
|
description: Whether the stack was automatically rolled back (Pro tier).
|
|
|
|
/api/stacks/{stackName}/down:
|
|
post:
|
|
operationId: downStack
|
|
tags: [Stacks]
|
|
summary: Tear down stack
|
|
description: Runs `docker compose down` for the stack, removing containers and networks. Requires `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Command started.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: Command started
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/start:
|
|
post:
|
|
operationId: startStack
|
|
tags: [Stacks]
|
|
summary: Start stack containers
|
|
description: Starts all stopped containers in the stack via the Docker Engine API. Requires `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Containers started.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
description: No containers found for this stack.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/stop:
|
|
post:
|
|
operationId: stopStack
|
|
tags: [Stacks]
|
|
summary: Stop stack containers
|
|
description: Stops all running containers in the stack via the Docker Engine API. Requires `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Containers stopped.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
description: No containers found for this stack.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/restart:
|
|
post:
|
|
operationId: restartStack
|
|
tags: [Stacks]
|
|
summary: Restart stack containers
|
|
description: Restarts all containers in the stack via the Docker Engine API. Requires `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Containers restarted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
description: No containers found for this stack.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/update:
|
|
post:
|
|
operationId: updateStackImages
|
|
tags: [Stacks]
|
|
summary: Pull and recreate stack
|
|
description: |
|
|
Pulls latest images and recreates containers (`docker compose pull && up -d`).
|
|
On Pro tier, uses atomic update with automatic rollback on failure.
|
|
Requires `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Update completed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: Update completed
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
description: Update failed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [error]
|
|
properties:
|
|
error:
|
|
type: string
|
|
rolledBack:
|
|
type: boolean
|
|
|
|
/api/stacks/{stackName}/rollback:
|
|
post:
|
|
operationId: rollbackStack
|
|
tags: [Stacks]
|
|
summary: Rollback stack
|
|
description: Restores the stack to its previous deployment state. Requires Pro license and `stack:deploy` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Stack rolled back.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Stack rolled back successfully.
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
description: No backup available.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
example:
|
|
error: No backup available for this stack.
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/stacks/{stackName}/backup:
|
|
get:
|
|
operationId: getStackBackupStatus
|
|
tags: [Stacks]
|
|
summary: Check backup status
|
|
description: Returns whether a deployment backup exists for this stack and when it was created.
|
|
parameters:
|
|
- $ref: "#/components/parameters/stackName"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Backup status.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [exists]
|
|
properties:
|
|
exists:
|
|
type: boolean
|
|
timestamp:
|
|
type: ["integer", "null"]
|
|
description: Unix timestamp of the backup, or null if no backup exists.
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
|
|
# ── Containers ──────────────────────────────────────────
|
|
/api/containers:
|
|
get:
|
|
operationId: listContainers
|
|
tags: [Containers]
|
|
summary: List all containers
|
|
description: Returns all running containers on the target node.
|
|
parameters:
|
|
- $ref: "#/components/parameters/nodeId"
|
|
- $ref: "#/components/parameters/nodeIdQuery"
|
|
responses:
|
|
"200":
|
|
description: Array of container objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Container"
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/containers/{id}/logs:
|
|
get:
|
|
operationId: streamContainerLogs
|
|
tags: [Containers]
|
|
summary: Stream container logs
|
|
description: |
|
|
Streams container logs as Server-Sent Events (SSE). The connection remains
|
|
open and pushes new log lines as `data:` events in real time.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: SSE log stream.
|
|
content:
|
|
text/event-stream:
|
|
schema:
|
|
type: string
|
|
description: Each event is a `data:` line containing a JSON-encoded log string.
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/containers/{id}/start:
|
|
post:
|
|
operationId: startContainer
|
|
tags: [Containers]
|
|
summary: Start container
|
|
description: Starts a stopped container. Requires admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Container started.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Container started
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/containers/{id}/stop:
|
|
post:
|
|
operationId: stopContainer
|
|
tags: [Containers]
|
|
summary: Stop container
|
|
description: Stops a running container. Requires admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Container stopped.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Container stopped
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/containers/{id}/restart:
|
|
post:
|
|
operationId: restartContainer
|
|
tags: [Containers]
|
|
summary: Restart container
|
|
description: Restarts a container. Requires admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
- $ref: "#/components/parameters/nodeId"
|
|
responses:
|
|
"200":
|
|
description: Container restarted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Container restarted
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
# ── API Tokens ──────────────────────────────────────────
|
|
/api/api-tokens:
|
|
post:
|
|
operationId: createApiToken
|
|
tags: [API Tokens]
|
|
summary: Create API token
|
|
description: |
|
|
Generates a new scoped API token. The full token is only returned in the creation response
|
|
and cannot be retrieved again. Requires Admiral license and admin role.
|
|
|
|
**Note:** API tokens cannot create other API tokens.
|
|
responses:
|
|
"201":
|
|
description: Token created. The `token` field contains the full JWT — save it now, it won't be shown again.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [id, token]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
token:
|
|
type: string
|
|
description: Full JWT token. Store securely — this is the only time it's returned.
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [name, scope]
|
|
properties:
|
|
name:
|
|
type: string
|
|
description: Human-readable token name.
|
|
maxLength: 100
|
|
example: CI/CD Deploy Token
|
|
scope:
|
|
type: string
|
|
description: |
|
|
Permission scope:
|
|
- `read-only` — GET requests only
|
|
- `deploy-only` — Read + stack deploy/stop/restart operations
|
|
- `full-admin` — All operations (except license, user, and token management)
|
|
enum: [read-only, deploy-only, full-admin]
|
|
expires_in:
|
|
type: ["integer", "null"]
|
|
description: Token lifetime in days. Use `null` for no expiry.
|
|
enum: [30, 60, 90, 365, null]
|
|
example: 90
|
|
get:
|
|
operationId: listApiTokens
|
|
tags: [API Tokens]
|
|
summary: List API tokens
|
|
description: |
|
|
Returns all API tokens for the current user. Token hashes are never exposed.
|
|
Requires Admiral license and admin role.
|
|
|
|
**Note:** API tokens cannot list other API tokens.
|
|
responses:
|
|
"200":
|
|
description: Array of token metadata.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ApiToken"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/api-tokens/{id}:
|
|
delete:
|
|
operationId: revokeApiToken
|
|
tags: [API Tokens]
|
|
summary: Revoke API token
|
|
description: |
|
|
Permanently revokes an API token. Users can only revoke their own tokens.
|
|
Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Token revoked.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"400":
|
|
description: Invalid token ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
# ── Webhooks ────────────────────────────────────────────
|
|
/api/webhooks:
|
|
get:
|
|
operationId: listWebhooks
|
|
tags: [Webhooks]
|
|
summary: List webhooks
|
|
description: Returns all configured webhooks with masked secrets. Requires Pro license.
|
|
responses:
|
|
"200":
|
|
description: Array of webhook objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Webhook"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
post:
|
|
operationId: createWebhook
|
|
tags: [Webhooks]
|
|
summary: Create webhook
|
|
description: |
|
|
Creates a new webhook for a stack. The webhook secret is auto-generated and only
|
|
returned in the creation response. Requires Pro license and admin role.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [name, stack_name, action]
|
|
properties:
|
|
name:
|
|
type: string
|
|
example: GitHub Deploy Hook
|
|
stack_name:
|
|
type: string
|
|
description: Target stack name.
|
|
example: my-app
|
|
action:
|
|
type: string
|
|
enum: [deploy, restart, stop, start, pull]
|
|
description: Action to execute when the webhook is triggered.
|
|
enabled:
|
|
type: boolean
|
|
default: true
|
|
responses:
|
|
"201":
|
|
description: Webhook created. The `secret` field is the HMAC signing key — save it now.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [id, secret]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
secret:
|
|
type: string
|
|
description: HMAC-SHA256 signing secret. Store securely — this is the only time it's returned in full.
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/webhooks/{id}:
|
|
put:
|
|
operationId: updateWebhook
|
|
tags: [Webhooks]
|
|
summary: Update webhook
|
|
description: Updates webhook configuration. Requires Pro license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
stack_name:
|
|
type: string
|
|
action:
|
|
type: string
|
|
enum: [deploy, restart, stop, start, pull]
|
|
enabled:
|
|
type: boolean
|
|
responses:
|
|
"200":
|
|
description: Webhook updated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
delete:
|
|
operationId: deleteWebhook
|
|
tags: [Webhooks]
|
|
summary: Delete webhook
|
|
description: Permanently deletes a webhook. Requires Pro license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Webhook deleted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/webhooks/{id}/history:
|
|
get:
|
|
operationId: getWebhookHistory
|
|
tags: [Webhooks]
|
|
summary: Get webhook execution history
|
|
description: Returns the execution log for a webhook. Requires Pro license.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Array of execution records.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/WebhookExecution"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/webhooks/{id}/trigger:
|
|
post:
|
|
operationId: triggerWebhook
|
|
tags: [Webhooks]
|
|
summary: Trigger webhook
|
|
description: |
|
|
Externally triggers a webhook action. This endpoint is public but requires a valid
|
|
HMAC-SHA256 signature in the `X-Webhook-Signature` header.
|
|
|
|
Compute the signature as: `sha256=` + HMAC-SHA256(raw_request_body, webhook_secret).
|
|
security:
|
|
- webhookSignature: []
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
action:
|
|
type: string
|
|
description: Override the default webhook action.
|
|
responses:
|
|
"202":
|
|
description: Webhook accepted and action queued.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [message, action]
|
|
properties:
|
|
message:
|
|
type: string
|
|
example: Webhook accepted
|
|
action:
|
|
type: string
|
|
example: deploy
|
|
"401":
|
|
description: Missing or invalid signature.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
description: Webhook not found or disabled.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
# ── Nodes ───────────────────────────────────────────────
|
|
/api/nodes:
|
|
get:
|
|
operationId: listNodes
|
|
tags: [Nodes]
|
|
summary: List all nodes
|
|
description: Returns all registered nodes (local and remote).
|
|
responses:
|
|
"200":
|
|
description: Array of node objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Node"
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
post:
|
|
operationId: createNode
|
|
tags: [Nodes]
|
|
summary: Register a new node
|
|
description: |
|
|
Adds a new local or remote node. Remote nodes require an API URL pointing to
|
|
another Sencho instance and an API token for authentication.
|
|
Requires `node:manage` permission.
|
|
|
|
**Note:** API tokens cannot manage nodes.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [name, type]
|
|
properties:
|
|
name:
|
|
type: string
|
|
example: production-server
|
|
type:
|
|
type: string
|
|
enum: [local, remote]
|
|
compose_dir:
|
|
type: string
|
|
description: Base directory for Compose stacks (defaults to `/app/compose`).
|
|
is_default:
|
|
type: boolean
|
|
description: Set as the default node.
|
|
api_url:
|
|
type: string
|
|
description: Remote Sencho instance URL (required for remote nodes).
|
|
example: https://sencho.example.com:3000
|
|
api_token:
|
|
type: string
|
|
description: API token for authenticating with the remote Sencho instance.
|
|
responses:
|
|
"200":
|
|
description: Node created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [success, id]
|
|
properties:
|
|
success:
|
|
type: boolean
|
|
id:
|
|
type: integer
|
|
description: New node ID.
|
|
warning:
|
|
type: string
|
|
description: Warning if the remote URL uses plain HTTP.
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"409":
|
|
description: Node name already exists.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/nodes/{id}:
|
|
get:
|
|
operationId: getNode
|
|
tags: [Nodes]
|
|
summary: Get node details
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Node object.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Node"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
put:
|
|
operationId: updateNode
|
|
tags: [Nodes]
|
|
summary: Update node
|
|
description: Updates node configuration. Requires `node:manage` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
compose_dir:
|
|
type: string
|
|
is_default:
|
|
type: boolean
|
|
api_url:
|
|
type: string
|
|
api_token:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: Node updated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [success]
|
|
properties:
|
|
success:
|
|
type: boolean
|
|
warning:
|
|
type: string
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
delete:
|
|
operationId: deleteNode
|
|
tags: [Nodes]
|
|
summary: Delete node
|
|
description: Removes a node from the fleet. Requires `node:manage` permission.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Node deleted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/nodes/{id}/test:
|
|
post:
|
|
operationId: testNodeConnection
|
|
tags: [Nodes]
|
|
summary: Test node connection
|
|
description: Tests connectivity to a node. For remote nodes, pings the remote Sencho instance's health endpoint.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Connection test result.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [success]
|
|
properties:
|
|
success:
|
|
type: boolean
|
|
message:
|
|
type: string
|
|
error:
|
|
type: string
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/nodes/{id}/meta:
|
|
get:
|
|
operationId: getNodeMeta
|
|
tags: [Nodes]
|
|
summary: Get node capabilities
|
|
description: "Returns the version and supported capabilities for a specific node. For local nodes, returns this instance's own metadata. For remote nodes, relays the response from the remote instance's `/api/meta` endpoint. Returns `{ version: null, capabilities: [] }` if the remote node is offline or doesn't support the meta endpoint."
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Node capability metadata.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [version, capabilities]
|
|
properties:
|
|
version:
|
|
type: string
|
|
nullable: true
|
|
description: Sencho version of the node, or null if unavailable.
|
|
example: "0.31.0"
|
|
capabilities:
|
|
type: array
|
|
description: List of feature capabilities the node supports. Empty array if unavailable.
|
|
items:
|
|
type: string
|
|
example: ["stacks", "containers", "fleet"]
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
|
|
# ── Fleet ───────────────────────────────────────────────
|
|
/api/fleet/overview:
|
|
get:
|
|
operationId: getFleetOverview
|
|
tags: [Fleet]
|
|
summary: Fleet overview
|
|
description: Returns an aggregated overview of all nodes including status, container stats, and stack counts.
|
|
responses:
|
|
"200":
|
|
description: Array of fleet node overviews.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/FleetNodeOverview"
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/fleet/node/{nodeId}/stacks:
|
|
get:
|
|
operationId: getFleetNodeStacks
|
|
tags: [Fleet]
|
|
summary: List stacks on a fleet node
|
|
description: Returns stack names from a specific fleet node. Requires Pro license.
|
|
parameters:
|
|
- name: nodeId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
"200":
|
|
description: Array of stack names.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"502":
|
|
description: Failed to reach remote node.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"503":
|
|
description: Remote node not configured.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/api/fleet/node/{nodeId}/stacks/{stackName}/containers:
|
|
get:
|
|
operationId: getFleetNodeStackContainers
|
|
tags: [Fleet]
|
|
summary: List containers in a fleet node stack
|
|
description: Returns containers for a specific stack on a specific fleet node. Requires Pro license.
|
|
parameters:
|
|
- name: nodeId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
- $ref: "#/components/parameters/stackName"
|
|
responses:
|
|
"200":
|
|
description: Array of container objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Container"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
description: Remote node not configured.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/api/fleet/update-status:
|
|
get:
|
|
operationId: getFleetUpdateStatus
|
|
tags: [Fleet]
|
|
summary: Fleet update status
|
|
description: Returns version comparison and active update status for all nodes. Compares each node's version against the gateway's version.
|
|
responses:
|
|
"200":
|
|
description: Update status for all nodes.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
nodes:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
nodeId:
|
|
type: integer
|
|
name:
|
|
type: string
|
|
type:
|
|
type: string
|
|
enum: [local, remote]
|
|
version:
|
|
type: string
|
|
nullable: true
|
|
latestVersion:
|
|
type: string
|
|
updateAvailable:
|
|
type: boolean
|
|
updateStatus:
|
|
type: string
|
|
nullable: true
|
|
enum: [updating, completed, timeout, failed, null]
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"403":
|
|
$ref: "#/components/responses/ProRequired"
|
|
|
|
/api/fleet/nodes/{nodeId}/update:
|
|
post:
|
|
operationId: triggerNodeUpdate
|
|
tags: [Fleet]
|
|
summary: Trigger node update
|
|
description: Initiates a self-update on the specified node. For remote nodes, sends the update command via the proxy. For local nodes, triggers the update directly.
|
|
parameters:
|
|
- name: nodeId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
"202":
|
|
description: Update initiated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"403":
|
|
$ref: "#/components/responses/ProRequired"
|
|
"404":
|
|
description: Node not found.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"409":
|
|
description: Update already in progress.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"503":
|
|
description: Self-update not available on the target node.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
|
|
/api/fleet/update-all:
|
|
post:
|
|
operationId: triggerFleetUpdateAll
|
|
tags: [Fleet]
|
|
summary: Update all outdated nodes
|
|
description: Triggers self-update on all remote nodes running an older version than the gateway. Local nodes are excluded from bulk updates.
|
|
responses:
|
|
"202":
|
|
description: Bulk update initiated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
updating:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: Names of nodes where update was triggered.
|
|
skipped:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: Names of nodes that were skipped.
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"403":
|
|
$ref: "#/components/responses/ProRequired"
|
|
|
|
/api/fleet/snapshots:
|
|
post:
|
|
operationId: createFleetSnapshot
|
|
tags: [Fleet]
|
|
summary: Create fleet snapshot
|
|
description: Creates a point-in-time backup of all compose files across all nodes. Requires Pro license and admin role.
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
description:
|
|
type: string
|
|
description: Optional description for the snapshot.
|
|
example: Pre-migration backup
|
|
responses:
|
|
"201":
|
|
description: Snapshot created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/FleetSnapshot"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
get:
|
|
operationId: listFleetSnapshots
|
|
tags: [Fleet]
|
|
summary: List fleet snapshots
|
|
description: Returns paginated fleet snapshots. Requires Pro license.
|
|
parameters:
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
maximum: 100
|
|
- name: offset
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
responses:
|
|
"200":
|
|
description: Paginated snapshot list.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [snapshots, total]
|
|
properties:
|
|
snapshots:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/FleetSnapshot"
|
|
total:
|
|
type: integer
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/fleet/snapshots/{id}:
|
|
get:
|
|
operationId: getFleetSnapshot
|
|
tags: [Fleet]
|
|
summary: Get snapshot details
|
|
description: Returns full snapshot details including all captured files grouped by node and stack. Requires Pro license.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Snapshot with file contents.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/FleetSnapshotDetail"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
delete:
|
|
operationId: deleteFleetSnapshot
|
|
tags: [Fleet]
|
|
summary: Delete snapshot
|
|
description: Permanently deletes a fleet snapshot. Requires Pro license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Snapshot deleted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/fleet/snapshots/{id}/restore:
|
|
post:
|
|
operationId: restoreFleetSnapshot
|
|
tags: [Fleet]
|
|
summary: Restore from snapshot
|
|
description: Restores a specific stack on a specific node from the snapshot. Optionally redeploys after restore. Requires Pro license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [nodeId, stackName]
|
|
properties:
|
|
nodeId:
|
|
type: integer
|
|
description: Target node ID to restore to.
|
|
stackName:
|
|
type: string
|
|
description: Stack to restore.
|
|
redeploy:
|
|
type: boolean
|
|
default: false
|
|
description: Whether to redeploy the stack after restoring files.
|
|
responses:
|
|
"200":
|
|
description: Stack restored.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessMessage"
|
|
example:
|
|
message: Stack restored successfully.
|
|
"400":
|
|
description: Missing required fields.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
# ── Scheduled Tasks ─────────────────────────────────────
|
|
/api/scheduled-tasks:
|
|
get:
|
|
operationId: listScheduledTasks
|
|
tags: [Scheduled Tasks]
|
|
summary: List scheduled tasks
|
|
description: Returns all scheduled tasks. Requires Admiral license and admin role.
|
|
responses:
|
|
"200":
|
|
description: Array of scheduled task objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ScheduledTask"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
post:
|
|
operationId: createScheduledTask
|
|
tags: [Scheduled Tasks]
|
|
summary: Create scheduled task
|
|
description: |
|
|
Creates a new recurring task. Action-target rules:
|
|
- `restart` requires `target_type: stack` (with `target_id` and `node_id`)
|
|
- `snapshot` requires `target_type: fleet`
|
|
- `prune` requires `target_type: system`
|
|
|
|
Requires Admiral license and admin role.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [name, target_type, action, cron_expression]
|
|
properties:
|
|
name:
|
|
type: string
|
|
example: Nightly restart
|
|
target_type:
|
|
type: string
|
|
enum: [stack, fleet, system]
|
|
target_id:
|
|
type: string
|
|
description: Stack name (required when target_type is `stack`).
|
|
node_id:
|
|
type: integer
|
|
description: Target node ID (required when target_type is `stack`).
|
|
action:
|
|
type: string
|
|
enum: [restart, snapshot, prune]
|
|
cron_expression:
|
|
type: string
|
|
description: Standard 5-field cron expression.
|
|
example: "0 3 * * *"
|
|
enabled:
|
|
type: boolean
|
|
default: true
|
|
prune_targets:
|
|
type: array
|
|
items:
|
|
type: string
|
|
enum: [containers, images, networks, volumes]
|
|
description: Resources to prune (only for `prune` action).
|
|
target_services:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: Specific services to restart (only for `restart` action).
|
|
prune_label_filter:
|
|
type: string
|
|
description: Docker label filter for prune operations.
|
|
responses:
|
|
"201":
|
|
description: Task created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ScheduledTask"
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/scheduled-tasks/{id}:
|
|
get:
|
|
operationId: getScheduledTask
|
|
tags: [Scheduled Tasks]
|
|
summary: Get scheduled task
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Scheduled task object.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ScheduledTask"
|
|
"400":
|
|
description: Invalid task ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
put:
|
|
operationId: updateScheduledTask
|
|
tags: [Scheduled Tasks]
|
|
summary: Update scheduled task
|
|
description: Updates task configuration. Same validation rules as creation apply. Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
target_type:
|
|
type: string
|
|
enum: [stack, fleet, system]
|
|
target_id:
|
|
type: string
|
|
node_id:
|
|
type: integer
|
|
action:
|
|
type: string
|
|
enum: [restart, snapshot, prune]
|
|
cron_expression:
|
|
type: string
|
|
enabled:
|
|
type: boolean
|
|
prune_targets:
|
|
type: array
|
|
items:
|
|
type: string
|
|
target_services:
|
|
type: array
|
|
items:
|
|
type: string
|
|
prune_label_filter:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: Task updated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ScheduledTask"
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
delete:
|
|
operationId: deleteScheduledTask
|
|
tags: [Scheduled Tasks]
|
|
summary: Delete scheduled task
|
|
description: Permanently deletes a scheduled task. Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Task deleted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"400":
|
|
description: Invalid task ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/scheduled-tasks/{id}/toggle:
|
|
patch:
|
|
operationId: toggleScheduledTask
|
|
tags: [Scheduled Tasks]
|
|
summary: Toggle task enabled/disabled
|
|
description: Flips the enabled state of a scheduled task. Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Task toggled. Returns updated task.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ScheduledTask"
|
|
"400":
|
|
description: Invalid task ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/scheduled-tasks/{id}/run:
|
|
post:
|
|
operationId: runScheduledTask
|
|
tags: [Scheduled Tasks]
|
|
summary: Run task immediately
|
|
description: Executes the scheduled task immediately, regardless of its cron schedule. Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Task executed. Returns updated task with `last_run_at`.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ScheduledTask"
|
|
"400":
|
|
description: Invalid task ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/scheduled-tasks/{id}/runs:
|
|
get:
|
|
operationId: listScheduledTaskRuns
|
|
tags: [Scheduled Tasks]
|
|
summary: List task execution history
|
|
description: Returns paginated execution history for a scheduled task. Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
- name: limit
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 50
|
|
- name: offset
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
default: 0
|
|
responses:
|
|
"200":
|
|
description: Array of task run records.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ScheduledTaskRun"
|
|
"400":
|
|
description: Invalid task ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/scheduled-tasks/{id}/runs/export:
|
|
get:
|
|
operationId: exportScheduledTaskRuns
|
|
tags: [Scheduled Tasks]
|
|
summary: Export task history as CSV
|
|
description: Downloads the execution history for a scheduled task as a CSV file. Requires Admiral license and admin role.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: CSV file download.
|
|
content:
|
|
text/csv:
|
|
schema:
|
|
type: string
|
|
description: "CSV with columns: Timestamp, Source, Status, Duration (s), Details"
|
|
"400":
|
|
description: Invalid task ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
# ── Registries ──────────────────────────────────────────
|
|
/api/registries:
|
|
get:
|
|
operationId: listRegistries
|
|
tags: [Registries]
|
|
summary: List registries
|
|
description: |
|
|
Returns all configured private container registries. Secrets are never included in the response.
|
|
Requires Admiral license and admin role. API tokens receive `SCOPE_DENIED`.
|
|
responses:
|
|
"200":
|
|
description: Array of registry objects.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Registry"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
post:
|
|
operationId: createRegistry
|
|
tags: [Registries]
|
|
summary: Create registry
|
|
description: |
|
|
Adds a new private container registry. The secret is encrypted at rest using AES-256-GCM.
|
|
Requires Admiral license and admin role. API tokens receive `SCOPE_DENIED`.
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/RegistryCreate"
|
|
responses:
|
|
"201":
|
|
description: Registry created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [id]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/registries/{id}:
|
|
put:
|
|
operationId: updateRegistry
|
|
tags: [Registries]
|
|
summary: Update registry
|
|
description: |
|
|
Updates an existing registry. All fields are optional — only provided fields are changed.
|
|
Requires Admiral license and admin role. API tokens receive `SCOPE_DENIED`.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/RegistryCreate"
|
|
responses:
|
|
"200":
|
|
description: Registry updated.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"400":
|
|
description: Validation error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
delete:
|
|
operationId: deleteRegistry
|
|
tags: [Registries]
|
|
summary: Delete registry
|
|
description: |
|
|
Removes a registry and its encrypted credentials.
|
|
Requires Admiral license and admin role. API tokens receive `SCOPE_DENIED`.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Registry deleted.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SuccessBoolean"
|
|
"400":
|
|
description: Invalid registry ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"404":
|
|
$ref: "#/components/responses/NotFound"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/registries/{id}/test:
|
|
post:
|
|
operationId: testRegistry
|
|
tags: [Registries]
|
|
summary: Test registry connection
|
|
description: |
|
|
Tests connectivity and authentication against a configured registry.
|
|
Requires Admiral license and admin role. API tokens receive `SCOPE_DENIED`.
|
|
parameters:
|
|
- $ref: "#/components/parameters/idPath"
|
|
responses:
|
|
"200":
|
|
description: Test result.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
success:
|
|
type: boolean
|
|
message:
|
|
type: string
|
|
"400":
|
|
description: Invalid registry ID.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"403":
|
|
$ref: "#/components/responses/Forbidden"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
# ── Image Updates ───────────────────────────────────────
|
|
/api/image-updates:
|
|
get:
|
|
operationId: getImageUpdates
|
|
tags: [Image Updates]
|
|
summary: Get image update status
|
|
description: Returns the update availability status for all stacks.
|
|
responses:
|
|
"200":
|
|
description: Array of update status objects per stack.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/ImageUpdateStatus"
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/image-updates/refresh:
|
|
post:
|
|
operationId: refreshImageUpdates
|
|
tags: [Image Updates]
|
|
summary: Trigger image update check
|
|
description: |
|
|
Triggers a background check for newer image versions across all stacks.
|
|
Rate limited to one refresh per 10 minutes. Requires admin role.
|
|
responses:
|
|
"200":
|
|
description: Refresh started.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
success:
|
|
type: boolean
|
|
message:
|
|
type: string
|
|
example: Image update check started in background.
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|
|
"429":
|
|
description: Rate limited — wait at least 10 minutes between refreshes.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/Error"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
|
|
/api/image-updates/status:
|
|
get:
|
|
operationId: getImageUpdateCheckStatus
|
|
tags: [Image Updates]
|
|
summary: Check if update scan is running
|
|
description: Returns whether an image update check is currently in progress.
|
|
responses:
|
|
"200":
|
|
description: Check status.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [checking]
|
|
properties:
|
|
checking:
|
|
type: boolean
|
|
description: "`true` if a scan is currently running."
|
|
"401":
|
|
$ref: "#/components/responses/Unauthorized"
|